Visual Basic System Services
MapDebugInformation: Obtain Debugging Info for an Image
     
Posted:   Saturday January 15, 2000
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   Requires Imagehlp.dll
Author:   Dev Ashish, VBnet - Randy Birch
     
 Prerequisites
Imagehlp.dll. Available as a redistributable for Windows 95.

Those who've used Microsoft's Dependency Walker (depends.exe) are familiar with the listing of exported functions / methods from DLL's. This code, originally authored by Dev Ashish for use under MS Access, has been graciously provided to VBnet by Dev. Using the MapDebugInformation API this code allows you to select any DLL (or any other file for that matter) in order to see the names of any methods that might be exported by the file. Note that not all files export methods.  And in Windows parlance an executable file is an 'image' ... and an executable file can be either an exe or a dll

The MSDN offers the following explanation of imagehlp's MapDebugInformation API in a 1997 Under the Hood article by Matt Pietrek. Slightly paraphrased here to make sense taken out of context (from the entire article), it covers significantly more details than this demo shows, is included for its completeness (and to spark ideas).

MapDebugInformation maps the executable file into memory, then figures out what the best type of symbol information is as well as some basic information about that symbol table. 'Best' means best - it turns out that an executable can be built with more than one type of debug information. For example, an executable can have both CodeView (PDB) information and a COFF symbol table. IMAGEHLP knows how to read both formats, as well as a few others, and knows which one is optimal for your executable. Once finished with the file, MapDebugInformation needs to be cleaned up by calling UnmapDebugInformation.

Because in C the symbols for an executable may be in a file other than the executable itself (a debug file), the MapDebugInformation API can take a parameter specifying the symbol search path, if such a file was available.

Besides mapping and loading the executable and its symbols (if present), the MapDebugInformation API returns a pointer to an IMAGE_DEBUG_INFORMATION structure. This structure contains many more fields than a LOADED_IMAGE structure (used with MapandLood), although nearly every field in the LOADED_IMAGE structure can be found in the IMAGE_DEBUG_INFORMATION structure. For example, the MappedBase field contains the address where the executable was mapped, and is the same as the Mapped­Address field in a LOADED_IMAGE structure. Similarly, the Sections field is a pointer to the executable’s section table, and so forth.

More useful information found in the IMAGE_DEBUG_INFORMATION structure includes the preferred load address (the ImageBase field), and the size of the executable in memory (the SizeOfImage field). There are also pointers to the table of names for the exported functions, as well as the executable’s time/date stamp DWORD.

The meaning of some fields in the IMAGE_DEBUG_INFORMATION structure isn’t so obvious, like the pointers to Function and FPO tables. The Function table is data used by the structured exception handling code on the Alpha and MIPS platforms (it’s not encountered with Intel-based executables). FPO information is seen only on the Intel platform; it helps debuggers walk the call stack in the absence of standard EBP register stack frames.

Finally, the IMAGE_DEBUG_INFORMATION has a variety of fields that indicate if CodeView and COFF information are present, and if so, where. There’s even a pointer to the debug directory. This is the data structure in the PE file that tells you what types of debug information are present and where. The MapDebugInformation API does a good job of extracting this information and presenting it in the IMAGE_DEBUG_INFORMATION structure. Still, if you’re so inclined, you can go straight to the same raw data that IMAGEHLP uses to generate the IMAGE_DEBUG_INFORMATION structure. Remember though, the whole advantage of using IMAGEHLP is to avoid such low-level grunginess.

 BAS Module Code
None.

 Form Code
To a form, add a command button, label, listbox and common dialog control. Use the default names and add the following code:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Const GENERIC_READ = &H80000000
Private Const GENERIC_WRITE = &H40000000
Private Const INVALID_HANDLE_VALUE = -1
Private Const OPEN_EXISTING = 3
Private Const FILE_SHARE_READ = &H1
Private Const FILE_SHARE_WRITE = &H2
Private Const FILE_ATTRIBUTE_NORMAL = &H80
Private Const MAX_PATH As Long = 260

Private Type IMAGE_DEBUG_INFORMATION
   List As Long
   Size As Long
   MappedBase As Long
   Machine As Long
   Characteristics As Long
   CheckSum As Long
   ImageBase As Long
   SizeOfImage As Long
   NumberOfSections As Long
   Sections As Long                       'pointer to a IMAGE_SECTION_HEADER type
   ExportedNamesSize As Long
   ExportedNames As Long
   NumberOfFunctionTableEntries As Long
   FunctionTableEntries As Long           'pointer to a IMAGE_FUNCTION_ENTRY type
   LowestFunctionStartingAddress As Long
   HighestFunctionEndingAddress As Long
   NumberOfFpoTableEntries As Long
   FpoTableEntries As Long
   SizeOfCoffSymbols As Long
   CoffSymbols As Long                    'pointer to the COFF symbol table
   SizeOfCodeViewSymbols As Long
   CodeViewSymbols As Long
   ImageFilePath As Long
   ImageFileName As Long
   DebugFilePath As Long
   TimeDateStamp As Long
   RomImage As Long
   DebugDirectory As Long                 'pointer to a IMAGE_DEBUG_DIRECTORY type
   NumberOfDebugDirectories As Long
   OriginalFunctionTableBaseAddress As Long
   Reserved0 As Long
   Reserved1 As Long
   Reserved2 As Long
