The image data directories...continued
The exports directory
The exports directory holds details of the functions exported by this executable. For example, if you were to look in the exports directory
of the MSVBVM50.dll it would list all the functions it exports that make up the visual basic 5 runtime environment.
This directory consists of some info to tell you how many exported functions there are followed by three parallel arrays which give you the
address, name and ordinal of the functions respectively. The structure is defined thus:


VB Code:
  1. Private Type IMAGE_EXPORT_DIRECTORY
  2.     Characteristics As Long
  3.     TimeDateStamp As Long
  4.     MajorVersion As Integer
  5.     MinorVersion As Integer
  6.     lpName As Long
  7.     Base As Long
  8.     NumberOfFunctions As Long
  9.     NumberOfNames As Long
  10.     lpAddressOfFunctions As Long    '\\ Three parrallel arrays...(LONG)
  11.     lpAddressOfNames As Long        '\\ (LONG)
  12.     lpAddressOfNameOrdinals As Long '\\ (INTEGER)
  13. End Type

And you can read this info from the executable thus:

VB Code:
  1. Private Sub ProcessExportTable(ExportDirectory As IMAGE_DATA_DIRECTORY)
  2.  
  3. Dim deThis As IMAGE_EXPORT_DIRECTORY
  4. Dim lBytesWritten As Long
  5. Dim lpAddress As Long
  6.  
  7. Dim nFunction As Long
  8.  
  9. If ExportDirectory.VirtualAddress > 0 And ExportDirectory.Size > 0 Then
  10.     '\\ Get the true address from the RVA
  11.     lpAddress = AbsoluteAddress(ExportDirectory.VirtualAddress)
  12.     '\\ Copy the image_export_directory structure...
  13.     Call ReadProcessMemoryLong(DebugProcess.Handle, lpAddress, VarPtr(deThis), Len(deThis), lBytesWritten)
  14.     With deThis
  15.         If .lpName <> 0 Then
  16.             image.Name = StringFromOutOfProcessPointer(DebugProcess.Handle, image.AbsoluteAddress(.lpName), 32, False)
  17.         End If
  18.         If .NumberOfFunctions > 0 Then
  19.             For nFunction = 1 To .NumberOfFunctions
  20.                 lpAddress = LongFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(.lpAddressOfNames) + ((nFunction - 1) * 4))
  21.                 fExport.Name = StringFromOutOfProcessPointer(DebugProcess.Handle, image.AbsoluteAddress(lpAddress), 64, False)
  22.                 fExport.Ordinal = .Base + IntegerFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(.lpAddressOfNameOrdinals) + ((nFunction - 1) * 2))
  23.                 fExport.ProcAddress = LongFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(.lpAddressOfFunctions) + ((nFunction - 1) * 4))
  24.             Next nFunction
  25.         End If
  26.     End With
  27. End If
  28.  
  29.    
  30. End Sub


The imports directory
The imports directory lists the dynamic link libraries that this executable depends on and which functions it imports from that dynamic link library.
It consists of an array of IMAGE_IMPORT_DESCRIPTOR structures terminated by an instance of this structure where the lpName parameter is zero.
The structure is defined as:


VB Code:
  1. Private Type IMAGE_IMPORT_DESCRIPTOR
  2.     lpImportByName As Long ''\\ 0 for terminating null import descriptor
  3.     TimeDateStamp As Long  ''\\ 0 if not bound,
  4.                            ''\\ -1 if bound, and real date\time stamp
  5.                            ''\\ in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
  6.                            ''\\ O.W. date/time stamp of DLL bound to (Old BIND)
  7.     ForwarderChain As Long ''\\ -1 if no forwarders
  8.     lpName As Long
  9.     lpFirstThunk As Long ''\\ RVA to IAT (if bound this IAT has actual addresses)
  10. End Type

And you can walk the import directory thus:

