Published by OpenTask, Republic of Ireland

Copyright © 2009 by OpenTask

 

Win32dd – Memory Imaging article © Matthieu Suiche

PowerDbg v5.1 – Using PowerShell to Control WinDbg article © Roberto Alexis Farah

All other articles in this issue © Dmitry Vostokov

 

All rights reserved.

 

Debugged! MZ/PE: MagaZine for/from Practicing Engineers

                                                                           

Volume 1, Issue 1, March 2009

           

            Welcome to the first issue of Debugged! MZ/PE magazine! More than 3 years ago I conceived an idea of a Debugging Gazette but was swayed by a blogging wave. Last year DumpAnalysis.org together with OpenTask iterative and incremental publisher announced 2009 as The Year of Debugging, 2010 as The Year of Dump Analysis and the magazine is the vital part of this initiative.

            The goal of every issue is to be useful as much as possible and be on a desk of practicing engineers for as long as possible. By engineers I mean software engineers in general, software technical support and escalation engineers, and software test engineers. To achieve this goal every issue features useful tables and charts in full color and back covers can be used as mini posters featuring tips on debugging. The focus is on Windows debugging but not limited to if any information about other platforms is of sufficient generality.

            Despite an economic downturn and recession we decided to go ahead and even increased the number of pages for the first issue.

 

Dmitry Vostokov

Editor-in-Chief

 


 

Pattern-Driven Memory Dump Analysis WinDbg Command Supplement

Dmitry Vostokov, 1st March 2009
http://www.dumpanalysis.org

 

On 26th of February at a Global Engineering Conference organized by Microsoft Global Escalation Services team I was presenting Pattern-Driven Memory Dump Analysis methodology that involves scripts, checklists and patterns and can be summarized on a waterfall-like diagram to the right of this column. We can use various scripts to get textual information from memory dumps, then we can use various checklists to extract specific information and aid the identification of memory dump analysis patterns, common recurrent identifiable problems together with a set of recommendations and possible solutions to apply in a specific context. So far we have identified more than 100 patterns[1] and many readers of my blog suggested to map them to WinDbg commands or command combinations used to identify them. This article is a first attempt to do such mapping based on commands I used to describe patterns on my blog and in Memory Dump Analysis Anthology volumes. It can also be used as a command reminder and serve as a list of the most used core WinDbg commands.

Patterns that can only be found in kernel or complete memory dumps are shown in red color. All other patterns can be seen in user, kernel and complete memory dumps. Some commands are grouped together if they are similar, for example, da, dpa, du and dpu commands.

 

 

 

The table (12 pages) is available in the printed version.

 

 

 

 

Win32dd – Memory Imaging

Matthieu Suiche, 13th February 2009
matt[0x40]msuiche[0x2E]net
http://www.msuiche.net

 

Physical Memory (also known as RAM) contains vari­ous types of volatile information. Each time an appli­cation is launched a copy of it is loaded into the physical memory; this includes the physical application as provided on the hard drive and also tempo­rary memory buffers used by the application during its ex­ecution. Physi­cal memory analysis is very valuable for in­cident response analysis because inves­tigators are able to collect infor­ma­tion they won’t be able to collect on hard drive during an analysis on a “dead” system. Live system analysis has sev­eral notable advantages compared to classical hard drive analysis. Moreover, investigators who proceed to an analy­sis of a physical memory snap­shot can use sev­eral external tools that do not modify the contents of the physical memory state unlike launching tools on an run­ning Oper­ating System. 

 

Win32dd is an open-source tool to provide an effi­cient way to acquire physical memory in two different formats. The first format is the most common, out­put file is a raw format and contains the exact con­tent of the physical memory; the second format is the one used by Microsoft to generate a crash dump file. This feature aims at pro­viding users a compliant format used by Microsoft debug­ging and troubleshoot­ing tools like Windows Debugger (WinDbg).

 

 

Raw memory dumps

Raw physical memory snapshots are widely used in the forensics industry and also by memory acquisition tools even if they are not the most powerful to pro­ceed to an efficient analysis regarding common things like process listing, modules listing, drivers list­ing and so on. As the name suggests this is a raw dump of the full physical memory usually called \Device\PhysicalMemory by Win­dows section ob­jects.

 