End Type

Private Declare Function MapDebugInformation Lib "ImageHlp.dll" _
  (ByVal FileHandle As Long, _
   ByVal FileName As String, _
   ByVal SymbolPath As String, _
   ByVal ImageBase As Long) _
   As Long

Private Declare Function UnmapDebugInformation Lib "imagehlp.dll" _
  (ByVal DebugInfo As Long) As Long

Private Declare Function CreateFile Lib "kernel32" _
   Alias "CreateFileA" _
  (ByVal lpFileName As String, _
   ByVal dwDesiredAccess As Long, _
   ByVal dwShareMode As Long, _
   ByVal lpSecurityAttributes As Long, _
   ByVal dwCreationDisposition As Long, _
   ByVal dwFlagsAndAttributes As Long, _
   ByVal hTemplateFile As Long) _
   As Long

Private Declare Function CloseHandle Lib "kernel32" _
    (ByVal hObject As Long) As Long

Private Declare Sub CopyMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
  (Destination As Any, _
   Source As Any, _
   ByVal Length As Long)


Private Function GetFileImageInformation(sDLLName As String) As String

   On Local Error GoTo fileExport_error
   
   Dim IMGDBG As IMAGE_DEBUG_INFORMATION
   Dim hFile As Long
   Dim ptrDbg As Long
   Dim strOut As String
   Const ERR_GENERIC = vbObjectError + 5555

  'obtain a handle to the specified file
   hFile = CreateFile(sDLLName, GENERIC_READ, _
                      FILE_SHARE_READ Or FILE_SHARE_WRITE, _
                      0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0&)

  'if the handle is invalid, raise an
  'error to properly clean up and exit
   If hFile = INVALID_HANDLE_VALUE Then
       Err.Raise ERR_GENERIC
   End If

  'obtain a pointer to the image debug
  'info in the passed file
   ptrDbg = MapDebugInformation(hFile, sDLLName, vbNullString, 0&)
   
  'if the handle is invalid, raise an
  'error to properly clean up and exit
   If ptrDbg = 0 Then
       Err.Raise ERR_GENERIC
   End If
   
  'fill the UDT with the image debug info
   Call CopyMemory(IMGDBG, ByVal ptrDbg, Len(IMGDBG))

  'pad a string sufficiently large enough to
  'receive the string of exported functions
  'and copy the functions into the string.
   With IMGDBG
      strOut = String$(.ExportedNamesSize, vbNullChar)
      Call CopyMemory(ByVal strOut, ByVal .ExportedNames, .ExportedNamesSize)
   End With
   
  'if the calls above are successful, and the
  'file has exported methods, return the string
  'consisting of a series of null-terminated
  'strings naming all the functions exported
  'from the image
   GetFileImageInformation = strOut


fileExport_exit:
    Call UnmapDebugInformation(ptrDbg)
    Call CloseHandle(hFile)
    On Error GoTo 0
    Exit Function

fileExport_error:
    GetFileImageInformation = vbNullString
    Resume fileExport_exit
    
End Function


Private Sub Command1_Click()

   Dim sExports As String
   Dim tmp As String
   
   On Local Error GoTo enumdata_error
   
  'set up the common dialog
   With CommonDialog1
   
      .Flags = cdlOFNExplorer Or cdlOFNPathMustExist
      .CancelError = True
      .Filter = "DLL's (*.dll)|*.dll|OCX's (*.ocx)|*.ocx|All Files (*.*)|*.*"
      
     'get the file
      .ShowOpen
      
     'double check
      If Len(.FileName) > 0 Then
      
        'set up the form
         List1.Clear
         Label1.Caption = LCase$(.FileName)
      
        'retrieve the exported functions
         sExports = GetFileImageInformation(.FileName)
         
        'double check
         If Len(sExports) > 0 Then
         
           'remove an item from the string
           'returned and add to the list
            Do While Len(sExports) > 0 
               
               List1.AddItem StripNulls(sExports)
               
            Loop
         
         End If 'If Len(sExports)
      End If 'If .FileName
   End With 'With CDlg1
   

enumdata_exit:
   Exit Sub

enumdata_error:

   Label1.Caption = Err.Description
   Resume enumdata_exit
   
End Sub


Private Function StripNulls(startstr As String) As String

  Dim pos As Long
  pos = InStr(startstr, Chr$(0))
  
  If pos Then
      
      StripNulls = Mid$(startstr, 1, pos - 1)
      startstr = Mid$(startstr, pos + 1, Len(startstr))
    
  End If

End Function
 Comments
Try experimenting with the other attributes.  Of particular interest might be the Checksum member of the UDT.

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter