We’re Hiring!

February 8th, 2013

For those who don’t know, I’m a Consulting Associate at OSR Open Systems Resources, Inc. We specialize in all things kernel mode on Windows, from file systems to device drivers to general Windows internals knowledge. If you come to work for us, you’ll get to work on all kinds of interesting projects in different ways (i.e. development, design, review, etc.). We also train students all over the world, which has its own fun and challenges.

I’m about to pass my 11 year anniversary here, so I think it’s a pretty great place to work and maybe you will too! Check out our job posting here:


And feel free to contact me if you have any questions.

Why is lmv sometimes more verbose?

January 28th, 2012

While working in WinDBG, lm is the standard command for viewing the loaded module list of the target. Amongst other things, this command takes a v flag to increase the verbosity as well as an m flag to match a particular module. Thus, if you want to see verbose information for a module named foo you can execute the following command:

lmv mfoo

That’s all fine and good, but you might notice that some modules provide more information than others. For example, check out the detailed information provided for the NTFS module:

1: kd> lmv mntfs
start             end                 module name
fffff880`01250000 fffff880`013f3000   Ntfs       (deferred)
    Image path: \SystemRoot\System32\Drivers\Ntfs.sys
    Image name: Ntfs.sys
    Timestamp:        Mon Jul 13 19:20:47 2009 (4A5BC14F)
    CheckSum:         00195F88
    ImageSize:        001A3000
    File version:     6.1.7600.16385
    Product version:  6.1.7600.16385
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        3.7 Driver
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     ntfs.sys
    OriginalFilename: ntfs.sys
    ProductVersion:   6.1.7600.16385
    FileVersion:      6.1.7600.16385 (win7_rtm.090713-1255)
    FileDescription:  NT File System Driver
    LegalCopyright:   © Microsoft Corporation. All rights reserved.

Versus the limited information we get for the FltMgr module:

1: kd> lmv mfltmgr
start             end                 module name
fffff880`010fa000 fffff880`01146000   fltmgr     (deferred)
    Image path: \SystemRoot\system32\drivers\fltmgr.sys
    Image name: fltmgr.sys
    Timestamp:        Mon Jul 13 19:19:59 2009 (4A5BC11F)
    CheckSum:         00056413
    ImageSize:        0004C000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

What’s up with that?

The answer is the availability of resource information. The PE file format allows for a .rsrc section, which contains developer provided information about the binary (company, version, description, etc.). The information placed here comes from a .rc file supplied when the image is compiled and is usually accessed via the Details tab of the file properties:

If an image contains a resident and valid .rsrc section, lmv will provide the details from it as part of its output. If we dump the PE header for the NTFS module, we can see that it does indeed have a .rsrc section:

1: kd> !dh ntfs
    8664 machine (X64)
       8 number of sections
4A5BC14F time date stamp Mon Jul 13 19:20:47 2009
   .rsrc name
   1CFF0 virtual size
  185000 virtual address
   1D000 size of raw data
  176600 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         (no align specified)
         Read Only

Which explains why we saw all of that detailed info for NTFS. But why didn’t we see it for FltMgr? In fact, if we check out the file properties for FltMgr.sys we see that it does indeed have a valid resource section:

But lmv was unable to parse it for some reason. Let’s check out the PE header to find out why:

1: kd> !dh fltmgr
File Type: DLL
    8664 machine (X64)
       B number of sections
4A5BC11F time date stamp Mon Jul 13 19:19:59 2009
   .rsrc name
    1E50 virtual size
   49000 virtual address
    2000 size of raw data
   42E00 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
42000040 flags
         Initialized Data
         (no align specified)
         Read Only

Aha! The .rsrc section is marked as discardable, meaning the the image loader is free to remove this section from memory when the module is loaded. Thus, while this module has a valid .rsrc section on disk, it is not guaranteed to have one in memory.

Discardable is the default used by the compiler/linker for resource sections because the information is typically only used in file property dialogs, where it can easily be retrieved from the image on disk. However, certain Microsoft supplied modules (such as NTFS) go out of their way to mark their resource sections as non-discardable to make it possibly to query version information from the debugger at the cost of some extra RAM.

