Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I debug a process that starts at boot time?

I am trying to set a breakpoint into a Windows service that starts at boot time. Because of an unfortunate mistake on my end, the service forces the machine into a reboot loop: this means that I can't get to a stable state from which I could deploy a fix, and obviously I can't try to debug the service at a more convenient time.

I can use windbg in kernel mode. I'd very much like to break when the service hits the wmain function, but I'm having issues with that.

Up to now, I found that I can stop when the image is loaded by using the following commands:

!gflag +ksl
sxe ld MyServiceExecutable.exe

The problem is that once it breaks, I find myself in an empty process, in which I am apparently unable to set breakpoints. bm MyServiceExecutable!wmain says that it can't find the symbol and that the breakpoint will be "deferred", but it is effectively never set or reached. Setting a breakpoint on KERNEL32!BaseThreadInitThunk seems to work more or less at random across all the processes running and I didn't have a lot of luck with it to stop in my service so far.

like image 474
zneak Avatar asked Jun 20 '14 03:06

zneak


People also ask

How do I debug Windows startup?

To enable boot debugging, use the BCDEdit /bootdebug command and specify the appropriate boot component. If you wish to perform kernel debugging after Windows starts, use the BCDEdit /debug command as well. You must also select a debugging connection, just as in normal kernel debugging.


1 Answers

Alright, this might not the best way to do it, but it worked. MSFTs, please correct me if I'm doing something dumb!

The first part was good:

kd> !gflag +ksl
    New NtGlobalFlag contents: 0x00440000
        ksl - Enable loading of kernel debugger symbols
        ece - Enable close exception
kd> sxe ld MyServiceExecutable.exe
kd> g

In kernel mode, sxe ld will stop the first time the executable is loaded only.

When the debugger stops again, we're inside the freshly created process. We don't need the gflag anymore:

kd> !gflag -ksl
    New NtGlobalFlag contents: 0x00400000
        ece - Enable close exception

Though we're going to need the EPROCESS pointer. We can get it with .process or !process -1 0, but it is already in the $proc pseudo-register:

kd> r $proc
    $proc=0011223344556677
kd> .process
    Implicit process is now 00112233`44556677

From this point it's possible to set breakpoints on nt symbols, so let's use NtMapViewOfSection as it's called for each dll loaded.

kd> bp /p @$proc nt!NtMapViewOfSection
kd> g

On the next stop ntdll should be loaded (check with kn if it's on the stack, .reload /user if necessary), so you can set a breakpoint on RtlUserThreadStart. Also, we are overwriting breakpoint 0, because since we don't need to break on NtMapViewOfSection anymore(it would just be a nuisance).

kd> bp0 /p @$proc ntdll!RtlUserThreadStart
kd> g

All symbols should have been loaded by the time the first user thread starts, so you're free to set your breakpoint wherever you want.

kd> .reload /user
kd> bp /p @$proc MyServiceExecutable!wmain
kd> g
like image 60
zneak Avatar answered Oct 03 '22 09:10

zneak