[go: up one dir, main page]

Skip to content
This repository has been archived by the owner on Oct 12, 2023. It is now read-only.

Latest commit

 

History

History

svcctrl

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

Fun with the Service Control Handler

This tool was originally written for a CTF many years ago for the purpose of stopping the windows event logger. Since the event logger refused to accept SERVICE_CONTROL_STOP, the only option was to terminate the host process or at least the threads running the service.

Internal Dispatch Entry

Every Windows service has a "control handler" to receive control codes from the operating system. Depending on what the service is willing to accept, the more common control codes for a service are interrogate, start, stop, pause or resume. A pointer to the Control Handler is stored in a data structure on the heap that Microsoft refers to as an "Internal Dispatch Entry" (IDE).

The following structure is supported by versions of windows 7.

typedef struct _INTERNAL_DISPATCH_ENTRY {
    LPWSTR                  ServiceName;
    LPWSTR                  ServiceRealName;
    LPSERVICE_MAIN_FUNCTION ServiceStartRoutine;
    LPHANDLER_FUNCTION_EX   ControlHandler;
    HANDLE                  StatusHandle;
    DWORD                   ServiceFlags;
    DWORD                   Tag;
    HANDLE                  MainThreadHandle;
    DWORD                   dwReserved;
} INTERNAL_DISPATCH_ENTRY, *PINTERNAL_DISPATCH_ENTRY;

The following structure is supported by versions of windows 10.

typedef struct _INTERNAL_DISPATCH_ENTRY {
    LPWSTR                  ServiceName;
    LPWSTR                  ServiceRealName;
    LPWSTR                  ServiceName2;       // Windows 10
    LPSERVICE_MAIN_FUNCTION ServiceStartRoutine;
    LPHANDLER_FUNCTION_EX   ControlHandler;
    HANDLE                  StatusHandle;
    DWORD64                 ServiceFlags;        // 64-bit on windows 10
    DWORD64                 Tag;
    HANDLE                  MainThreadHandle;
    DWORD64                 dwReserved;
    DWORD64                 dwReserved2;
} INTERNAL_DISPATCH_ENTRY, *PINTERNAL_DISPATCH_ENTRY;

To find valid entries consists of searching all writeable areas of memory for a process hosting a service.

Stopping a service

Once a valid service IDE has been found, we can stop the service by executing the ControlHandler code using a remote thread and passing SERVICE_CONTROL_STOP as the parameter. For more information, refer to the StopService() function.

Process injection

It's also possible to overwrite the ControlHandler value with a a pointer to other code and forcing the service to execute via the ControlService API and SERVICE_CONTROL_INTERROGATE code. This requires setting the ServiceFlags field to SERVICE_CONTROL_INTERROGATE. For more information, refer to the SvcCtrlInject() function.

When things go wrong

The service names in an IDE don't always correspond with the name in the service database. Take for example the following entry.

SERVICE_NAME: WpnUserService_2e777
DISPLAY_NAME: Windows Push Notifications User Service_2e777
        TYPE               : e0  USER_SHARE_PROCESS INSTANCE
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_PRESHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

This tool will find the host process for "WpnUserService_2e777" but will not find a valid IDE. An additional option is available (-l) to list all valid IDEs found in the host process. Using the same service again with -l

ServiceName         : 0000024667405FD0 (WpnUserService)
ServiceRealName     : 0000024667405FD0 (WpnUserService)
ServiceStartRoutine : 00007FF790652F80
ControlHandler      : 00007FFF9422D3C0
StatusHandle        : 00000246674155C0
ServiceFlags        : 0000000000000002
Tag                 : 00007FFF942381A0
MainThreadHandle    : 0000000000000111 (273)

ServiceName         : 0000024667406010 (UnistoreSvc)
ServiceRealName     : 0000024667406010 (UnistoreSvc)
ServiceStartRoutine : 00007FF790652F80
ControlHandler      : 00007FFF96FBF0D0
StatusHandle        : 00000246698FF100
ServiceFlags        : 0000000000000002
Tag                 : 00007FFF97008580
MainThreadHandle    : 000000000000010F (271)

ServiceName         : 0000024667406028 (PimIndexMaintenanceSvc)
ServiceRealName     : 0000024667406028 (PimIndexMaintenanceSvc)
ServiceStartRoutine : 00007FF790652F80
ControlHandler      : 00007FFF96E5DFD0
StatusHandle        : 0000024669C54440
ServiceFlags        : 0000000000000002
Tag                 : 00007FFF96E6AB00
MainThreadHandle    : 000000000000010D (269)

ServiceName         : 0000024667406056 (CDPUserSvc)
ServiceRealName     : 0000024667406056 (CDPUserSvc)
ServiceStartRoutine : 00007FF790652F80
ControlHandler      : 00007FFF9426FAD0
StatusHandle        : 0000024667415760
ServiceFlags        : 0000000000000002
Tag                 : 0000024667426060
MainThreadHandle    : 0000000000000108 (264)

ServiceName         : 000002466740606C (UserDataSvc)
ServiceRealName     : 000002466740606C (UserDataSvc)
ServiceStartRoutine : 00007FF790652F80
ControlHandler      : 00007FFF8501F4C0
StatusHandle        : 00000246698FF180
ServiceFlags        : 0000000000000002
Tag                 : 00007FFF85078500
MainThreadHandle    : 0000000000000110 (272)

ServiceName         : 0000024667406084 (OneSyncSvc)
ServiceRealName     : 0000024667406084 (OneSyncSvc)
ServiceStartRoutine : 00007FF790652F80
ControlHandler      : 00007FFF8498BE90
StatusHandle        : 0000024669841A40
ServiceFlags        : 0000000000000002
Tag                 : 00007FFF849AAFB0
MainThreadHandle    : 000000000000010C (268)

"WpnUserService" is the service name stored in the IDE, but the service database uses "WpnUserService_2e777". An additional option can be used to target a specific thread. Pass the decimal value of MainThreadHandle to the tool along with the database service name and it will locate the correct entry.