From a developer point of view, it is very impor­tant to notice that classical (user-mode) memory im­aging tools cannot proceed to a physical memory snapshot because Microsoft decided to block access to the \Device\ PhysicalMemory object from user-land applica­tions for secu­rity reasons as we can read on the Microsoft Developer Network website[2].

“In Windows Server 2003 SP1, user-mode access to the \Device\PhysicalMemory object is not permitted. All forms of access (read, write) are refused when the \Device\PhysicalMemory object is accessed from a user-mode application. Access to the \Device\PhysicalMemory object is refused regardless of the user context (Administrators, Users, Local System, etc.) the application is running in.”

 

This part explains why additional privileges are re­quired from investigators to access to the Windows physical memory under an operating system version higher to Windows Server 2003 because of the need to load a ker­nel-land module to access to this specific object section.

 

Pros This dump file type contains the exact physical memory content; physical offset aligned on file off­set.


Cons
Lack of additional information like processor state control registers that are man­datory to emulate Windows memory management, especially virtual to physical address translation[3].

 

Microsoft crash dump file

Physical memory acquisition is used for post-mortem de­bugging purposes, security pur­poses (e.g. rootkit analysis, information extraction and so on.). It is mainly known because of the Microsoft feature called “Blue Screen of the Death” (BSOD) that generates a crash dump file. There are three different types of crash dump files: small memory dump, kernel memory dump and complete memory dump.

This article only covers complete memory dumps.

Complete memory dump is the most useful mem­ory snapshot for advanced analysis because it contains the full physical memory including both user-land and kernel-land space. It can be easily used, for example, to unpack code for both user-land and kernel-land applications. 

Pros This is a full memory snapshot of pages used by Windows Memory Manager this in­cludes both user-land and kernel-land. It means both malwares and rootkits analysis can be done.


Cons
This option might not be available on com­puters that are running a 32-bit operating system and that have 2 gigabytes (GB) or more of RAM ac­cording to Mi­crosoft KB 274598.[4]

Here is Microsoft crash dump header in a 32-bit format:

typedef struct _DUMP_HEADER32 {

    /* 0x000 */ ULONG Signature;

    /* 0x004 */ ULONG ValidDump;

    /* 0x008 */ ULONG MajorVersion;

    /* 0x00C */ ULONG MinorVersion;

    /* 0x010 */ ULONG DirectoryTableBase;

    /* 0x014 */ ULONG PfnDataBase;

    /* 0x018 */ PLIST_ENTRY PsLoadedModuleList;

    /* 0x01C */ PLIST_ENTRY PsActiveProcessHead;

    /* 0x020 */ ULONG MachineImageType;

    /* 0x024 */ ULONG NumberOfProcessors;

    /* 0x028 */ ULONG BugCheckCode;

    /* 0x02C */ ULONG BugCheckParameter1;

    /* 0x030 */ ULONG BugCheckParameter2;

    /* 0x034 */ ULONG BugCheckParameter3;

    /* 0x038 */ ULONG BugCheckParameter4;

    /* 0x03C */ UCHAR VersionUser[32];

    /* 0x05C */ UCHAR PaeEnabled;

    /* 0x05d */ UCHAR KdSecondaryVersion;

    /* 0x05e */ UCHAR Spare3[2];

    /* 0x060 */ PKDDEBUGGER_DATA64

      KdDebuggerDataBlock;

    union {

        /* 0x064 */ PHYSICAL_MEMORY_DESCRIPTOR32

           PhysicalMemoryBlock;

        /* 0x064 */ UCHAR

           PhysicalMemoryBlockBuffer[700];

    }

    union {

        /* 0x320 */ CONTEXT Context;

        /* 0x320 */ UCHAR ContextRecord[120];

    }

    /* 0x7d0 */ EXCEPTION_RECORD32 Exception;

    /* 0x820 */ UCHAR Comment[128];

    /* 0x8a0 */ UCHAR _reserved0[1768];

    /* 0xf88 */ ULONG DumpType;

    /* 0xf8c */ ULONG MiniDumpFields;

    /* 0xf90 */ ULONG SecondaryDataState;

    /* 0xf94 */ ULONG ProductType;

    /* 0xf98 */ ULONG SuiteMask;

    /* 0xf9c */ ULONG WriterStatus;

    /* 0xfa0 */ LARGE_INTEGER RequiredDumpSpace;

    /* 0xfa8 */ UCHAR _reserved2[16];

    /* 0xfb8 */ LARGE_INTEGER SystemUpTime;

    /* 0xfc0 */ LARGE_INTEGER SystemTime;

    /* 0xfc8 */ UCHAR _reserved3[56];

} DUMP_HEADER32, *PDUMP_HEADER32;

Win32dd

 

We need to have both win32dd.exe and win32dd.sys in the same directory. Moreover, we also need ad­ministrator rights and enough permission to load the driver.


Here is the output of the help option:


C:\ >win32dd.exe -h

 

  Win32dd - v1.2.1.20090106 - Kernel land physical memory acquisition

  Copyright (c) 2007 - 2009, Matthieu Suiche <http://www.msuiche.net>

  Copyright (c) 2008 - 2009, MoonSols <http://www.moonsols.com>

 

Usage:

  win32dd.exe [option] [output path]

 

Option:

  -r    Create a raw memory dump/snapshot. (default)

  -l    Level for the mapping (with -r option only).

     l 0  Open \\Device\\PhysicalMemory device (default).

     l 1  Use Kernel API MmMapIoSpace()

 

  -d    Create a Microsoft full memory dump file (WinDbg compliant).

  -t    Type of MSFT dump file (with -d option only).

     t 0  Original MmPhysicalMemoryBlock, like BSOD. (default).

     t 1  MmPhysicalMemoryBlock (with PFN 0).

 

  -h    Display this help.

 

 

Sample:

Usage: win32dd.exe -d physmem.dmp

Usage: win32dd.exe -l 1 -r C:\dump\physmem.bin

 


 

Raw memory dump (-r option)

 

The –r option generates a raw dump. This is a default op­tion if we don’t indicate to win32dd that we want to gen­erate the dump file type we want.


Level 0

 

As we explained before, \Device\PhysicalMemory access has been restricted to kernel-land only since Win­dows 2003 SP1. That’s why win32dd reads it from kernel-land. This level is a default option, if we don’t indicate to win32dd which level we want.

 

Level 1

 

Because of anti-forensics techniques, win32dd pro­vides an alternative methods to dump the physical memory through the Kernel Memory Management API: MmMapIoSpace(). Here is the description of this API from Microsoft Developer Network website[5].

“The MmMapIoSpace routine maps the given physical address range to nonpaged system space.”

 

Microsoft crash dump (-d option)

 

The –d option generates a Mi­cro­soft crash dump file type. This dump file is a complete memory dump.

 

 

Type 0

 

The type 0 is a default when we are gene­rating a Microsoft crash dump file. Like BSOD fump files it contains the exact copy of memory range taken from

MmPhysicalMemoryBlock internal variable.

 

Type 1

 

When we set the type to 1, it forces win32dd to save the first physical page which is ignored by

MmPhysicalMemoryBlock as illustrated on the picture below.


 

win32dd.png


 

 

PowerDbg v5.1 – Using PowerShell to Control WinDbg

Roberto Alexis Farah, 25th February 2009
http://blogs.msdn.com/debuggingtoolbox/

 

 

Here I would like to introduce a minor version of PowerDbg 5.0[6] with a few new cmdlets. These new cmdlets are those that we use most of the time for .NET debugging.

 

POWERDBG FILES

 

WinDbg.PSM1  ß Contains cmdlets used to communicate with WinDbg.

 

 

Microsoft.PowerShell_Profile.PS1 ß Contains cmdlets that parse command output. Uses WinDbg.PSM1 under the hood.

 

INSTALLATION

 

WinDbg.PSM1

 

Goes to %\Windows\System32\WindowsPowerShell\v1.0\Modules\WinDbg

 

Note: PowerDbg assumes the folder c:\debuggers as the default installation folder. This is true for the default in­stallation of our private debugger version (Microsoft) but not for the public version. So, please, change this variable to reflect your installation:

 

param($cdbPath = "C:\debuggers\cdb.exe")

 

 

Microsoft.PowerShell_Profile.PS1

 

Goes to %\Documents\windowspowershell

 

In order to know the exact location, use this command from PowerShell:

 

$profile

 

REQUIREMENT

 

PowerShell v2.0

 

USAGE

 

First, make sure you can run scripts:

 

set-executionpolicy remotesigned

 

