########################################################################################################
# Script: PowerDbgScriptExceptions
#
# Parameters: None.
#
# Purpose: Show all threads that handled exceptions and the corresponding call stack for each inner exception.
# This script is for 32 bits.
#
# Usage: Before running the script:
# a) Open your workspace to load the symbols.
# b) .wtitle PowerDbg
# c) .reload /f
#
# Attention! If you don't load the symbols before running the script you may face timing issues.
#
# Changes History: 03/29/08 - Removed all start-sleep from the code. PowerDbg version 3.0 and later
# doesn't require it anymore.
#
# 01/23/09 - This script was changed to be compatible with PowerDbg v5.0
#
# 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 "Finding hidden exceptions..." -foreground Green -background Black
# Just cleans WinDbg window...
Send-PowerDbgCommand ".cls"
# The command below search all threads for the exception context.
# It shows the thread id and all exception contexts found in each call stack. Scanning all the call stack.
Send-PowerDbgCommand "~* e r @`$tid;.echo CONTEXT;s -[1]d poi(@`$teb+0x8) poi(@`$teb+0x4) 0001003f ; .ECHO END_CONTEXT"
write-Host "Done!" -foreground Green -background Black
# Extract output removing commands.
$builder = New-Object System.Text.StringBuilder
$key = ""
$value = ""
$oldTID = ""
$output = @{}
$hasExceptions = $false
$stringReader = [System.IO.StringReader] $global:g_commandOutput
# Scan the symbols for each thread, line by line.
while(($line = $stringReader.ReadLine()) -ne $null)
{
# Gets the thread ID. We'll need this information later.
if($line -match "(^\`$tid=(?<key>(\w+)))")
{
# Save the key that in this case is the thread ID.
$key = $matches["key"]
}
elseif($line -match "(^0x(?<value>(\w+)))") # Gets thread context.
{
# We can have more than one different exception context for each thread.
# For this case the value is the exception context.
$value = $matches["value"]
# If this flag is true we found exceptions.
$hasExceptions = $true
# Check if we changed threads.
if($oldTID -ne $key)
{
$oldTID = $key
write-Host "`n===================================================================================" -foreground Green -background Black
write-Host "Thread ID = $oldTID" -foreground Green -background Black
write-Host "`nCurrent call stack:`n" -foreground Green -background Black
# Changes the thread context.
Send-PowerDbgCommand "~~[$oldTID]s"
# To get the registers we can just get the content from the output file.
write-Host $global:g_commandOutput -foreground Green -background Black
# Gets the call stack. This is the original call stack. We didn't change the exception context.
Send-PowerDbgCommand "kbn 1000"
# Parses the command output.
Parse-PowerDbgK
# Gets the output.
$output = Convert-PowerDbgCSVtoHashTable
# Displays the call stack replacing the g_frameDelimiter by new line.
write-Host $output["#"].Replace($global:g_frameDelimiter, "`n") -foreground Green -background Black
}
# Displays call stack from the exception context.
write-Host "Call stack from the exception context ["$value" ] :" -foreground Green -background Black
write-Host ""
# Changing exception context...
Send-PowerDbgCommand ".cxr $value"
# To get the registers we can just get the content from the output file.
write-Host $global:g_commandOutput -foreground Green -background Black
write-Host "If all registers are empty and there is no call stack it may indicate a false positive.`n" -foreground Red -background Black
# Gets the call stack. This is the original call stack. We didn't change the exception context.
Send-PowerDbgCommand "kbn 1000"
# Parses the command output.
Parse-PowerDbgK
# Gets the output.
$output = Convert-PowerDbgCSVtoHashTable
# Displays the call stack replacing the g_frameDelimiter by new line.
write-Host $output["#"].Replace($global:g_frameDelimiter, "`n") -foreground Green -background Black
}
}
# Notifies user if there is no exceptions.
if($hasExceptions -eq $false)
{
write-Host "`nThis process has no threads handling exceptions or threads with hidden exceptions." -foreground Green -background Black
}
write-Host "`nFor more technical details see the WinDbg window." -foreground Green -background Black
# Notifies user the script finished the execution.
Send-PowerDbgComment "PowerDbgScriptExceptions was executed. See the PowerShell window for more information."