VB Code:
  1. Private Sub ProcessImportTable(ImportDirectory As IMAGE_DATA_DIRECTORY)
  2.  
  3. Dim lpAddress As Long
  4. Dim diThis As IMAGE_IMPORT_DESCRIPTOR
  5. Dim byteswritten As Long
  6. Dim sName As String
  7. Dim lpNextName As Long
  8. Dim lpNextThunk As Long
  9.  
  10. Dim lImportEntryIndex As Long
  11.  
  12. Dim nOrdinal As Integer
  13. Dim lpFuncAddress As Long
  14.  
  15.  
  16. '\\ If the image has an imports section...
  17. If ImportDirectory.VirtualAddress > 0 And ImportDirectory.Size > 0 Then
  18.     '\\ Get the true address from the RVA
  19.     lpAddress = AbsoluteAddress(ImportDirectory.VirtualAddress)
  20.     Call ReadProcessMemoryLong(DebugProcess.Handle, lpAddress, VarPtr(diThis), Len(diThis), byteswritten)
  21.    
  22.     While diThis.lpName <> 0
  23.         '\\ Process this import directory entry
  24.         sName = StringFromOutOfProcessPointer(DebugProcess.Handle, image.AbsoluteAddress(diThis.lpName), 32, False)
  25.  
  26.         '\\ Process the import file's functions list
  27.         If diThis.lpImportByName <> 0 Then
  28.             lpNextName = LongFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(diThis.lpImportByName))
  29.             lpNextThunk = LongFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(diThis.lpFirstThunk))
  30.             While (lpNextName <> 0) And (lpNextThunk <> 0)
  31.                 '\\ get the function address
  32.                 lpFuncAddress = LongFromOutOfprocessPointer(DebugProcess.Handle, lpNextThunk)
  33.                 nOrdinal = IntegerFromOutOfprocessPointer(DebugProcess.Handle, lpNextName)
  34.                 '\\ Skip the two-byte ordinal hint
  35.                 lpNextName = lpNextName + 2
  36.                 '\\ Get this function's name
  37.                 sName = StringFromOutOfProcessPointer(DebugProcess.Handle, image.AbsoluteAddress(lpNextName), 64, False)
  38.                 If Trim$(sName) <> "" Then
  39.                     '\\ Get the next imported function...
  40.                     lImportEntryIndex = lImportEntryIndex + 1
  41.                     lpNextName = LongFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(diThis.lpImportByName + (lImportEntryIndex * 4)))
  42.                     lpNextThunk = LongFromOutOfprocessPointer(DebugProcess.Handle, image.AbsoluteAddress(diThis.lpFirstThunk + (lImportEntryIndex * 4)))
  43.                 Else
  44.                     lpNextName = 0
  45.                 End If
  46.             Wend
  47.         End If
  48.                
  49.         '\\ And get the next one
  50.         lpAddress = lpAddress + Len(diThis)
  51.         Call ReadProcessMemoryLong(DebugProcess.Handle, lpAddress, VarPtr(diThis), Len(diThis), byteswritten)
  52.     Wend
  53.  
  54. End If
  55.    
  56. End Sub

The resource directory
The structure of the resource director is somewhat more involved. It consists of a root directory (defined by the structure
IMAGE_RESOURCE_DIRECTORY immediately followed by a number of resource directory entries (defined by the structure
IMAGE_RESOURCE_DIRECTORY_ENTRY
). These are defined thus:

VB Code:
  1. Private Type IMAGE_RESOURCE_DIRECTORY
  2.     Characteristics As Long '\\Seems to be always zero?
  3.     TimeDateStamp As Long
  4.     MajorVersion As Integer
  5.     MinorVersion As Integer
  6.     NumberOfNamedEntries As Integer
  7.     NumberOfIdEntries As Integer
  8. End Type
  9.  
  10. Private Type IMAGE_RESOURCE_DIRECTORY_ENTRY
  11.     dwName As Long
  12.     dwDataOffset As Long
  13.     CodePage As Long
  14.     Reserved As Long
  15. End Type

Each resource directory entry can either point to the actual resource data or to another layer of resource directory entries. If the highest bit of
dwDataOffset is set then this points to a directory otherwise it points to the resource data.

How is this information useful?
Once you know how an executable is put together you can use this information to peer into its workings. You can view the resources compiled into
it, the dlls it depends on and the actual functions it imports from them. More importantly you can attach to the executable as a debugger and
track down any of those really troublesome general protection faults. The next article will describe how to attach a debugger and use the PE file
format.