From the WinDbg window type:

 

.server tcp:port=10456,server=ServerName   ß ServerName is your server name.

 

The command above enables a port for communication with the WinDbg instance as a server. You can use other port numbers.

 

From the PowerShell window you must initialize the communication:

 

Import-module WinDbg  ß Importing our module WinDbg.PSM1

 

Connect-Windbg "tcp:Port=10456,Server=SERVER" ß Connects session to WinDbg instance.

 

Or:

 

Connect-Dbg "tcp:Port=10456,Server=SERVER" ß Connects session to WinDbg

 

Note: Don’t forget to load symbols and your extensions!

 

At this point you’re ready to use PowerDbg or PowerDbg scripts.

Example:

 

Analyze-PowerDbgThreads   ß Cmdlet.

 

.\PowerDbgScriptExceptions  ß Script.

 

NEW FOR POWERDBG v5.1

 

Load-PowerDbgSymbols <$symbolPath>

 

Load symbols.

Usage:  

Load-PowerDbgSymbols “SRV*c:\PUBLICSYMBOLS*http://msdl.microsoft.com/download/symbols"

 

Parse-PowerDbgASPXPAGES

 

Maps the output from the !ASPXPages command and saves it into the CSV file POWERDBG-PARSED.LOG

To convert the CSV file to a Hash Table use Convert-PowerDbgCSVToHashTable.

 

For this version we consider the fields:

Key: HttpContext

Value: Timeout+Completed+Running+ThreadId+ReturnCode+Verb+RequestPath+QueryString

 

Parse-PowerDbgCLRSTACK

 

Maps the output from the !clrstack command or ~* e !clrstack and saves it into the CSV file POWERDBG-PARSED.LOG

To convert the CSV file to a Hash Table use Convert-PowerDbgCSVToHashTable.

 

Attention! The key is the thread number and the value is the call stack separated by $global:g_frameDelimiter.

Commas "," are replaced for ";" to avoid confusing with the comma used by the CSV file.

If you use this cmdlet to parse the output from ~* e !clrstack the threads not running managed code are au­tomatically ignored.

 

Parse-PowerDbgTHREADS

 

Maps the output from the !threads command and saves it into the CSV file POWERDBG-PARSED.LOG

To convert the CSV file to a Hash Table use Convert-PowerDbgCSVToHashTable.

 

The following fields are extracted:

Thread Number                                                                                       - Key

ID+OSID+ThreadOBJ+State+GC+Context+Domain+Count+APT+Exception - Value

 


Parse-PowerDbgDSO

 

Maps the output from the !dso or ~* e !dso command and saves it into the CSV file POWERDBG-PARSED.LOG

To convert the CSV file to a Hash Table use Convert-PowerDbgCSVToHashTable.

 The Thread Number is the key and the stack is the value, like the way that Parse-PowerDbgK or Parse-PowerDbgCLRSTACK operate.

 

Attention! Commas are replaced by ";" and $global:g_FrameDelimiter is used to separate frames.

 

CMDLETS FROM POWERDBG

 

Send-PowerDbgCommand <$command>

 

This was the most complex cmdlet, but now it’s just a wrapper for Invoke-WinDbgCommand.

SendPowerDbgCommand sends commands to WinDbg.

 

Parse-PowerDbgDT [$useFieldNames]

 

Parses the output from the dt command and saves it into POWERDBG-PARSED.LOG using a CSV file format.

If $useFieldNames has a value, the cmdlet stores fields from struct/classes and values. Otherwise it stores offsets and values.

To convert the CSV file to a Hash Table use Convert-PowerDbgCSVToHashTable.

 

Convert-PowerDbgCSVToHashTable

 

Converts the output from the Parse-PowerDbg* cmdlets to a Hash Table.

 

Send-PowerDbgDML <$hyperLinkDML> <$commandDML>

 

Creates a DML command and sends it to WinDbg.

DML stands for Debug Markup Language. Using DML you can create hyperlinks that execute commands.

 

Parse-PowerDbgNAME2EE

 

Maps the output from the !name2ee and saves it into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVtoHashTable converts the output into a Hash Table.

 

 

Parse-PowerDbgDUMPMD

 

Maps the output from !dumpmd command and saves it into the CSV file POWERDBG-PARSED.LOG.

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Parse-PowerDbgDUMPMODULE

 

