Debug Windows Service
Recently at $dayjob
I had to deal with a malware that was run as a Windows Service. After some searching I found an old article that exemplifies how to do it. This post will not bring anything new, but serves as a reference for myself, with a script that facilitates the debugging.
The idea behind this setup is to use Global Flags to spawn Microsoft’s Console Debugger (CBD) which will expose a debugging server, and then use WinDBG to attach to CBD.
Requirements
To be able to debug the service we need the following:
- Global Flags (
gflags
) - which allows us to spawn a debugger when a given program is executed, you can get it here; - Console Debugger (
cdb
) - which will be the debugger that launches when the program is executed, available from the previous link as well; - WinDBG - which we’ll use as the interface to attach to
cdb
. I personally use the new WinDBG, which is available here. The older version comes with the debugger tools.
Prerequisites
As we are launching a debugger for a service, the debugger will stop the service execution until we tell it to continue. This is an issue as the service is supposed to set its status, and if it doesn’t do it in a given time, Windows assumes that the service did not start correctly and terminates the process. To overcome this we need to change the ServicesPipeTimeout
value to a bigger value:
reg add "HKLM\SYSTEM\CurrentControlSet\Control" /v ServicesPipeTimeout /t REG_DWORD /d 86400000 /f
After setting this we need to restart the machine for the changes to take effect.
Debugging
Creating the Service
I took the batch script from here and made some modifications, including adding support for both 32 and 64 bit services. The script is the following:
The script takes 2 arguments, the first argument is the architecture (32 or 64), and the second argument is the fullpath to the service DLL. The script should be run in a CMD with Administrator privileges. An example of creating a 32 bit service would be:
create_service.bat 32 C:\sample.dll
Configuring gflags
We’ll now configure gflags
to spawn cdb
and expose a debugging server so that we can then attach to it.
Configuration is very straightforward: we just need to tell it that when malsvchost.exe
(a copy of svchost.exe
) spawns, we want to run it inside a debugger. The following image shows how gflags
should look.
The Debugger
field contains the path to cdb.exe
as well as the options to expose a debugging server:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe -server tcp:port=5505
Start and Debug the Service
We can now start the service and attach WinDBG to CDB. Starting is pretty straightforward:
sc start EVILSVC
And if we check the processes, we can see a new cdb.exe
process, whose parent is services.exe
and has the malsvchost.exe
as its child:
As we’ve modified the ServicesPipeTimeout
value, we don’t need to rush to attach to cdb
. We can now open WinDBG and connect to a remote debugger with the following string:
tcp:Port=5505,Server=localhost
We ideally want to avoid all the svchost.exe
code and just break when we load our service. We can do that with the following command:
sxe ld sample.dll
Which will cause a module load exception when the module sample.dll
is loaded. In practice this will break on ntdll!NtMapViewOfSection
, we just need to g
until we reach the module load exception:
0:000> sxe ld sample.dll
0:000> g
ModLoad: 00000000`77120000 00000000`7712a000 C:\Windows\System32\wow64cpu.dll
ModLoad: 00000000`75190000 00000000`75280000 C:\Windows\SysWOW64\KERNEL32.DLL
ModLoad: 00000000`75770000 00000000`759aa000 C:\Windows\SysWOW64\KERNELBASE.dll
ModLoad: 00000000`75090000 00000000`75106000 C:\Windows\SysWOW64\sechost.dll
ModLoad: 00000000`762f0000 00000000`763af000 C:\Windows\SysWOW64\RPCRT4.dll
ModLoad: 00000000`769c0000 00000000`76ae0000 C:\Windows\SysWOW64\ucrtbase.dll
(199c.10ec): WOW64 breakpoint - code 4000001f (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
ntdll_77130000!LdrpDoDebuggerBreak+0x2b:
771e1c12 cc int 3
0:000> g
*** WARNING: Unable to verify checksum for c:\sample.dll
ModLoad: 00000000`741b0000 00000000`741bf000 c:\sample.dll
ntdll!NtMapViewOfSection+0x14:
00007ff9`67d6d564 c3 ret
From here we can set breakpoints wherever we’re interest in the service, like entry
, ServiceMain
or Update
.
Happy Debugging!