Scanning for Control Areas (!ca and !memusage)

January 3rd, 2012

Control areas are an important data structure in Windows in that they track the state of memory mapped files. From this state you can find out things like the name of the file that is memory mapped, the location of the data in memory, etc.

Control areas are created not only for files that are memory mapped for data and executable access, but even for files being accessed via cached I/O (internally the Cache Manager memory maps these files), “special” files such as the MFT (the file systems effectively perform cached I/O on these files), and memory mapped sections that are backed by the paging file. As you can imagine, these data structures can be useful when analyzing a memory dump for lots of different reasons. So, how do you go about finding control areas to play with?

Turns out, there are two different methods provided by WinDBG for quickly scanning a target for control areas. Note that control areas can also be found indirectly through file objects, but that’s a separate technique that we’re not covering here. 

The first is the !ca command, which is normally used to display a control area that you already have the address of:

0: kd> !ca fffffa8003c2c770
ControlArea  @ fffffa8003c2c770
  Segment      fffff8a00f39d190  Flink      fffffa8003c2c3f8
  Section Ref                 0  Pfn Ref                   1
  User Ref                    0  WaitForDel                0
  File Object  fffffa800593dc40  ModWriteCount             0
  WritableRefs                0
  Flags (4080) File Accessed     
File: \Program Files\Debugging Tools for Windows (x64)\windbg.exe
Segment @ fffff8a00f39d190
  ControlArea     fffffa8003c2c770  ExtendInfo    0000000000000000
  Total Ptes                    9d
  Segment Size               9cd10  Committed                    0
  Flags (c0000) ProtectionMask
Subsection 1 @ fffffa8003c2c7f0
  ControlArea  fffffa8003c2c770  Starting Sector        0
  Base Pte     fffff8a00a3e2820  Ptes In Subsect       9d
  Flags                d100000d  Sector Offset        d10
  Flink        fffffa8003c2c4c0  Blink   fffffa8006a1cae0

For information about how to use the above output to retrieve the contents of the file, refer to my previous article found here.

However, !ca also has another interesting usage. If instead of a valid control area address you specify 0, you’ll be greeted to a list of all the control areas the command can find. It doesn’t do this via any sort of elegant method, instead what it does is search the various executive pools for allocations with the appropriate pool tags:

0: kd> !ca 0
Scanning large pool allocation table for Tag: MmCa (fffffa80072df000 : fffffa80075df000)
fffffa8005115bd0 0       File: \$Directory
fffffa80057a7450 1       Pagefile-backed section
Searching NonPaged pool (fffffa8003901000 : ffffffe000000000) for Tag: MmCa
fffffa80039cbd40 0       File: \WinDDK\7600.16385.1\inc\api\SpecStrings_strict.h
fffffa800396c250 0       File: \Windows\Fonts\couri.ttf
fffffa8007738640 0       File: \Windows\SysWOW64\davhlpr.dll
Scanning large pool allocation table for Tag: MmCi (fffffa80072df000 : fffffa80075df000)
fffffa80051596c0 0       Image: \Windows\SysWOW64\WMASF.DLL
fffffa8003e51260 0       Image: \Windows\SysWOW64\thumbcache.dll
fffffa8003e575f0 0       Image: \Windows\SysWOW64\netshell.dll
fffffa80061f5b50 0       Image: \Windows\System32\samlib.dll
fffffa80067125d0 0       Image: \Windows\System32\atiu9p64.dll
fffffa8006e34610 0       Image: \Windows\System32\WUDFx.dll
fffffa80056d1df0 0       Image: \Windows\System32\iertutil.dll
fffffa80056f49c0 0       Image: \Windows\System32\urlmon.dll
Searching NonPaged pool (fffffa8003901000 : ffffffe000000000) for Tag: MmCi
fffffa8003aace30 0       Image: \Windows\System32\dmocx.dll
fffffa8003ae5750 0       Image: \WinDDK\7600.16385.1\bin\x86\rc.exe
fffffa8006dff160 0       Image: \Windows\System32\mapi32.dll
fffffa8006dff460 0       Image: \Windows\SysWOW64\mfplat.dll
fffffa8006e169a0 0       Image: \Windows\SysWOW64\d3d8thk.dll
fffffa8006e2b5a0 0       Image: \Windows\SysWOW64\atiumdva.dll