Maps the output from !DumpModule command and saves it into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Parse-PowerDbgDUMPLMI

 

Maps the output from !lmi command and saves it into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Has-PowerDbgCOMMANDSUCCEEDED

 

Returns $true if the last command succeeded or $false if not.

 

Send-PowerDbgComment

 

Sends a comment, a string in bold, to the WinDbg window.

 

Parse-PowerDbgVERTARGET

 

Maps the output from vertarget command, either the Kernel Time or the User Time.

The output is saved into the CSV file POWERDBG-PARSED.LOG.

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Parse-PowerDbgRUNAWAY

 

Maps the output of !runaway 1 or !runaway 2 and stores the results into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Attention! If you need to know the top threads consuming CPU time, use Convert-PowerDbgRUNAWAYtoArray. The items will be in the same exact order of the original command.

 

Convert-PowerDbgRUNAWAYtoArray

 

Returns an array of two dimensions corresponding to the output of !runaway 1 or !runaway 2.

 

Parse-PowerDbgK

 

Maps the output of k command and its variations like kv, kbn, kpn, etc.

The output is saved into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Attention! This cmdlet doesn’t work with kPn. It also replaces “,” with “;” to avoid conflict with the CSV deli­miter.

 

Parse-PowerDbgSymbolsFromK

 

Maps just the symbols from k command and its variants, saving the content into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Attention! This cmdlet doesn’t work with kPn. It also replaces “,” with “;” to avoid conflict with the CSV deli­miter.

 

Parse-PowerDbgLM1M

 

Maps just the output from lm1m and stores it into the CSV file POWERDBG-PARSED.LOG

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Classify-PowerDbgThreads

 

Returns an array where the index is the thread number and the value is one of these values:

 

0 UNKNOWN_SYMBOL

1 WAITING_FOR_CRITICAL_SECTION

2 DOING_IO

3 WAITING

4 GC_THREAD

5 WAIT_UNTIL_GC_COMPLETE

6 SUSPEND_FOR_GC

7 WAIT_FOR_FINALIZE

8 TRYING_MANAGED_LOCK

9 DATA_FROM_WINSOCK

 

It’s very easy to add more symbols and constants to get a more granular analysis. Look at the source code for de­tails.

 

Analyze-PowerDbgThreads

 

Analyzes and shows what each thread is doing and its cor­responding CPU time, sorted by User Mode time.

This cmdlet is very useful for scenarios like hangs, high CPU, and crashes.

 

Attention! This command requires thread information if debugging a dump file.

 

Parse-PowerDbgPRINTEXCEPTION

 

Maps the output from  !PrintException command and saves it into the CSV file POWERDBG-PARSED.LOG.

The following fields are considered while others are ignored:

 

Exception object:

Exception type;

Message:

InnerException:

HRESULT:

 

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Parse-PowerDbgDD-L1

 

Maps the output from dd <address> L1 or dd poi(<address>) L1 and saves the results into the CSV file POWERDBG-PARSED.LOG.

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

 

Parse-PowerDbgGCHANDLELEAKS

 

Maps the output from  !GCHandleLeaks command and saves it into the CSV file POWERDBG-PARSED.LOG.

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Parse-PowerDbgDUMPOBJ

 

Maps the output from !DumpObj command and saves it into the CSV file POWERDBG-PARSED.LOG.

The assembly path and file name are saved using the key name ‘Assembly:’.

If the object is invalid the ‘Name:’ field will have the string “Invalid Object.” You may want to check this string to make sure you’ve got valid data.

The keys are the fields or Method Table, and values are the corresponding value.

Convert-PowerDbgCSVToHashTable converts the output into a Hash Table.

 

Attention! This version maps the fields below “Fields:” using MethodTable as key and Value as value. The prob­lem with this approach is that the same MethodTable may appear more than once. If it happens, the last or most recent MethodTable and value will be considered.

Based on users’ feedback this approach may be changed in the near future.

 

POWERDBG SCRIPTS

 

PowerDbgScriptDumpDict.PS1

 

http://blogs.msdn.com/debuggingtoolbox/archive/2008/10/29/powershell-script-extracting-all-key-value-pairs-from-a-dictionary-object.aspx

 

Extracts the key/value pair from a Dictionary.

 

