[Windbg Script] Saving a Module - Extracting Base Address and Image Name from a method call

After creating this script, I have used it in almost every case that requires decompilation, and I guess you are going to use it, too.

This script gives you the base address and module name, so you can use !SaveModule from SOS to save the module.

Ok… maybe you are wondering what is so cool about this script.

Let me explain. Using the !SaveModule or !SaveModules commands from SOS, you can save the modules and decompile them using .NET Reflector. However, if you use !SaveModule, you need to get the base address and image name before using this command.

It means you need to dig in the dump file to get this information. If you read John’s book or Tess’s blog you know how to do that.

The goal of this script is to:

1- Save time getting the module address and image file name.

2- Enable you to use just the method name from !clrstack, for example, to get the module information. So, if for some reason you want to get the module or all modules from a method call, you just need to run the SAVE_MODULE.TXT script, following this example:

0x0012eebc 0x03df0669 [DEFAULT] [hasThis] Void SomeComponent.fACAtenInterl.DtGRes_DoubleClick(Object,Class System.EventArgs)

$$a<myscripts\save_module.txt SomeComponent.fACAtenInterl.DtGRes_DoubleClick

And you will get the Base Address and Image Name for all modules using this method. Then you use !SaveModule to save the file.

I opted for using the method name as argument, but I could use the EIP address (0x03df0669 above). It would be faster than it is today, but less flexible in my opinion because sometimes you may want to get the module information using a method call that’s not in the call stack.

As you can see I use the .shell command. Using this command I cannot use spaces to indent the script. In other words, I cannot use line breaks. It means you will get two very long lines when copying and pasting the source code below to a text file. 

Note: If you use Word it'll keep the same spacing I'm using below. Using Notepad it adds a blank line for each source code line.

Another point to keep in mind: you need to run it using $$a< not $$>a< because the latest form is used when the lines are separated by line breaks as most of my scripts. For more, check the Windbg help file.

Screenshots:

Calling the script using one method from the call stack above.

Using !SaveModule. The arguments are the output from the script execution:

 

 

Now, using a method call that doesn’t exist:

 

 

Below is the Base Address and Image Name, so you can learn how to locate this information from the script output:

 

 

Source code for SAVE_MODULE.TXT:

$$

$$ =============================================================================

$$ Get Base Address and Image Name from a managed module using a method as argument.

$$ This information can be used as argument for !SaveModule.

$$

$$ Compatibility: Win32.

$$

$$ Usage: $$a<myscripts\SAVE_MODULE.TXT methodName

$$

$$ Example: $$a<myscripts\SAVE_MODULE.TXT System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.MapPathSlowUsingIISCore

$$

$$ Note: Don't use the method arguments, just the method name:

$$ System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.MapPathSlowUsingIISCore(String) <-- Wrong.

$$ System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.MapPathSlowUsingIISCore <-- Right.

$$

$$ Attention: Do NOT use $$>a<, use $$a< to run the script.

$$

$$ Requirements: Public symbols.

$$

$$ Roberto Alexis Farah

$$ Blog: https://blogs.msdn.com/debuggingtoolbox/

$$

$$ All my scripts are provided "AS IS" with no warranties, and confer no rights.

$$

$$ Below I force the temporary files to be overwritten with invalid content.

$$ If I don't do that and you use an argument that doesn't exist in any module, then the last one will be displayed.

$$ It happens because the script will read the temp files and they will have valid content from the last good execution.

$$

$$ =============================================================================

$$

.block{.printf /D "\n\n<b>Invalidating content from temporary files... Ignore the error messages...</b>\n\n"}

.writemem TEMP.TXT 0x1

.writemem TEMP2.TXT 0x1

.writemem TEMP3.TXT 0x1

.block{.if(0 = ${/d:$arg1}){.printf /D "<b>\n\n\nYou need to provide a method name from !clrstack output. Do not use the arguments type, just the method name.\n\n</b>"}.else{.printf /D "\nScanning all modules and looking for <b>${$arg1}...</b>\n\n";.block{.shell -o TEMP.TXT -ci "!Name2EE *!${$arg1}" FIND "MethodDesc:"};.printf "\nExtracting Module from MethodDescriptor...\n";.foreach /f /pS 1 /ps 1 (obj "TEMP.TXT"){.shell -o TEMP2.TXT -ci "!dumpmd ${obj}" FIND "Module:"};.printf "\nExtracting Module address...\n";.foreach /f /pS 1 /ps 1 (obj "TEMP2.TXT"){.shell -o TEMP3.TXT -ci "!DumpModule ${obj}" FIND "MetaData"};.printf /D "\n<b>Base Address and Image Name:</b>\n\n";.foreach /f /pS 3 /ps 2 (obj "TEMP3.TXT"){.if(0 != $sicmp("${obj}", "MetaData")){.block{.shell -i - -ci "!lmi ${obj}" FIND "Base Address:"};.block{.shell -i - -ci "!lmi ${obj}" FIND "Image Name:"}}};.printf /D "\n<b>Use</b> !sos.SaveModule BaseAddress path\\IMAGENAME<b> to save the Image file.\nThen you can use .NET Reflector to decompile it.\n\n</b>"}}

Read me.