Beefier Breakpoints Using Debugger Scripts


Written by Ron Stock

I recently collaborated with a third-party vendor which required me to track down a problem during the Vista upgrade process. The issue came down to catching a Microsoft component opening a specific registry service key during the setup process. Issues like this frequently call for live debugging to catch registry access in real time. I could have set a breakpoint on RegOpenKeyExW() and examined every requested key passed into the function, but this method is very time consuming considering RegOpenKeyExW()  is a highly used code path. This methodology involves setting a break point on the function, waiting for the break in, checking the second parameter (lpSubKey) for a match to the desired registry key (string), and hitting “g” if the key doesn’t match. I could have potentially repeated these steps a hundred times before finding the call containing the key I needed. I wanted some way to set a ‘break on access’ only when my key was touched. We ran into a similar issue requiring the ability to catch a file system function working with a specific file when possibly hundreds of files were in use. So how do you gain the automation needed for this type of debugging? The answer is debugger scripts.

Let’s run through an example with Windows Explorer.exe using this method. In this scenario we’ll attempt to catch Explorer.exe opening the HKEY_LOCAL_MACHINE\SYSTEM\Setup key.  Windows Explorer opens several keys per second, so the focus is to ‘break  in’ when this specific key is passed to RegOpenKeyEx() without having to manually wade through hundreds of possible keys passed into the function.

1.       Identify the function in question.


Per MSDN, RegOpenKeyEx is defined as the following, and we’re interested in the second parameter because it contains the name of the key.


  __in        HKEY hKey,

  __in_opt    LPCTSTR lpSubKey,

  __reserved  DWORD ulOptions,

  __in        REGSAM samDesired,

  __out       PHKEY phkResult


2.       Create the debugger script

The script logic is straight forward. Our debugger script will break in every time RegOpenKeyEx() is called.  It then checks the second parameter for the string SYSTEM\Setup. If it finds a match it stays broken in, otherwise it returns command to the running thread. Our script will look like this -


.if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

.if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }


r$t0 is considered a pseudo-register and is used as a variable in this script. Since the second parameter of RegOpenKeyEx() is passed via esp+8, our debugger command program will dereference esp+8 and move it into r$t0. Our pseudo-register now holds the memory address of lpSubKey. The second line of the program simply checks to see if r$t0 is NULL, and will setup our alias named _str at the address held in r$t0 if r$t0 is not NULL. The third line uses the MASM string operator, $spat, looking for a string pattern which matches system\setup. If it doesn’t find a match it prints the string, and returns command back to the executing thread. It’s the equivalent of typing ‘g’ in the debugger. If the string pattern system\setup is found, the string is printed out on the screen, and the debugger remains broken in. Then we can debug the underlying problem in the appropriate context. Now save them into a txt located at c:\debuggers\strings.txt so we can reference it from our breakpoint.


3.       Set the break point.


Next we break into the Explorer process from the kernel debugger


kd> !process 0 0 Explorer.exe

PROCESS 8340d6a8  SessionId: 1  Cid: 0270    Peb: 7ffd5000  ParentCid: 01b0

    DirBase: 2d48f000  ObjectTable: 90171348  HandleCount: 559.

    Image: explorer.exe



kd> !bpid 0270

Finding wininit.exe (1)...

Finding winlogon.exe (1)...

Waiting for winlogon.exe to break.  This can take a couple of minutes...

Break instruction exception - code 80000003 (first chance)

Break into process 270 set.  The next break should be in the desired process.

Break instruction exception - code 80000003 (first chance)


77f17dfe cc              int     3



Then we set a breakpoint on ADVAPI32!RegOpenKeyExW instructing it to use our script to evaluate whether SYSTEM\Setup is the key passed in at the second parameter. If it finds a match to our key it stays broken in otherwise execution is passed back to the running Explorer thread.


kd> bp ADVAPI32!RegOpenKeyExW "$$<c:\\debuggers\\strings.txt"

kd> bl

0 e 76e8f09d     0001 (0001) ADVAPI32!RegOpenKeyExW "$$<c:\debuggers\strings.txt"


This is the output we would expect to see for all of the non-matches.


kd> g

kd> r$t0=poi(esp+8)

kd> .if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

kd> .if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }


kd> r$t0=poi(esp+8)

kd> .if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

kd> .if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }

Software\Policies\Microsoft\Windows NT\DnsClient


(Several entries have been removed here to conserve space in the blog)


kd> .if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

kd> .if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }


kd> r$t0=poi(esp+8)

kd> .if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

kd> .if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }

Software\Policies\Microsoft\Windows NT\DnsClient

kd> r$t0=poi(esp+8)

kd> .if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

kd> .if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }


kd> r$t0=poi(esp+8)

kd> .if (@$t0 != 0) { as /mu ${/v:_str} @$t0 } .else { ad /q ${/v:_str} }

kd> .if ($spat("${_str}x", "*system\Setup*") == 0) { .echo _str;g } .else { .echo _str }



76e8f09d 8bff            mov     edi,edi


Bingo! It detected that System\Setup was the string passed in and remained broken in allowing us to start any necessary debugging.



kd> kv

ChildEBP RetAddr  Args to Child             

077aeae0 760dfb75 80000002 760dfb94 00000000 ADVAPI32!RegOpenKeyExW (FPO: [Non-Fpo])

WARNING: Frame IP not in any known module. Following frames may be wrong.

077aed18 760dfbb7 00000004 00000000 0000003a 0x760dfb75

077aee20 7678c1b2 00000000 00000000 00000000 0x760dfbb7

077aee24 00000000 00000000 00000000 00000000 kernel32!WaitForSingleObject+0x12 (FPO: [Non-Fpo])


kd> dc 760dfb94

760dfb94  00790053 00740073 006d0065 0053005c  S.y.s.t.e.m.\.S.

760dfba4  00740065 00700075 ff530000 4ce81075  e.t.u.p...S.u..L

760dfbb4  8bffffff 0ffb3bf8 0032c284 89fb3b00  .....;....2..;..

760dfbc4  840ff47d 0000012b f875ff56 013ee857  }...+...V.u.W.>.

760dfbd4  f08b0000 840ff33b 000000f2 0000c3e9  ....;...........

760dfbe4  90909000 ff8b9090 83ec8b55 458b1cec  ........U......E

760dfbf4  db335308 fc458956 e8f45d89 fffff583  .S3.V.E..]......

760dfc04  ff18758b 1e891475 fff5a9e8 89c33bff  .u..u........;..


Debugger scripts are another cool tool to add to your debugging arsenal. I’ve personally benefited from this type of scripting many times. Again check the debugger help files for more information on the available commands.


Share this post :

Comments (5)

  1. says:

    cool !! is like condition property for breakpoints in Visual Studio with managed code. I’ll try in mi code now !!

  2. eranb says:


    This is a very useful post. I must say that windbg scripting is not for the faint of heart. Can you provide further examples of such scripts so the community will be able to use them and modify them as needed.



  3. Torsten Sprejz says:

    Thanks for this article. It will save me time in developing the needed scripts on conditional breakpoints.


  4. Ryanman says:

    This is a great blog! Thanks for taking the time to write these blogs.

Skip to main content