Windows System Software -- Consulting, Training, Development -- Engineering Excellent, Every Time.

WinDbg, Debugger Objects, and JavaScript! Oh, My!

WinDbg, Debugger Objects, and JavaScript! Oh, My!

In case you’ve missed it, there are tons of changes going on under the covers in WinDbg. There is a fundamental paradigm shift going on in terms of how WinDbg grants access and presents data to the user and it can lead to some pretty cool results.

Let’s take a concrete example of the old way versus the new way. Say you want to look up every process in the system. In the old way, you would run the following command:

!process 0 0

This gives you some nice output that lists the processes in the system:

!process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS ffff8e018de56040
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 001aa000  ObjectTable: ffffbb8ca0e012c0  HandleCount: 2040.
    Image: System

PROCESS ffff8e018e1b27c0
    SessionId: none  Cid: 020c    Peb: 7126d5e000  ParentCid: 0004
    DirBase: 0673c000  ObjectTable: ffffbb8ca1d72440  HandleCount:  52.
    Image: smss.exe

Cool. But, what if I want to know more about the processes? For example, maybe I want to see every thread in every process. Off to the documentation I go to see if there’s a flag specific to this command that I can pass to get the details I want. In this case I can pass “2” and I get to see some thread details:

!process 0 2
 **** NT ACTIVE PROCESS DUMP ****
 PROCESS ffff8e018de56040
 SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
 DirBase: 001aa000  ObjectTable: ffffbb8ca0e012c0  HandleCount: 2040.
 Image: System

THREAD ffff8e018de5d5c0  Cid 0004.000c  Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Executive) KernelMode Non-Alertable
 fffff800e11c42a0  SynchronizationEvent

THREAD ffff8e018de5f4c0  Cid 0004.0010  Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Executive) KernelMode Non-Alertable
 fffff800e11c5180  Semaphore Limit 0x7fffffff

Excellent! OK, now I want to know which threads are impersonating…Womp, womp. Sorry, there’s not a flag for that. Even worse, my choices for getting this information kind of suck at this point: I can write my own debugger extension or I can write my own Debugger Command Program using the WinDbg scripting language (ha!).

Enter the world of the debugger object model. Instead of running a command that will list the processes in the system, the debugger provides access to an array of objects that represent each process in the system. You can dump this array using the dx command:

dx -r2 Debugger.Sessions[0].Processes
    [0x0]            : 
        KernelObject     [Type: _EPROCESS]
        Name             : Unknown Image
        Id               : 0x0
        Threads         
        Modules         
        Environment     
        Io              
    [0x4]            : 
        KernelObject     [Type: _EPROCESS]
        Name             : Unknown Image
        Id               : 0x4
        Threads         
        Modules         
        Environment     
        Io              
    [0x20c]          : smss.exe
        KernelObject     [Type: _EPROCESS]
        Name             : smss.exe
        Id               : 0x20c
        Threads         
        Modules         
        Environment     
        Io

Note that each object has several properties, including a property that gives us access to all of the threads within the process (Threads). So, if I want to see all of the threads in the processes I can perform a LINQ query and ask for all threads in all processes:

dx -r2 Debugger.Sessions[0].Processes.Select(p => p.Threads)
    [0x0]           
        [0x0]            : nt!DbgBreakPointWithStatus (fffff800`e0feea40) 
    [0x4]           
        [0xc]            : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x10]           : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x14]           : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x18]           : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x1c]           : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x20]           : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x24]           : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0x28]           : nt!KiSwapContext+0x76 (fffff800`e0fede06)

OK, I admit that wasn’t intuitively obvious to ME, but now we’ll get to see why this is cool…

Back to my original issue of needing only threads that are impersonating, I can do another LINQ query to filter to only the threads that are impersonating. I’ll base this on a property of the ETHREAD kernel object that is part of each thread debugger object:

