[PowerShell Script] Saving a Module from a .NET Method Call

This is my first script using the PowerDbg functions. It’s a good example of how to use PowerDbg to build your own scripts.

PowerDbgScriptSaveModule.ps1 is the PowerShell version of my Windbg script Save_Module.txt

Actually it does more than the previous version: it automatically saves the module that uses a specific method call. Then you just need to use .NET Reflector to decompile the code.

You can see this version is more powerful, easier to change, easier to improve, and easier to understand. Besides it has error handling!

When using PowerDbg scripts you can see the output from the PowerShell window; therefore, it’s easy to understand what the script is doing and the final result.

Remember, when using PowerDbg scripts, the WinDbg window has the row command processing and the PowerShell window has the output from the script. You should focus on the PowerShell window to get information that you may use in either reports or in e-mails to your customers. While other scripts may have copious amounts of information shown in the PowerShell window, this script doesn’t. It simply saves a module, so that’s why I chose it to be our first PowerDbg script.

Make sure to use the latest PowerDbg version.

 

The source code is actually indented. Copy and paste it to Word and save it as text file, then you can see the code using Notepad.

These are the screenshots:

 

 

.\PowerDbgScriptSaveModule "System.Threading.ExecutionContext.runTryCode" "c:\dumps"

 

 

 

 

 

 

 

This is the source code for PowerDbgScriptSaveModule.ps1:

 

 

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

# Script: PowerDbgScriptSaveModule

#

# Parameters: [string] <$methodName>

# Name of the method from call stack.

#

# [string] <$directory>

# Directory where the module will be saved. Make sure this is a valid directory.

# Example: "c:\module"

#

# Purpose: Saves the module that has the method call provided as parameter into the directory provided as second

# parameter. After that, you can use a tool like .NET Reflector to decompile the code.

# This script makes the assumption that SOS.DLL extension is loaded.

#

# Changes History:

#

# Roberto Alexis Farah

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

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

param(

[string] $methodName = $(throw "Error! You must provide the method name."),

[string] $directory = $(throw "Error! You must provide the directory where the method will be saved.")

)

set-psdebug -strict

$ErrorActionPreference = "stop"

trap {"Error message: $_"}

write-Host "Extracting MethodDesc..." -foreground Green -background Black

# If you want you can create code to load SOS.DLL extension if necessary.

# Here I'm assuming this extension was already loaded.

Send-PowerDbgCommand("!name2ee *!$methodName")

# Extracts output from previous command.

Parse-PowerDbgNAME2EE

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

# Convert CSV file to Hash Table.

$output = @{}

$output = Convert-PowerDbgCSVtoHashTable

write-Host "MethodDesc = " $output["MethodDesc:"] -foreground Green -background Black

# If something goes wrong, stops execution and notifies user.

if($null -eq $output["MethodDesc:"])

{

throw "Couldn't retrieve MethodDesc!"

}

write-Host "Extracting Module..." -foreground Green -background Black

# Calls !dumpmd using the specific value we want.

Send-PowerDbgCommand("!dumpmd " + $output["MethodDesc:"])

# Extracts output from previous command.

Parse-PowerDbgDUMPMD

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

# Convert CSV file to Hash Table.

$output = @{}

$output = Convert-PowerDbgCSVtoHashTable

write-Host "Module = " $output["Module:"] -foreground Green -background Black

# If something goes wrong, stops execution and notifies user.

if($null -eq $output["Module:"])

{

throw "Couldn't retrieve Module!"

}

write-Host "Extracting Metadata start address..." -foreground Green -background Black

# Calls !dumpmodule using the module address.

Send-PowerDbgCommand("!DumpModule " + $output["Module:"])

# Now we need to get the base address...

# To do that we need a specific field from the previous command.

# The fields from SOS.DLL have differences between .NET Framework 2.0 and 1.1

Parse-PowerDbgDUMPMODULE

# Convert CSV file to Hash Table.

$output = @{}

$output = Convert-PowerDbgCSVtoHashTable

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

# We need to verify if the field is from SOS 2.0 or SOS 1.1

if($null -eq $output["MetaData starts at"])

{

write-Host "Metadata start address = " $output["MetaData start address:"] -foreground Green -background Black

}

else

{

write-Host "Metadata start address = " $output["MetaData starts at"] -foreground Green -background Black

}

# If something goes wrong, stops execution and notifies user.

if(($null -eq $output["MetaData starts at"]) -and ($null -eq $output["MetaData start address:"]) )

{

throw "Couldn't retrieve MetaData start address!"

}

# We need to verify if the field is from SOS 2.0 or SOS 1.1

if($null -eq $output["MetaData start address:"])

{

Send-PowerDbgCommand("!lmi " + $output["MetaData starts at"])

}

else

{

Send-PowerDbgCommand("!lmi " + $output["MetaData start address:"])

}

write-Host "Extracting base address..." -foreground Green -background Black

# Yet again we need the output from the previous command.

Parse-PowerDbgLMI

# Convert CSV file to Hash Table.

$output = @{}

$output = Convert-PowerDbgCSVtoHashTable

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

write-Host "Module to be saved = " $output["Image Name:"] -foreground Green -background Black

# If something goes wrong, stops execution and notifies user.

if($null -eq $output["Image Name:"])

{

throw "Couldn't retrieve Image Name!"

}

write-Host "Saving module " $output["Image Name:"] " into directory " $directory -foreground Green -background Black

# Finally we save the module now that we have the name of the module and its base address.

Send-PowerDbgCommand("!savemodule " + $output["Base Address:"] + " " + $directory + "\" + $output["Image Name:"])

$hasCommandSucceeded = Has-PowerDbgCommandSucceeded

if($false -eq $hasCommandSucceeded)

{

throw "Couldn't save module into the provided path!"

}

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

# Notifies user the script finished the execution.

Send-PowerDbgComment "PowerDbgScriptSaveModule was executed. See the PowerShell window for more information."