[Windbg Script] Get Portable Executable Headers


There are several tools you can use to read the image headers, like Dumpbin.exe and Link.exe, for instance. You can, however, also use Windbg for doing that! In other words, during your debugging session you can see the header from an image file without executing any other tool except this script.


 


This is a very simple script that lists all loaded modules and gives you two options:


      Visualize a summarized view of the header for a specific image.


      Visualize a detailed view of the header for a specific image.


 


Actually, there is one more option: run the script and provide the module as an argument. J


 


Hot trick! I just learned how to use arguments when running scripts! It’s very cool! (and undocumented L) I hope the next Windbg version includes it, anyway, you can learn it from this script that I just changed to accept parameters.


 


Basically you need to run the script as:


$$>a<scriptname.txt arg1 arg2 arg3 …   ß Notice the >a<


 


Then you get the arguments from the script source code using alias, like:


${$arg1}      ß Alias, not pseudo registers.


 


It starts in $arg1 until $arg<n>.


 


In my script I came up with a test to check if the script was called using arguments or not. You may want to reuse this idea.


 


If you get a syntax error, just run it once more and it should run. This is a problem related to alias. I already mentioned something about it here.


 


These are some screenshots:


 


 


 


 


 


 


 


Now using a module name when calling the script:


 



 


Source code for GET_HEADERS.TXT:


 


$$


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


$$ Get headers from images.


$$


$$ Compatibility: Win32.


$$


$$ Usage: $$>< to run the program without arguments.


$$        $$>a<scriptfile dllname to run the program using arguments.


$$


$$ Example: $$>a<myscripts\get_headers.txt kernel32


$$


$$ If necessary change the filename below to include your path and filename.


$$ Default file name and path should be changed below if necessary.


$$


$$ Roberto Alexis Farah


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


$$


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


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


$$


 


.block


{


    as ${/v:ScriptName} MYSCRIPTS\\GET_HEADERS.TXT


}


r @$t0 = 0


.if(${/d:$arg1})


{


    .printf /D “\nYou selected the module: <b>${$arg1}</b>\n\n”


    !dh ${$arg1} -a


}


.else


{


    .printf /D “\n\n<b>Select option below for loaded modules:</b>\n\n”


    .foreach(obj {lm1mo})


    { 


        .block


        {


            .printf “${obj}\t <– “


            .printf /D “<link cmd=\”.echo ${obj};!lmi ${obj};ad ${/v:ScriptName};$$><${ScriptName}\”>Summarized</link> or “


            .printf /D “<link cmd=\”.echo ${obj};!dh ${obj} -a;ad ${/v:ScriptName} *;$$><${ScriptName}\”>Detailed</link>\n”       


        }


    }


    .printf /D “<b>\nAfter selection scroll up the screen to see the information.</b>\n”


}


 


Read me.

Comments (10)

  1. Alessandro says:

    Amazing Roberto ,

    I have already posted on the LATAM blog , but you deserve one in English as well.

    I have tested and it is officially in my toolbox.

    I hope you never get tired of this

  2. Hi Alessandro,

    I really appreciate your comments! πŸ™‚

    If you have some suggestion for a new script, let me know. I’m really interested!

    If you find a bug let me know, too. It’s not always possible to test them under different Windows or .NET Framework versions.

    Thanks

  3. Neil Brench says:

    I actually just learnt about passing arguments to scripts from your recent e-mail exchange πŸ™‚

    There’s a slightly easier way to check whether an argument was passed:

    .if (${/d:$arg1})

    {

       .echo You supplied an argument: ${$arg1}

    }

    .else

    {

       .echo No argument was supplied

    }

    The help for ${} has more information.

  4. Hi Neil,

    Thanks for the information!

     Lesson learned: Next time I have problems with new commands I’ll read the help before trying my own solution πŸ™‚

     I’m going to change the script source code.

    Roberto

  5. Kumar says:

    Hi Roberto

    Just want to confirm if the script works by having ad /q *(deleting all aliases) as the first statement because the script works for me only when I removed ad /q * from the script or am I missing anything here?

  6. This is interesting… have you tried to load the script twice in a row. You should get an error when loading it for the second time (it’s expected!), but no error during the third time. For some reason this is the behavior with all of my scripts that use alias. I’m not sure if the behavior is the same with newer WinDbg versions, but based on your comment I guess so. It seems your issue is related to this. For instance: you load the script the first time. It works. You remove the ad /q* from the script and reload it. It should work fine since it’s going to reuse the same alias. However, if another script had defined the same alias for a different content, the script is going to fail.

    Let me know the results of your tests.

  7. Kumar says:

    I’m really sorry for the delayed response

    The script works as expected now : I mean the script fails for the second time and works for the third time

    I can’t wait for the debugging team to fix this in windbg

    Thanks a lot

  8. Hi, there!

    Based on the code here, I created a bit safer script.

    .catch

    {

        r $t0  = ${/d:$SafetyCheck}

        .block

        {

           .if (0 == @$t0)

           {

               .reload /f

               as $SafetyCheck "Written by Takashi Toyota"

           }

           .else

           {

               al

               ad /q *

           }

        }

        .if (${/d:$arg1})

        {

           .printf /D "nYou selected the module: <b>${$arg1}</b>nn"

           !dh -a ${$arg1}

       }

       .else

       {

           .printf /D "nn<b>Select option below for loaded modules:</b>nn"

           .foreach(obj {lm1mo})

           {

               .block

               {

                  .printf "${obj}t <– "

                  .printf /D "<link cmd=".echo ${obj}; !lmi ${obj}">Summarized</link> or "

                  .printf /D "<link cmd=".echo ${obj}; !dh -a ${obj}">Detailed</link>n"      

               }

           }

        }

    }

    I welcome any comment! Enjoy it, please.

  9. Hi Takashi,

    I haven’t tested it yet, but I liked your style. Based on your style I deduce you’re also a C/C++ programmer:

    .if(0 == #$t0)

    Constant on left side to have a syntax error if you try to assign a value to the constant by mistake, for example, forgetting one of the = signs. I like it. I definitely like it. πŸ™‚

  10. It has been a long time since my last post, but I’m back on the blog. The article for today is about