Special Command—Parsing Strings, Files, and Commands Output Using .foreach

 

This is by far one of the most powerful WinDbg commands. Even if you don’t create scripts, you’ll benefit from this command. It’s powerful because it’s flexible. You can use it for a huge variety of operations.

The .foreach token parses the output of one or more debugger commands and uses each value in this output as the input to one or more additional commands.

Syntax

.foreach [Options] ( Variable { InCommands } ) { OutCommands }

.foreach[Options] /s ( Variable " InString" ) { OutCommands }

.foreach [Options] /f ( Variable " InFile" ) { OutCommands }

Options:

Can be any combination of the following options:

/pS InitialSkipNumber

Causes some initial tokens to be skipped. InitialSkipNumber specifies the number of output tokens that will not be passed to the specified OutCommands.

/ps SkipNumber

InString

Used with /s. Specifies a string that will be parsed; the resulting tokens will be passed to OutCommands.

InFile

Used with /f. Specifies a text file that will be parsed; the resulting tokens will be passed to OutCommands. The file name InFile must be enclosed in quotation marks.

I know it looks complicated. To help you understand the variations, I present several different examples ranging from the simplest to the most complex.

Dumping virtual memory addresses that have the “nt” string:

.foreach(obj {s -[1]a 0 0FFFFFFF "nt"}){da /c 0n100 ${obj}}

 

 

Using !DumpObj for each object address from !DumpStackObjects command. Both commands are from the extension SOS.DLL:

.foreach /pS 1 /ps 2 (obj {.shell -i - -ci "!dso" FIND "System."}){!do ${obj}}

 

In bold below is the part skipped using /pS 1 (skips 1 token) and in red are the parts skipped using /ps 2 (causes 2 tokens to be skipped repeatedly). The output is from !DumpStackObjects:

OS Thread Id: 0x1d3c (0)

ESP/REG Object Name

ebx      01d9eb80 System.Windows.Forms.Application+ThreadContext

esi  01e56a84 System.Collections.Hashtable+HashtableEnumerator

002df0a4 01e434e4 System.Windows.Forms.NativeMethods+MSG[]

002df0a8 01d9eb80 System.Windows.Forms.Application+ThreadContext

002df0b0 01d9ee10 System.Windows.Forms.Application+ComponentManager

002df0fc 01f63884 System.Windows.Forms.ApplicationContext

002df104 01f63884 System.Windows.Forms.ApplicationContext

002df128 01d9eb80 System.Windows.Forms.Application+ThreadContext

002df178 01f63884 System.Windows.Forms.ApplicationContext

002df17c 01e62b28 System.Windows.Forms.Application+ThreadContext

002df18c 01df1130 System.Windows.Forms.ApplicationContext

002df194 01f505a0 System.ComponentModel.EventHandlerList

002df198 01f63898 System.EventHandler

002df1a0 01f63898 System.EventHandler

002df1a4 01f63898 System.EventHandler

002df1a8 01e9e33c NetWiz.frmNetWiz

002df1ac 01f63884 System.Windows.Forms.ApplicationContext

002df1b0 01e62b28 System.Windows.Forms.Application+ThreadContext

002df1bc 01f63884 System.Windows.Forms.ApplicationContext

002df1c4 01e9e33c NetWiz.frmNetWiz

002df1d0 01d9ef48 System.IO.FileStream

002df1d4 01d9e960 System.Object[] (System.Diagnostics.Process[])

002df1d8 01d85a10 System.Diagnostics.Process

Not impressed yet? Ok, take a look at the code below. I’m going to run commands from a string:

$$ Creates an alias with several different commands.

as ${/v:CommandString} kb r !clrstack

$$ Extracts the commands from the string and executes them.

.foreach /s (obj "CommandString") {${obj}}

Same thing, but gets the commands from a text file:

.foreach /f (obj "C:\downloads\test.txt") {${obj}}

 

Content from text file:

kpn !clrstack !dso r

 

As I said, this is a very powerful command! I bet you’re going to find other ways to leverage it!