共用方式為


Working with pointers and mixed syntax in Windbg kernel debugger

One of the more confusing aspects of Windbg for me is the ability to compound commands. This commonly shows up in creation of conditional breakpoints, but can be generally helpful when working with complex structures and serves as the basis of debugger command programs (scripts). The Windbg help is very good and describes the syntax but is short on examples, especially complex examples.

There are two different syntaxes available in Windbg, MASM (assembler) and C++. Several commands allow you to choose between these, reset the debugger default, etc. For a complete treatment, see Windbg help heading ‘Evaluating Expressions’. For our purposes, there are several key things to understand. The MASM syntax is the default, but you can specify the syntax you want for your command by using either @@c++(<commands>), or @@masm(<commands>). The @@(<commands>) symbol has the effect of switching from the current syntax to the alternate syntax. If you want to evaluate an expression using the C++ syntax, you specify ‘??’ as the evaluation command, for MASM use ‘?’. Each syntax has its own strengths and weaknesses. We will be looking at traversing structures which is one of the major strengths of the C++ syntax so we will primarily be using it in this post. As not everyone involved in kernel debugging is intimately familiar with C++, examples of using this syntax can be helpful.

In order to work through the following examples, you will need to have Windbg setup on your machine with public OS symbols working correctly. You will also need a sample OS kernel crash dump. The dump must contain the full kernel memory space so a “small kernel dump” will not work. Alternatively, you can connect to, and kernel debug, a target machine. These preliminary steps are outside the scope of this post, but there are lots of articles on the web and in Windbg help explaining how to do this. I used Windows 7 X86 to create and test the examples. Once you have loaded your dump file and symbols are verified, try the following:

Get a thread to use:

  • !process - (use any addresses after “THREAD” from the output)

Dump the Executive Thread Block pointed to by this address

  • Dt <address> _ethread

Use the C++ evaluator (??) to evaluate one of the structures members after casting it to the correct type

  • ?? ((nt!_kthread *) 0x8add4540)->Process - (note that the 0x prefix is mandatory. Without this you will get an error)

Use the MASM evaluator (?) to evaluate this using MASM syntax – note the 0x150 offset is the location of the ‘Process’ member.

  • ? poi(0x8add4540 + 0x150) - (poi() dereferences a pointer. Dumps the address of the structure, but not the type as it does not map this to the symbol data)

You can use the dump type command (dt) or specific extensions to display similar type information to that seen in the c++ example, given the address from the MASM command

  • !process poi(0x8add4540 + 0x150) : gives the extension output correctly

A big advantage of C++ syntax is the access to symbolic information, as seen in the example above. Besides being more readable and easier to work with, the symbolic names of structure members are less likely to change in future OS service packs and releases than raw offsets, making this a more reliable method of traversing structures.

We can now look at a more complex example using the internal list of ‘Dispatcher’ objects associated with a particular thread. For an overview of dispatcher objects, see Windows Internals 5th Edition page 178. This post is only covering how to access objects through various syntaxes in windbg. The following structures were chosen as examples for their complexity, we are not going to look at their functionality, or synchronization issues, and we will not be creating a functionally useful script (we will do that in later posts). This exercise is just designed to learn the mechanics. We don’t need to understand the functionality of the structures, but we do need to understand how they relate to one another. The below illustration shows these relationships…

objects
The goal of this exercise is to start with address of a particular _DISPATCHER_HEADER structure, and try to get all the way to the name of the app that started the process containing the thread that is waiting for it (whew!).

The easiest way to get a _DISPATCHER_HEADER structure to play with is to look at the output of !process for the system process. Here are the commands to run in your sample dump file to get the address of a _DISPATCHER_HEADER object…

  • !process 0 0 system – (output as below, copy the highlighted address)
  • PROCESS 83f3da40 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
  • DirBase: 00185000 ObjectTable: 86c01cb8 HandleCount: 474.
  • Image: System
  • !process <address>
  • e.g. “!process 83f3da40”