PowerDbgScriptExceptions.PS1

 

http://blogs.msdn.com/debuggingtoolbox/archive/2008/01/15/powershell-script-displaying-inner-and-hidden-exceptions.aspx

 

Displays the call stacks that have inner or hidden exceptions.

 

PowerDbgScriptGCHandleLeaksChart.PS1

 

http://blogs.msdn.com/debuggingtoolbox/archive/2008/08/22/powershell-script-chart-and-statistics-from-top-20-objects-leaking.aspx

 

It displays statistics and a chart from the top 20 objects leaking.

 

PowerDbgScriptHighCPU.PS1

 

http://blogs.msdn.com/debuggingtoolbox/archive/2007/12/17/powershell-script-isolating-the-threads-consuming-high-cpu.aspx

 

It displays all threads consuming high CPU using a specific time as a threshold.

 

PowerDbgScriptSaveModule.PS1

 

http://blogs.msdn.com/debuggingtoolbox/archive/2007/09/06/powershell-script-saving-a-module-from-a-net-method-call.aspx

 

It saves all modules that have a specific method. You pro­vide the method name, and it gives you the corresponding modules.

 

Download PowerDbg

 

http://www.codeplex.com/powerdbg

 

Example: [PowerShell Script] Statistics from .NET Applications

 

This script is the reason why PowerDbg v5.1 was created. I had to create some new cmdlets in order to create this script. By the way, thanks to my teammate Aaron Barth that gave the idea for this script!

This script collects information from all threads running managed code and gives the user statistics by threads like:

 

-      CLR stack.

-      Managed objects from the stack.

-      ASP.NET page.

-      What the thread is doing.

-      Exceptions by threads.

-      Threads running ASP.NET pages.

 

Contrary to what you may think this script is very simple and shows you how to use PowerDbg. It’s very easy to customize it or improve it. For example, you may want to display the ASP.NET pages by threads or queries/stored procedures by threads.

 

Screenshots:

 

ScriptASPX_1.jpg


 

ScriptASPX_2.jpg


 

ScriptASPX_3.jpg


 

ScriptASPX_4.jpg


 

Source code for PowerShellScriptASPXStatistics.ps1:

###########################################################################

# Script:      PowerDbgScriptASPXStatistics

#

# Parameters:  None.

#

# Purpose:     Shows statistics from threads running ASP.NET pages.

#

#              Attention! This script was not tested on Win64.

#

# Changes History:

#

# Roberto Alexis Farah

# All my functions are provided "AS IS" with no warranties, and confer no rights.

###########################################################################set-psdebug -strict

$ErrorActionPreference = "stop"

trap {"Error message: $_"}

 

write-Host "Scanning all threads and extracting the CLR stack..." -foreground Green -background Black

 

# First, let's scan all threads and identify those running managed code.

Send-PowerDbgCommand "~* e !clrstack"

Parse-PowerDbgCLRSTACK

 

# Get all the stacks running managed code.

$clrStack = Convert-PowerDbgCSVToHashTable

 

write-Host "Done!" -foreground Green -background Black

 

# Sorts the keys by Thread Number and save them into an array.

$arrayOfThreads = $clrStack.keys | Sort-Object {[int] $_}

 

# Let's consider the situation where the dump has no thread running managed code.

if($arrayOfThreads.Count -eq 0)

{

    write-Host "There are not threads running managed code!" -foreground Red -background Black

    return

}

 

write-Host "Scanning all threads and extracting the managed objects..." -foreground Green -background Black

 

Send-PowerDbgCommand "~* e !dso"

Parse-PowerDbgDSO

 

$dso = Convert-PowerDbgCSVToHashTable

 

write-Host "Done!" -foreground Green -background Black

 

write-Host "Collecting information about each thread..." -foreground Green -background Black

 

Send-PowerDbgCommand "!Threads"

Parse-PowerDbgTHREADS

 

$threads = Convert-PowerDbgCSVToHashTable

 

write-Host "Done!" -foreground Green -background Black

 

write-Host "Collecting information from threads running ASP.NET..." -foreground Green -background Black

 

Send-PowerDbgCommand "!ASPXPages"

Parse-PowerDbgASPXPAGES

 

$aspxPages = Convert-PowerDbgCSVToHashTable

 

write-Host "Done!" -foreground Green -background Black

 

