|< Windows Services Programming 5 | Main | Site Index | Download |


 

 

 

 

 

MODULE EE3

WINDOWS SERVICES

Part 6: FINAL PART

 

 

 

 

 

 

 

 

 

 

 

 

 

What we have in this Module?

  1. Sending control requests to a service program example.

  2. Modifying the DACL for a service program example.

 

My Training Period: xx hours. Before you begin, read some instruction here.

 

 

 

The programming abilities for this session:

  • Able to collect and understand all the required information related to Windows Services.

  • Able to understand, build and run various Windows Service application programs.

Note: For Multithreaded program examples, you have to set your project to Multithread project type.

 

 

Sending Control Requests to a Service

 

The following example uses the ControlService() function to send a control value to a running service. Different control values require different levels of access to the service object. For example, a service object handle must have SERVICE_STOP access to send the SERVICE_CONTROL_STOP control code. When ControlService() returns, a SERVICE_STATUS structure contains the latest status information for the service.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

 

// This example try to stop a service by sending

// a control code.  It ONLY for services that don't

// have dependencies...else it will fail

// For services that have dependencies, use the previous example...

// Parameters:

//   argc - the number of command-line arguments

//   argv[ ] - an array of command-line arguments

 

int main(int argc, char *argv[ ])

{

       SC_HANDLE schSCManager, schService;

       LPCTSTR lpszServiceName;

       SERVICE_STATUS ssStatus;

       DWORD fdwAccess;

       // Try sending stop control code...

       // Notifies a service that it should stop...

       DWORD fdwControl = SERVICE_CONTROL_STOP;

   // If no command line argument supplied...

   if (argc < 2)

   {

      printf("usage: %s <ServiceName>\n", argv[0]);

         printf("Try again.\n");

      return 1;

   }

 

   lpszServiceName = argv[1];

       // Open a handle to the SC Manager database...

       schSCManager = OpenSCManager(

       NULL,                    // local machine

       NULL,                    // SERVICES_ACTIVE_DATABASE database is opened by default

       SC_MANAGER_ALL_ACCESS);  // full access rights

 

if (NULL == schSCManager)

   printf("OpenSCManager(), Open a handle to the SC Manager database failed, error: %d.\n", GetLastError());

else

   printf("OpenSCManager(), Open a handle to the SC Manager database looks OK.\n");

 

    // The required service object access depends on the control...

    switch (fdwControl)

    {

        case SERVICE_CONTROL_STOP:

            fdwAccess = SERVICE_STOP;

            break;

        case SERVICE_CONTROL_PAUSE:

        case SERVICE_CONTROL_CONTINUE:

            fdwAccess = SERVICE_PAUSE_CONTINUE;

            break;

        case SERVICE_CONTROL_INTERROGATE:

            fdwAccess = SERVICE_INTERROGATE;

            break;

        default:

            fdwAccess = SERVICE_INTERROGATE;

    }

 

    // Open a handle to the service.

    schService = OpenService(

        schSCManager,        // SCManager database

        lpszServiceName,     // name of service

        fdwAccess);               // specify the access right

 

       if (schService == NULL)

         printf("OpenService(), open a handle to a service with appropriate access failed, error: %d", GetLastError());

       else

         printf("OpenService(), open a handle to a service with appropriate access looks OK.\n");

    // Send a control value to the service...

    if (!ControlService(

            schService,   // handle to service

            fdwControl,   // control value to send, here is SERVICE_CONTROL_STOP

            &ssStatus))   // address of status info

 

       printf("ControlService(), sending control value to stop a service failed, error: %d\n", GetLastError());

    else

       printf("ControlService(), sending control value to a stop service looks OK.\n");

    // Print the service status.

    printf("\nStatus of %s: \n", lpszServiceName);

    printf("  Service Type: 0x%x\n", ssStatus.dwServiceType);

    printf("  Current State: 0x%x\n", ssStatus.dwCurrentState);

    printf("  Controls Accepted: 0x%x\n", ssStatus.dwControlsAccepted);

    printf("  Exit Code: %d\n", ssStatus.dwWin32ExitCode);

    printf("  Service Specific Exit Code: %d\n", ssStatus.dwServiceSpecificExitCode);

    printf("  Check Point: %d\n", ssStatus.dwCheckPoint);

    printf("  Wait Hint: %d\n", ssStatus.dwWaitHint);

 

    return 0;

}

 

Running the program at the command prompt, the following are sample outputs.

F:\myproject\myservices\Release>myservices dhcp

