Поделиться через


[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.

Comments

  • Anonymous
    July 12, 2007
    Thanks - the script is pretty useful, especially the ability to save the module info even for methods which are not in a call stack – great help when analyzing dumps which do not display managed stack via !clrstack command. Thanks again.

  • Anonymous
    July 13, 2007
    Thanks AlexeyK! I'm happy to know it's being useful for more people! :)

  • Anonymous
    December 24, 2007
    This is my first script using the PowerDbg functions. It’s a good example of how to use PowerDbg to build

  • Anonymous
    June 07, 2008
    Hi Roberto, Do you speak spanish? he he, I have a question, I need to extract an XML which is causing some errors in the software that I am testing, it is possible? how can I perform this? Thank you for your help. Regards Rene.

  • Anonymous
    June 09, 2008
    Hi Rene, My primarily language is Portuguese, that is similar to Spanish, so I can understand Spanish and speak a mix of Portuguese and Spanish. :) I'll talk more about me in a future article. I guess what you are trying to do is to get the XML from the .NET object using it, right? If yes, this is the general idea: you should use !dumpheap -type xml if you don't know the XML object to get all objects using XML. If you know the object you can see the stacks and use the !dso command when you find a thread using the XML object. If the application is raising a specific exception when processing the XML and assuming it is a first-chance exception (the application is handling the exception) you can use Debug Diagnostics in Crash mode and configure it to create a full user dump when the application throws the specific CLR exception. If it doesn't make sense I'll need more details. :)