write-Host "Scanning all threads and preparing statistics..." -foreground Green -background Black

 

# Scans all threads running managed code.

for($i = 0; $i -lt $arrayOfThreads.Length; $i++)

{

    # Make sure the content is not null.

    if($arrayOfThreads[$i] -eq "")

    {

        continue# Invalid, get next element.

    }

   

    write-Progress -activity "Thread Statistics" -status "Thread number $arrayOfThreads[$i]" -percentComplete ($i / $arrayOfThreads.Count * 100)

   

    write-Host "==============================================================" -foreground Green -background Black

   

    write-Host "`nThread number: " -foreground Green -background Black -nonewline

    write-Host $arrayOfThreads[$i] -foreground Red -background Black

   

    write-Host "`nCLR stack:`n" -foreground Green -background Black

   

    [string] $temp = $clrstack[$arrayOfThreads[$i]]

    $temp = $temp.Replace($global:g_frameDelimiter, "`n")

    $temp = $temp.Replace(";", ",")

   

    write-Host $temp -foreground Red -background Black

   

    write-Host "`nManaged objects from the stack:`n" -foreground Green -background Black

   

    $temp = $dso[$arrayOfThreads[$i]]

   

    $temp = $temp.Replace($global:g_frameDelimiter, "`n")

    $temp = $temp.Replace(";", ",")

   

    write-Host $temp -foreground Red -background Black 

 

    write-Host "`nThread Number   ID OSID ThreadOBJ   State   GC       Context           Domain  Count APT Exception`n" -foreground Green -background Black

 

    write-Host "  " $arrayOfThreads[$i] "         " $threads[$arrayOfThreads[$i]] -foreground Red -background Black

   

    $threadNum = $arrayOfThreads[$i]

   

    # Change context to the current thread being analyzed.

    Send-PowerDbgCommand "~ $threadNum s"

   

    # Get exception.

    Send-PowerDbgCommand "!PrintException"

    Parse-PowerDbgPRINTEXCEPTION

   

    $exception = $null

    $exception = Convert-PowerDbgCSVToHashTable

   

    # Makes sure there is an exception coming from that thread.

    if($exception["Message:"] -ne $null)

    {

        write-Host "`nException object:" -foreground Green -background Black    

        write-Host $exception["Exception object:"] -foreground Red -background Black

        write-Host "Exception type:" -foreground Green -background Black           

        write-Host $exception["Exception type:"] -foreground Red -background Black       

        write-Host "Message:" -foreground Green -background Black           

        write-Host $exception["Message:"] -foreground Red -background Black       

        write-Host "Inner Exception:" -foreground Green -background Black           

        write-Host $exception["InnerException:"] -foreground Red -background Black

        write-Host "HRESULT:" -foreground Green -background Black           

        write-Host $exception["HResult:"] -foreground Red -background Black       

    }

 

    # User must press any key to continue after 5 threads were displayed.

    if((($i + 1) % 5) -eq 0)

    {

        write-Host "`n####### Press any key to see 5 more threads... #######"

        $keyboard = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

    }

}

 

write-Host "Done!" -foreground Green -background Black

 

write-Host "`nASP.NET Pages:`n" -foreground Green -background Black

 

write-Host "HttpContext      Timeout Completed Running ThreadId ReturnCode Verb RequestPath QueryString" -foreground Green -background Black

 

foreach($item in $aspxPages.keys)

{

    write-Host $item  "  " $aspxPages[$item] -foreground Red -background Black

}

 



[2] Microsoft Developer Network (2005) \Device\PhysicalMemory Object. http://technet.microsoft.com/en-us/library/cc787565.aspx

[3] Intel 64 and IA-32 Architectures Software Developer’s Manual.

[4] Microsoft Knowledge Base (2008) Complete memory dumps are not available on computers that have 2 or more gigabytes of RAM, http://support.microsoft.com/kb/274598/

[5] Microsoft Developer Network MmMapIoSpace http://msdn.microsoft.com/en-us/library/ms801998.aspx

[6] PowerDbg is a PowerShell tool that automates debugging sessions. Using PowerDbg we can create PowerShell scripts that work like extensions. Here is the link to download the tool: http://blogs.msdn.com/debuggingtoolbox/archive/tags/PowerDbg+Library/default.aspx