From here, you can start running !ca on whatever control areas you might find interesting (the control area address is the address in the first column of the output). Note that files may in fact have two control areas, one for data access as well as one for image access.

The other way of finding control areas is by using the !memusage command (this is, in fact, the method documented in the Windows Internals book). This method is different in that instead of searching for pool tags, the !memusage command actually scans the PFN database and uses the data from the entries to find control areas. This is a much more complex procedure that we’ll have to save for another time :)


Happy New Year! (Well, almost…)

December 28th, 2011

Another long span without a post and another apologetic post about the delay…Eventually I’ll learn my lesson and stop doing this :)

It’s certainly shaping up to be an exciting year. With the release of Windows 8 we’ll have kernel debugging and crash analysis in Visual Studio, another processor architecture to learn, and new releases of WinDBG. Should be LOTS of fodder for blog posts in the months to come and remove any excuse I may have for not updating.

Looking forward to another year interacting with all of you and learning some new things. As always, feel free to keep me honest by joining my network on LinkedIn or following me on Twitter.

Here’s to hoping everyone has a healthy and prosperous new year!


Grokking the dt command

July 10th, 2011

Viewing data structures is usually a critical part of any analysis. In WinDBG, there are several different ways in which you can inspect data structures. However, one of the most common ways is with the Display Type command, dt. On the surface dt is pretty simple, you specify a data structure name and WinDBG will show you the fields of the structure along with their offsets and types:

0: kd> dt nt!_ksemaphore
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : Int4B

In addition, you can optionally specify an address to be interpreted as the specified type so that you can inspect the fields of the structure:

0: kd> dt nt!_ksemaphore 8a084120
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n1

Now that we have the basics down, let’s see some advanced usage of this command that will allow us to quickly and easily navigate any structure that we’re interested in.

Inspecting Structure Fields

In the above example, the standard dt output showed us two fields of the KSEMAPHORE structure: Header and Limit. If we knew beforehand that we only cared about the Limit field, we could specify an additional parameter to the dt command and limit the output to only show the Limit field:

0: kd> dt nt!_ksemaphore Limit
   +0x010 Limit : Int4B
0: kd> dt nt!_ksemaphore 8a084120 limit
   +0x010 Limit : 0n1

Note that the Header field itself was a data structure, though it wasn’t expanded in the standard dt output and it won’t be expanded if we limit the output to just show the Header field:

0: kd> dt nt!_ksemaphore header
   +0x000 Header : _DISPATCHER_HEADER
0: kd> dt nt!_ksemaphore 8a084120 Header
   +0x000 Header : _DISPATCHER_HEADER

However, you can use standard C syntax to inspect specific fields of the substructure:

0: kd> dt nt!_ksemaphore header.type
   +0x000 Header      :
      +0x000 Type        : UChar

In order to actually enumerate the fields of that structure we’re going to need to specify yet another parameter. Now, this is where the dt command syntax gets messy and inconsistent. There are, in fact, three different ways to expand substructures with the dt command: /r, ., and ->*. Let’s see when each of these are appropriate.

Structure Expansion With /r

The /r switch to the dt command is the, “recursively dump the subfields” switch. The switch takes a number between 1-9 to indicate the depth to which the command should traverse dumping structures. Using this method is only effective if you are not specifying a subfield of the structure. For example, these work as expected:

0: kd> dt nt!_ksemaphore /r1
   +0x000 Header           : _DISPATCHER_HEADER
      +0x000 Type             : UChar
      +0x001 Absolute         : UChar
      +0x002 Size             : UChar
      +0x003 Inserted         : UChar
      +0x004 SignalState      : Int4B
      +0x008 WaitListHead     : _LIST_ENTRY
   +0x010 Limit            : Int4B