dx -r2 Debugger.Sessions[0].Processes.Select(p => p.Threads.Where(t => t.KernelObject.ActiveImpersonationInfo != 0))
...
    [0x3d8]         
    [0x424]         
    [0x42c]         
    [0x460]         
        [0x830]          : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
        [0xf30]          : nt!KiSwapContext+0x76 (fffff800`e0fede06) 
    [0x468]         
    [0x494]         
    [0x4dc]         

Again I will agree that this isn’t intuitively obvious, but it’s pretty cool and provides a new way to navigate crash dump files.

Another cool feature of debugger objects is that they’re also exposed via JavaScript. So, instead of writing a debugger extension in C++ to list all the processes in the system, you can write some JavaScript instead:

//
// ListProcs.js
//
// Walk the current list of processes
//
// OSR Open Systems Resources, inc.
//
// http://www.osr.com
// http://www.osronline.com
//
//
// To run:
//  
//   .load jsprovider.dll
//   .scriptload ListProcs.js
//   dx Debugger.State.Scripts.ListProcs.Contents.ListProcs()
//
function ListProcs() {

    // Get easy access to the debug output method
    var dbgOutput = host.diagnostics.debugLog;

    dbgOutput("List Processes!\n\n");

    var processes = host.currentSession.Processes;

    for (var process of processes)
    {
        dbgOutput("Found Process:\n");
        dbgOutput("\tName : ", process.Name, "\n");
        dbgOutput("\tId   : ", process.Id, "\n");
    }

}
To run the command just save it in a .js file and perform the following steps:
.load jsprovider.dll
.scriptload ListProcs.js
dx Debugger.State.Scripts.ListProcs.Contents.ListProcs()

List Processes!
Found Process:
 Name : Unknown Image
 Id   : 0x0
Found Process:
 Name : Unknown Image
 Id   : 0x4
Found Process:
 Name : smss.exe
 Id   : 0x20c

Pretty neat!

To provide another example, we had a question on our WinDbg forum about how you might find all of the active file handles to a particular device object. In the old way this would be quite painful. You’d end up running the following command:
!handle 0 3 0 File
And searching the output for the address that you’re interested in.
In the new way, each debugger process object has a property that returns you an array of the handles in the process. With a bit of JavaScript we can use this data to find the file objects that reference our device object:
//
// FindDevHandle.js
//
// Walk the current list of processes and look for a handle to a file object
// that is accessing the specified device
//
// OSR Open Systems Resources, inc.
//
// http://www.osr.com
// http://www.osronline.com
//
//
// To run:
//  
//   .load jsprovider.dll
//   .scriptload FindDevHandle.js
//   dx Debugger.State.Scripts.FindDevHandle.Contents.FindDevHandle(0x12345678)
//
function FindDevHandle(devObjParam) {
 
    // Get easy access to the debug output method
    var dbgOutput = host.diagnostics.debugLog;
 
    // Get a typed device object for the incoming parameter
    var devObj = host.createTypedObject(devObjParam, "nt", "_DEVICE_OBJECT");
 
    dbgOutput("Finding handle to device ", devObj.targetLocation, "!\n\n");
 
    // Loop over each process
    var processes = host.currentSession.Processes;
 
    for (var process of processes) {
 
        dbgOutput("Process ", process.Name, "\n");
 
        // And each handle in every process
        var handles = process.Io.Handles;
 
        // Note that an exception can be raised while looping over the handles
        // (e.g. an empty handle table)
        try {
 
            for (var handle of handles) {
 
                // NOTE: We just treat every handle like it's a file handle
                // and catch exceptions along the way. A better idea would
                // be to key off of the type, but that appears to be broken
                // with public PDBs at the moment
                try {
 
                    // Cast the object to a file object
                    var fileObj = host.createTypedObject(handle.Object.Body.targetLocation, "nt", "_FILE_OBJECT");
 
                    // Dereference the DeviceObject field and get the target location
                    if (fileObj.DeviceObject.dereference().targetLocation == devObj.targetLocation) {
 
                        dbgOutput("\tFound one!\n");
                        dbgOutput("\t PID    : ", process.Id, "\n");
                        dbgOutput("\t Name   : ", process.Name, "\n");
                        dbgOutput("\t Handle : ", handle.Handle, "\n");
                        dbgOutput("\t Object : ", fileObj.targetLocation, "\n\n");
                    }
                   
                } catch (e) {
 
                    dbgOutput("\tException parsing handle!\n");
 
                }
 
            }
 
        } catch (e) {
 
            dbgOutput("\tException parsing handle table!\n");
 
        }
 
    }
 
}
Example of the results:
dx Debugger.State.Scripts.FindDevHandle.Contents.FindDevHandle(0xffff8e018f704140)

Finding handle to device 0xffff8e018f704140!
...
Process SearchFilterHost.exe
Process audiodg.exe
Process OSRLOADER.exe
Process NothingTest.exe
    Found one!
     PID    : 0x1300
     Name   : NothingTest.exe
     Handle : 0x9c
     Object : 0xffff8e018e619690
I’ve glossed over a lot of things in that script and we’ll certainly be writing more about all of this in the future, but in the meantime there’s some interesting documentation available on MSDN:
dx command:
Writing LINQ queries in WinDbg
JavaScript Debugger Example Scripts
Native Debugger Objects in JavaScript Extensions
 Defrag Tools #170 – Debugger – JavaScript Scripting