<Look for any thread that shows it is waiting on something – use the address of the object being waited for in the next steps (highlighted)>

<…>
THREAD 8af5a990 Cid 0004.0740 Teb: 00000000 Win32Thread: 00000000 WAIT: (WrQueue) KernelMode Non-Alertable
8af59d80 QueueObject
Not impersonating
DeviceMap 86c07868
Owning Process 83f3da40 Image: System
Attached Process N/A Image: N/A
Wait Start TickCount 2417 Ticks: 10245 (0:00:01:42.597)
Context Switch Count 1 IdealProcessor: 0
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address srv2!SrvProcWorkerThread (0x926686cb)
Stack Init 80fe4fd0 Current 80fe4c08 Base 80fe5000 Limit 80fe2000 Call 0
Priority 8 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr
80fe4c20 8286fc6d nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
80fe4c58 8286ead3 nt!KiSwapThread+0x266
80fe4c80 8286f7c5 nt!KiCommitThreadWait+0x1df
<…>

Now that we have the address of our dispatcher object, we need to get to the first _KWAIT_BLOCK in its list of wait blocks. This is a little tricky as the _DISPATCHER_HEADER stores pointers to these in a linked list so the WaitListHead member actually contains the Flink pointing to the first _KWAIT_BLOCK (see ‘dt _DISPATCHER_HEADER and ‘dt _LIST_ENTRY’).  The Flink points to the first wait block so we will cast the Flink to this type to access its members.

In the following examples, I am using the addresses from the thread example output above. The highlighted addresses will need to be changed to match those from your system.

  • ?? ((nt!_dispatcher_header *)0x8af59d80)->WaitListHead.Flink - (to get the linked list)
  • ?? (_kwait_block *)(((nt!_dispatcher_header *)0x8af59d80)->WaitListHead.Flink) - (to get the _kwait_block object)

Looking at the _kwait_block structure, we can see that it contains a _kthread member. We can access that next…

  • ?? ((_kwait_block *)(((nt!_dispatcher_header *)0x8af59d80)->WaitListHead.Flink))->Thread

Within the _kthread structure, there is a ‘Process’ member of type _kprocess. Since the correct type is contained in the structure, we can access without casting with the C++ syntax

  • ?? ((_kwait_block *)(((nt!_dispatcher_header *)0x8af59d80)->WaitListHead.Flink))->Thread->Process

With heavy use of Windows Internals 5th Edition, and the ‘dt’ command, we can find that every _kprocess structure is the first member of a containing _eprocess structure. This means that we can cast the pointer to an _eprocess structure and access its ‘ImageFileName’ member, which will give us the name of the exe file associated with the process (our end goal)…

  • ?? (_eprocess *)(((_kwait_block *)(((nt!_dispatcher_header *)0x8af59d80)->WaitListHead.Flink))->Thread->Process)
  • ?? ((nt!_EPROCESS *)((nt!_KWAIT_BLOCK *)((nt!_DISPATCHER_HEADER *)0x8bab3250)->WaitListHead.Flink)->Thread->Process)->ImageFileName

If we want to see the actual image file name, we can use da (dump ascii) instead of ‘??’ to dump the file name. Remember though that Windbg defaults to MASM syntax so if we are not using the ‘??’ command specific to evaluating C++ syntax, we have to place our command within the @@c++() parentheses

  • da @@C++(((nt!_EPROCESS *)((nt!_KWAIT_BLOCK *)((nt!_DISPATCHER_HEADER *)0x8bab3250)->WaitListHead.Flink)->Thread->Process)->ImageFileName)

This example is chosen to be (hopefully) as complex as most scenarios you might encounter in your own debugging. In a future series of articles, I am planning to provide an example of implementing a full scripted command (or command program) that is as easy to use as Windbg built in commands / extensions, practically useful, and available across debugging sessions. The idea is to show how you can create a library of custom commands, specific to your own drivers / troubleshooting tasks.