OpenSCManager(), Open a handle to the SC Manager database looks OK.

OpenService(), open a handle to a service with appropriate access looks OK.

ControlService(), sending control value to a stop service looks OK.

 

Status of dhcp:

  Service Type: 0x20

  Current State: 0x3

  Controls Accepted: 0x5

  Exit Code: 0

  Service Specific Exit Code: 0

  Check Point: 1

  Wait Hint: 25000

 

F:\myproject\myservices\Release>myservices iisadmin

OpenSCManager(), Open a handle to the SC Manager database looks OK.

OpenService(), open a handle to a service with appropriate access looks OK.

ControlService(), sending control value to stop a service failed, error: 1051

 

Status of iisadmin:

  Service Type: 0x20

  Current State: 0x4

  Controls Accepted: 0x7

  Exit Code: 0

  Service Specific Exit Code: 0

  Check Point: 0

  Wait Hint: 0

 

F:\myproject\myservices\Release>myservices w3svc

OpenSCManager(), Open a handle to the SC Manager database looks OK.

OpenService(), open a handle to a service with appropriate access looks OK.

ControlService(), sending control value to a stop service looks OK.

 

Status of w3svc:

  Service Type: 0x20

  Current State: 0x3

  Controls Accepted: 0x7

  Exit Code: 0

  Service Specific Exit Code: 0

  Check Point: 0

  Wait Hint: 0

 

F:\myproject\myservices\Release>

By assuming the dhcp, w3svc and IISADMIN services are running, from the output, services that do not have dependencies such as dhcp and w3svc have been stopped but the IISADMIN (depend on the w3svc service)  failed because its dependency on w3svc that should be stopped first. You can verify those services through the Services control panel. Error 1051 says that “A stop control has been sent to a service that other running services are dependent on”, (ERROR_DEPENDENT_SERVICES_RUNNING).

 

Modifying the DACL for a Service

 

An application can create or modify the DACL associated with a Service object in order to control access. To retrieve the DACL associated with a service object, use the QueryServiceObjectSecurity() function. To set the DACL, use the SetServiceObjectSecurity() function. Any changes made to the SECURITY_DESCRIPTOR associated with the service object are persistent until the service is removed from the system.

The following sample code creates and sets a new DACL for the service specified on the command line. The sample code merges one Access Control Entry (ACE) to the existing DACL for the service. The new ACE grants the Guest account start, stop, delete, and READ_CONTROL access to the specified service. Access to the service can be modified by the AccessPermissions parameter passed to the BuildExplicitAccessWithName() function. Note that for critical and security aware services, this program example not a good one to be executed :o)

 

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <aclapi.h>

#include <stdio.h>

#include <tchar.h>

 

// Simple user defined error messages...

void DisplayError(DWORD dwError, LPTSTR pszAPI)

{

   LPVOID lpvMessageBuffer;

   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |

                 FORMAT_MESSAGE_FROM_SYSTEM,

                 NULL, dwError,

                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

                (LPTSTR)&lpvMessageBuffer, 0, NULL);

 

   // Display the string.

   _tprintf(TEXT("ERROR: API        = %s.\n"), pszAPI);

   _tprintf(TEXT("       error code     = %u.\n"), dwError);

   _tprintf(TEXT("       message       = %s.\n"), (LPTSTR)lpvMessageBuffer);

 

   // Free the buffer allocated by the system.

   LocalFree(lpvMessageBuffer);

   ExitProcess(dwError);

}

 

// Using _tmain(), TCHAR - A WCHAR if UNICODE/wide character is defined, a CHAR otherwise.

int _tmain(int argc, TCHAR *argv[ ])