0: kd> dt nt!_ksemaphore /r2 8a084120
   +0x000 Header           : _DISPATCHER_HEADER
      +0x000 Type             : 0x5 ''
      +0x001 Absolute         : 0 ''
      +0x002 Size             : 0x5 ''
      +0x003 Inserted         : 0 ''
      +0x004 SignalState      : 0n0
      +0x008 WaitListHead     : _LIST_ENTRY [ 0x898a0090 - 0x898a0090 ]
         +0x000 Flink            : 0x898a0090 _LIST_ENTRY [ 0x8a084128 - 0x8a084128 ]
         +0x004 Blink            : 0x898a0090 _LIST_ENTRY [ 0x8a084128 - 0x8a084128 ]
   +0x010 Limit            : 0n1

However, if you attempt to expand just the Header field, you’ll see that the switch is ignored:

0: kd> dt nt!_ksemaphore /r1 Header
   +0x000 Header : _DISPATCHER_HEADER
0: kd> dt nt!_ksemaphore /r1 8a084120 Header
   +0x000 Header : _DISPATCHER_HEADER

 In order to perform that type of expansion, we’ll need the . and ->* switches.

 Structure Expansion With .

In our last example, we were trying to only expand the Header field of the KSEMAPHORE structure but the /r switch was ineffective. In order to expand the embedded structure by name in that case, we need to follow the structure name with a period. This indicates to the engine that you would like to view only that member of the structure and you would like to expand it:

0: kd> dt nt!_ksemaphore 8a084120 Header.
   +0x000 Header  :
      +0x000 Type    : 0x5 ''
      +0x001 Absolute : 0 ''
      +0x002 Size    : 0x5 ''
      +0x003 Inserted : 0 ''
      +0x004 SignalState : 0n0
      +0x008 WaitListHead : _LIST_ENTRY [ 0x898a0090 - 0x898a0090 ]

In addition, you can keep adding additional periods to keep increasing the depth of the expansion:

0: kd> dt nt!_ksemaphore 8a084120 Header..
   +0x000 Header   :
      +0x000 Type     : 0x5 ''
      +0x001 Absolute : 0 ''
      +0x002 Size     : 0x5 ''
      +0x003 Inserted : 0 ''
      +0x004 SignalState : 0n0
      +0x008 WaitListHead :  [ 0x898a0090 - 0x898a0090 ]
         +0x000 Flink    : 0x898a0090 _LIST_ENTRY [ 0x8a084128 - 0x8a084128 ]
         +0x004 Blink    : 0x898a0090 _LIST_ENTRY [ 0x8a084128 - 0x8a084128 ] 

  Structure Expansion With ->*

In the above case, the period suffix was appropriate because the field of the structure that we cared about was an embedded structure, not a pointer to a structure. Let’s try that again with a pointer to a structure, such as the Dpc field of the KTIMER:

0: kd> dt nt!_ktimer b46c82a0
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 DueTime          : _ULARGE_INTEGER 0xd2`333b7e55
   +0x018 TimerListEntry   : _LIST_ENTRY [ 0x8a06fb10 - 0x80562628 ]
   +0x020 Dpc              : 0xb46c82e0 _KDPC
   +0x024 Period           : 0n0
0: kd> dt nt!_ktimer b46c82a0 Dpc
   +0x020 Dpc : 0xb46c82e0 _KDPC
0: kd> dt nt!_ktimer b46c82a0 Dpc.
   +0x020 Dpc  :
Cannot find specified field members.

Oops! That doesn’t work so hot…Instead, we need a different suffix to the field name: ->* (no, I’m not joking, that’s really the syntax :) ). Taking our previous example:

0: kd> dt nt!_ktimer b46c82a0 Dpc->*
   +0x020 Dpc   :
      +0x000 Type  : 0n19
      +0x002 Number : 0 ''
      +0x003 Importance : 0x1 ''
      +0x004 DpcListEntry : _LIST_ENTRY [ 0x0 - 0x0 ]
      +0x00c DeferredRoutine : 0xb46be385        void  rdbss!RxTimerDispatch+0
      +0x010 DeferredContext : (null)
      +0x014 SystemArgument1 : (null)
      +0x018 SystemArgument2 : (null)
      +0x01c Lock  : (null)�

There are, of course, lots of other things that can be done with the dt command as well. However, hopefully that clears up some confusion and motivates you to find some other interesting things in the docs!

Bonus Tip: Quick Access to Last Type Used

Before we leave, here’s one last quick tip about this command. The dt command always stores the last type inspected and gives you quick access to the previous type by accepting a period character for the type name. This is best explained with an example. So, let’s say we want to look at two KSEMAPHORE structures. We actually only need to type the entire type name out the first time, the second time we can save a keyboard and just use a period for the type name:

0: kd> dt nt!_ksemaphore 8a084120
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n1
0: kd> dt . 8a0281e0�
Using sym nt!_ksemaphore
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n2147483647


Pseudo Registers have types

May 24th, 2011

OK, so, I was shamed into updating my blog this week…Sorry for the long delay, though I can’t promise that it won’t happen again :)

I’ve talked about pseudo registers and interesting ways to use them before, but I’ve failed to mention one other nice feature about them: some pseudo registers are typed when using the C++ evaluator. Specifically, the @$thread, @$proc, @$teb, and @$peb registers are typed to be their appropriate data structure. This means that when using these pseudo registers in a C++ expression, you can just reference whatever fields you want directly as if they are pointers.

So, for example, if I want to inspect the TopLevelIrp field of the current thread (which is a very strange field and a discussion of its own for another day), I can just do the following:

0: kd> ??@$thread->TopLevelIrp
unsigned long 0

Or maybe I want to see the command line that was used to launch the current process. In that case, I can just do the following:

0: kd> ??@$peb->ProcessParameters->CommandLine
 "windbg.exe  -z C:\Windows\livekd.dmp"
   +0x000 Length           : 0x48
   +0x002 MaximumLength    : 0x4a
   +0x004 Buffer           : 0x003b1c8a  "windbg.exe  -z C:\Windows\livekd.dmp"

Pretty handy and avoids having to do some otherwise nasty scripting.

Note that you can read more about C++ expressions in this article from The NT Insider.

Spot the WinDBG Script Bug: Solution

February 21st, 2011

In my previous post I asked you to find the issue in the following script:

as Host
as Port 6969
sx- -c “!load winext\\MSEC.dll;!exploitable -bestormhost:Host-bestormport:Port” sov

So in this post I’ll solve the mystery…

Whenever I have a problem like this, I like to break it down into something simpler so that I can verify the result myself before declaring victory. In this case, the script is using a debugger extension DLL and command that aren’t part of the standard WinDBG package, so my first idea was to simplify the script and just have the script do the following:

as Host
as Port 6969
.echo Host Port

If you save the above script to a file script.txt and execute the following command:


You will get a surprising result: No output! Nada, zip, zero, zilch:

How can that be? The answer lies in the documentation for as and $$><.  First, from the as docs:

If you do not use any switches, the as command uses the rest of the line as the alias equivalent.

OK, so as sets the remainder of the line (up to the newline) to be the alias equivalent. Now, from the $$>< docs:

 The $>< and $$>< tokens open the script file, replace all carriage returns with semicolons, and execute the resulting text as a single command block.

So, as sets the entire line after the alias name to be the alias and $$>< turns the entire script into a single line. Hmmm…Let’s check the aliases after the script is run:

Aha! Now it’s clear what’s going on, that first as command ended up eating the entire script and turning it into an alias for Host!

This is why the aS command exists. That command allows you to specify a quoted string to set the alias to, which avoids this type of confusion:

aS Host ""
aS Port "6969"
.echo Host Port

However, before giving this script a try we have a couple of other refinements that we can make to it. First off, while it may appear to work in some cases, you should always wrap aliases in the alias interpreter command ${}. In that case, our .echo command should be:

.echo ${Host} ${Port}

In addition, whenever I define my aliases I also like to wrap them in the alias interpreter command with the /v: switch, which deliberately shuts off the alias interpreter. This prevents you from recursively defining the alias in cases where your script is run for multiple invocations:

aS ${/v:Host} ""
aS ${/v:Port} "6969"

Putting it all together, we can now save the following in a text file and execute it:

aS ${/v:Host} ""
aS ${/v:Port} "6969"
.echo ${Host} ${Port}

D’oh! Still not there! What’s the problem this time?

Faithful readers will remember the previous post on using WinDBG aliases, in which we discovered that aliases are not interpreted until some kind of block statement is reached. Because there are no block statements in the above script, the aliases aren’t interpreted until the script has finished running. Thus our .echo statement just shows us the literal string value that we passed it. In order to fix this final issue we simply need to add a .block directive after we have defined the aliases:

aS ${/v:Host} ""
aS ${/v:Port} "6969"
.block {
    .echo ${Host} ${Port}

Using what I learned from my simplified script, this is the final solution that I submitted to the reader which ended up solving the issue:

aS ${/v:Host} ""
aS ${/v:Port} "6969"
.block {
    sx- -c “!load winext\\MSEC.dll;!exploitable -bestormhost:${Host}-bestormport:${Port}” sov

Spot the WinDBG Script Bug

February 17th, 2011

A reader sent me a question (snoone at this domain) because their WinDBG script wasn’t working properly. Here’s the script and the problem:

I am trying to do this (inside a windbg script):

as Host
as Port 6969

sx- -c “!load winext\\MSEC.dll;!exploitable -bestormhost:Host-bestormport:Port” sov

But the Host and Port aren’t being interpreted rather they are being passed as is

There are two scripting bugs in here, can you spot them? I’ll have a follow up post this week to explain the issues and name a winner if there is one. Your prize for being the winner? Absolutely nothing! I do run this blog at a loss you know :)

Interpreting !pool results part II: Large Pool oddities

February 14th, 2011

In my previous post Interepeting !pool results, I talked about the !pool command and walked through the output. However, I purposefully left off a couple of details when it comes to non-standard pool allocations, such as allocations from special pool and large pool allocations. This led to a question from a reader asking for an explanation of the strange output they were seeing when running !pool on a particular allocation:

Note how there appears to be two entries for a single allocation, one showing this as a freed allocation and the other saying that it is a valid, “large page allocation” of 0x2bc0 bytes. What’s up with that?

The answer is that large pool allocations are treated differently from other types of allocations. An allocation is considered to be a large allocation if it cannot fit within a page of memory (as defined by the architecture, i.e. 4K on the x86/x64) minus the overhead of the pool header required on the block. So, for example, an allocation of PAGE_SIZE bytes would be considered a large page allocation and thus tracked as such.

A large allocation is unique in that it does not contain a POOL_HEADER structure. Instead, the caller is returned a page aligned virtual address and the pool header is tracked in a global variable in the system. When a page aligned addressed is freed back to the allocator, the large pool table is consulted to determine if the allocation is indeed a large page so that the memory can be freed properly (NOTE: on legacy systems such as XP, this was managed a bit differently and the global table was only maintained when pool tracking was enabled via GFlags).

Now we can begin to unravel why the !pool output above is confused. Remember from the previous post that when supplied with an address, !pool will round the address down to PAGE_SIZE and interpret the result as a POOL_HEADER structure. However, in this case the address supplied is the base of a large pool allocation, thus it does not start with a POOL_HEADER structure. So what we have is the first bytes of this driver’s pool allocation being interpreted as a POOL_HEADER:

This is effectively garbage and thus the walk to the next entry fails. However, before reporting an error !pool decides to consult the large pool allocation table nt!PoolBigPageTable to determine if this is a valid large pool allocation (the structure of this table is not documented, however if you’re interested enough to care it’s easy enough to figure out :) ). In this case an entry is found, thus we get a successful hit and the output corrects itself.

I consider this to be a bug in the implementation, it would be better to look at the large pool allocation table first and then fall back to interpreting it as a pool header if it is not found. While this would be more overhead in the common case, it would make the less common case a little less confusing. As such, I have reported it as a bug and hopefully we’ll get a fix in the future.

Using WinDBG aliases

November 30th, 2010

WinDBG commands and scripts can get messy. Real messy. One of my major goals in life is to always write my WinDBG scripts such that the next person would be able to pick them up and make fixes or adjustments if needed, which is why I always turn to aliases when I’m writing anything but the most trivial scripts.

Aliases are simple: they’re a string substitution mechanism that you can use in your debugging sessions and scripts. I’ve shown you an alias here before, though now we’ll cover how to use aliases more effectively in a more general way.

No big surprise here, but understanding aliases comes down to three things: creating aliases, using aliases, and deleting aliases. In order to create aliases, all you need to do is use the as or aS command. Both commands take an alias to create as well as the string that should be substituted for the alias. The difference between the two is subtle, so I’ll try to untangle that here.

If you use the as command, everything after the alias name up to the newline is included in the alias definition. We can see the results of that here if we use the al command to list the aliases after we’re done:

0: kd> as FooAlias This is all kinds of stuff ; .echo Bar
0: kd> al
  Alias            Value
 -------          -------
 FooAlias         This is all kinds of stuff ; .echo Bar

Note that the semicolon is not used as a delimiter here. Alternatively, the aS command stops at semicolons so you get different results if you try the above with aS:

0: kd> aS FooAlias This is all kinds of stuff ; .echo Bar
0: kd> al
  Alias            Value
 -------          -------
 FooAlias         This is all kinds of stuff 

aS also has another nice feature in that you can enclose the alias string in quotes, making what goes into the alias explicit. Everything in between the quotation marks will be entered as the alias and the command will not stop at semicolons:

0: kd> aS FooAlias "This is all kinds of stuff ; .echo Bar"
0: kd> al
  Alias            Value
 -------          -------
 FooAlias         This is all kinds of stuff ; .echo Bar

For this reason aS is my preferred command for creating aliases. I recommend that you read the documentation for the as and aS commands as there are other interesting options to the command that I’m specifically ignoring (e.g. you can set an alias to the contents of an environment variable).

Once you’ve created an alias, it’s time to use it as part of an expression. This is where the alias interpreter command ${} comes into play. Any string put in between the braces will go through string substitution if it is an alias, so for example:

0: kd> aS Ping "Pong"
0: kd> .echo ${Ping}

The intepreter also has some nifty switches to it, for example the /d switch which checks to see if the alias is defined or not. If it is defined, the expression evaluates to 1 otherwise it evaluates to 0:

0: kd> aS Ping "Pong"
0: kd> .echo ${/d:Ping}
0: kd> .echo ${/d:BadAlias}

This is how we can check for the presense of arguments to scripts run with the $$>a< command, which creates aliases for each argument supplied. There is also the /v switch, which shuts the evaluator off and returns the literal string passed to the interpreter. This is handy if you want to display or use the name of the alias and not the contents. More details about the alias interpreter and its switches can be found in the documentation.

The last thing to know about aliases relates to their use in WinDBG scripts. One thing you must remember is that aliases are not expanded until a new block is entered. New blocks can be created with the various control flow tokens (e.g. .if, .while, .do, etc.) and arbitrarily with the .block token. This can have unexpected results if you’re not aware of it. Let’s take a brain dead script:

aS AliasTest "This is a test"
.echo ${AliasTest}

The first time you run this you will get some unexpected output:

0: kd> $$><c:\dumps\aliastest.wbs

As you can see, the alias was not properly expanded. However, if you check the alias list you will see that it is indeed there:

0: kd> al
  Alias            Value�
 -------          -------
 AliasTest        This is a test

The problem is that without a block statement, the aliases were not evaluated until after the script ran. We can fix this by putting an artificial block in the script file:

aS AliasTest "This is a test"
$$ Force any previous aliases to be evaluated.
    .echo ${AliasTest}

 This time we end up with what we expected:

0: kd> $$><c:\dumps\aliastest.txt
This is a test

Lastly, let’s delete our aliases that we’ve created. This can be done with the ad command, which will delete a specific alias or all aliases if passed an asterisk. There is a trick here though: beware alias expansion! If you’re not careful, the alias interpreter will pick up the alias name that you’re trying to delete and do a string substitution. The best way to avoid this is to use the /v switch to the interpreter, which will force it to shut off alias expansion:

aS AliasTest "This is a test"
$$ Force any previous aliases to be evaluated.
    .echo ${AliasTest}
ad ${/v:AliasTest}

That’s it for now…Try using aliases in your scripts and commands and see if they help you out at all. If you want some inspiration, you can look at the scripts that I’ve posted here previously for examples on how I use aliases:

Undocumented !process flags and switches

Finding image load and process notification callbacks

Finding registered Configuration Manager (i.e. registry) callbacks