{

   BOOL                 bDaclPresent   = FALSE;

   BOOL                 bDaclDefaulted = FALSE;

   DWORD                dwError        = 0;

   DWORD                dwSize         = 0;

   EXPLICIT_ACCESS      ea;

   PACL                 pacl           = NULL;

   PACL                 pNewAcl        = NULL;

   PSECURITY_DESCRIPTOR psd;

   SC_HANDLE            schManager     = NULL;

   SC_HANDLE            schService     = NULL;

   SECURITY_DESCRIPTOR  sd;

 

   if (argc != 2)

   {

       _tprintf(TEXT("Usage: %s [service name].\n"), argv[0]);

       _tprintf(TEXT("Please try again.\n"));

       return 1;

   }

 

   // Obtain a handle to the Service Controller.

   schManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);

   if (schManager == NULL)

      DisplayError(GetLastError(), TEXT("OpenSCManager()"));

   else

      _tprintf(TEXT("OpenSCManager(), handle to the service controller obtained successfully.\n"));

 

   // Obtain a handle to the service.

   schService = OpenService(schManager, argv[1], READ_CONTROL | WRITE_DAC);

   if (schService == NULL)

      DisplayError(GetLastError(), TEXT("OpenService()"));

   else

      _tprintf(TEXT("OpenSCManager(), handle to the service obtained successfully.\n"));

 

   // Get the current security descriptor.

   if (!QueryServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, &psd, 0, &dwSize))

   {

      if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)

      {

         psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);

         if (psd == NULL)

         {

            DisplayError(0, TEXT("HeapAlloc()"));

            // note HeapAlloc() does not support GetLastError()

         }

         else

           _tprintf(TEXT("HeapAlloc() looks OK.\n"));

 

         if (!QueryServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, psd, dwSize, &dwSize))

           

            DisplayError(GetLastError(), TEXT("QueryServiceObjectSecurity()"));

         else

           _tprintf(TEXT("QueryServiceObjectSecurity() looks OK.\n"));

      }

      else

         DisplayError(GetLastError(), TEXT("QueryServiceObjectSecurity()"));

   }

// Get the DACL...

   if (!GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, &bDaclDefaulted))

      DisplayError(GetLastError(), TEXT("GetSecurityDescriptorDacl()"));

   else

       _tprintf(TEXT("GetSecurityDescriptorDacl() looks OK.\n"));

 

   // Build the ACE.

   BuildExplicitAccessWithName(&ea, TEXT("GUEST"), SERVICE_START | SERVICE_STOP | READ_CONTROL | DELETE, SET_ACCESS, NO_INHERITANCE);

 

   dwError = SetEntriesInAcl(1, &ea, pacl, &pNewAcl);

 

   if (dwError != ERROR_SUCCESS)

      DisplayError(dwError, TEXT("SetEntriesInAcl()"));

   else

       _tprintf(TEXT("SetEntriesInAcl() looks OK.\n"));

 

   // Initialize a NEW Security Descriptor.

   if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))

      DisplayError(GetLastError(), TEXT("InitializeSecurityDescriptor()"));

   else

       _tprintf(TEXT("SetEntriesInAcl() looks OK.\n"));

 

   // Set the new DACL in the Security Descriptor.

   if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE))

      DisplayError(GetLastError(), TEXT("SetSecurityDescriptorDacl()"));

   else

       _tprintf(TEXT("SetSecurityDescriptorDacl() looks OK.\n"));

 

   // Set the new DACL for the service object.

   if (!SetServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, &sd))

      DisplayError(GetLastError(), TEXT("SetServiceObjectSecurity()"));

   else

       _tprintf(TEXT("SetServiceObjectSecurity() looks OK.\n"));

 

   // Close the handles.

   if (!CloseServiceHandle(schManager))

      DisplayError(GetLastError(), TEXT("CloseServiceHandle() schManager"));

   else

       _tprintf(TEXT("CloseServiceHandle() schManager looks OK.\n"));

 

   if (!CloseServiceHandle(schService))

      DisplayError(GetLastError(), TEXT("CloseServiceHandle() schService"));

   else

       _tprintf(TEXT("CloseServiceHandle() schService looks OK.\n"));

 

   // Free buffers.

   LocalFree((HLOCAL)pNewAcl);

   HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

   return 0;

}

 

Windows services: Modifying DACL (Access Right) of a service program example

 

We test our program by changing the spooler service’s Discretionary Access List (DACL) by adding guest user with start, stop, delete, and READ_CONTROL access right. Tenouk failed to verify this…:o) Using the program property page didn’t show the sign! May need to enumerate…

 

--------------------------------------------------End examples---------------------------------------------

 

Service Reference

 

The following are functions and structures used with services.

 

Service Functions

 

The following functions are used or implemented by services.

 

Function

Description

Handler()

An application-defined callback function used with the RegisterServiceCtrlHandler() function.

HandlerEx()

An application-defined callback function used with the RegisterServiceCtrlHandlerEx() function.

RegisterServiceCtrlHandler()

Register a function to handle service control requests for an application.

RegisterServiceCtrlHandlerEx()

Register a function to handle service control requests for an application.

ServiceMain()

An application-defined function that serves as the starting point for a service.

SetServiceStatus()

Updates the service control manager's status information for the calling service.

StartServiceCtrlDispatcher()

Connects the main thread of a service process to the service control manager.

 

Table 1

 

The following functions are used by programs that control or configure services.

 

Function

Description

ChangeServiceConfig()

Changes the configuration parameters of a service.

ChangeServiceConfig2()

Changes the optional configuration parameters of a service.

CloseServiceHandle()

Closes the specified handle to a service control manager object or a service object.

ControlService()

Sends a control code to a service.

CreateService()

Creates a service object and adds it to the specified service control manager database.

DeleteService()

Marks the specified service for deletion from the service control manager database.

EnumDependentServices()

Retrieves the name and status of each service that depends on the specified service.

EnumServicesStatus()

Enumerates services in the specified service control manager database.

EnumServicesStatusEx()

Enumerates services in the specified service control manager database based on the specified information level.

GetServiceDisplayName()

Retrieves the display name of the specified service.

GetServiceKeyName()

Retrieves the service name of the specified service.

LockServiceDatabase()

Requests ownership of the service control manager database lock.

NotifyBootConfigStatus()

Reports the boot status to the service control manager.

OpenSCManager()

Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.

OpenService()

Opens an existing service.

QueryServiceConfig()

Retrieves the configuration parameters of the specified service.

QueryServiceConfig2()

Retrieves the optional configuration parameters of the specified service.

QueryServiceLockStatus()

Retrieves the lock status of the specified service control manager database.

QueryServiceObjectSecurity()

Retrieves a copy of the security descriptor associated with a service object.

QueryServiceStatus()

Retrieves the current status of the specified service.

QueryServiceStatusEx()

Retrieves the current status of the specified service based on the specified information level.

SetServiceBits()

Registers a service type with the service control manager and the Server service.

SetServiceObjectSecurity()

Sets the security descriptor of a service object.

StartService()

Starts a service.

UnlockServiceDatabase()

Unlocks a service control manager database by releasing the specified lock.

 

Table 2

 

Applications can use the following functions for manipulating window station objects.

 

Function

Description

CloseWindowStation()

Closes a specified window station.

CreateWindowStation()

Creates a new window station.

EnumWindowStations()

Enumerates the window stations in the system by repeatedly calling an application-defined EnumWindowStationProc() callback function.

GetProcessWindowStation()

Retrieves a handle to the window station assigned to the calling process.

GetUserObjectInformation()

Gets information about a window station or desktop object.

GetUserObjectSecurity()

Gets security information for a window station or desktop object.

OpenWindowStation()

Opens a handle to an existing window station.

SetProcessWindowStation()

Assigns a specified window station to the calling process.

SetUserObjectInformation()

Sets information about a window station or desktop object.

SetUserObjectSecurity()

Sets security information for a window station or desktop object.

 

Table 3

 

Applications can use the following functions for manipulating desktop objects.

 

Function

Description

CloseDesktop()

Closes a specified desktop.

CreateDesktop()

Creates a new desktop on a specified window station.

EnumDesktops()

Enumerates the desktops on a specified window station by repeatedly calling an application-defined EnumDesktopProc() callback function.

EnumDesktopWindows()

Enumerates the windows on a specified desktop by repeatedly calling an application-defined EnumWindowsProc() callback function.

GetThreadDesktop()

Returns a handle to the desktop assigned to the calling thread.

GetUserObjectInformation()

Gets information about a window station or desktop object.

GetUserObjectSecurity()

Gets security information for a window station or desktop object.

OpenDesktop()

Opens a handle to an existing desktop.

OpenInputDesktop()

Opens a handle to the desktop that receives user input.

SetThreadDesktop()

Assigns a specified desktop to the calling thread.

SetUserObjectInformation()

Sets information about a window station or desktop object.

SetUserObjectSecurity()

Sets security information for a window station or desktop object.

 

Table 4

 

Service Structures

 

The following structures are used with services:

  1. ENUM_SERVICE_STATUS

  2. ENUM_SERVICE_STATUS_PROCESS

  3. QUERY_SERVICE_CONFIG

  4. QUERY_SERVICE_LOCK_STATUS

  5. SC_ACTION

  6. SERVICE_DESCRIPTION

  7. SERVICE_FAILURE_ACTIONS

  8. SERVICE_STATUS

  9. SERVICE_STATUS_PROCESS

  10. SERVICE_TABLE_ENTRY

  11. USEROBJECTFLAGS

 

 

-------------End Windows Services Examples-------------

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.

  2. For Multibytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).

  3. Windows data types are Windows data types.

  4. Microsoft Visual C++, online MSDN.

  5. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

 

 

|< Windows Services Programming 5 | Main | Site Index | Download |