Search This Blog

Tuesday 9 September 2014

WinDbg : Trying To Find The Import Address Table (IAT) Of A Binary

WinDbg : Trying To Find The Import Address Table (IAT) Of A Binary


In the last article we learnt how to use the basic WinDbg commands we had learnt, to find out useful information within the loaded binary images. We will extend that study here, to see how other relevant fields output by the !dh command can be used. The focus of this article would be to use the WinDbg commands and to find the Import Address Table of the loaded image and to see the list of functions that it has imported.


Commands used in this article are:

  1. !dh
  2. dds
  3. ln
For this example I picked the DLL called KernelBase. you can use any other DLL or executable of your choice.I also used the -f switch to trim the output of !dh to the relevant sections we need here.

The IAT is an array of pointers which are loaded by the PE Image Loader. The IAT is used primarily as form of a lookup table, which is used to call function present in other library modules (.DLLs). Since the executable module will not the the know memory addresses of the libraries and it's stored functions, it brings the in the purpose of the IAT. The IAT slots will be written with memory addresses by the linker. 

The IAT is part of a larger data structure called the _IMAGE_IMPORT_DESCRIPTOR, which also contains another lookup table called the INT (Import Name Table), which is identical to the IAT.

Unfortunately, this structure is not exported in WinDbg, so we cannot use the dt command to see it. However, it is available in WinNt.H, so I will copy paste it for our convenience.

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;

The OriginalFirstThunk points to the Import Name Table and the FirstThunk points to the Import Address Table. We can find these in WinDbg, but we would need to use the !dh extension to do so.

kd> !dh kernelbase.dll -f


File Type: DLL
FILE HEADER VALUES
     14C machine (i386)
       4 number of sections
4CE7B8F0 time date stamp Sat Nov 20 17:32:56 2010

       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
    2102 characteristics
            Executable
            32 bit word machine
            DLL

OPTIONAL HEADER VALUES
     10B magic #
    9.00 linker version
   42400 size of code
    3E00 size of initialized data
       0 size of uninitialized data
    7DE0 address of entry point
    1000 base of code
         ----- new -----
75d70000 image base
    1000 section alignment
     200 file alignment
       3 subsystem (Windows CUI)
    6.01 operating system version
    6.01 image version
    6.01 subsystem version
   4A000 size of image
     400 size of headers
   4984C checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
     140  DLL characteristics
            Dynamic base
            NX compatible
    18D4 [    4E67] address [size] of Export Directory
   406A8 [       28] address [size] of Import Directory
   46000 [      530] address [size] of Resource Directory
          0 [         0] address [size] of Exception Directory
          0 [         0] address [size] of Security Directory
   47000 [   25D0] address [size] of Base Relocation Directory
   43294 [       38] address [size] of Debug Directory
         0 [          0] address [size] of Description Directory
         0 [          0] address [size] of Special Directory
         0 [          0] address [size] of Thread Storage Directory
 1E110 [         40] address [size] of Load Configuration Directory
     278 [        1C] address [size] of Bound Import Directory
   1000 [       65C] address [size] of Import Address Table Directory
        0 [           0] address [size] of Delay Import Directory
        0 [           0] address [size] of COR20 Header Directory
        0 [           0] address [size] of Reserved Directory

Using the dd command to dump the memory addresses of the Import Directory, we can see the relevant members mentioned earlier.

kd> dd 406A8+kernelbase
75db06a8  000406dc ffffffff ffffffff 000406d0
75db06b8  00001000 00000000 00000000 00000000
75db06c8  00000000 00000000 6c64746e 6c642e6c
75db06d8  9090006c 00040d38 00040d58 00040d78
75db06e8  00040d86 00040d90 00040da4 00040db2
75db06f8  00040dce 00040dee 00040e00 00040e12
75db0708  00040e2e 00040e42 00040e5e 00040e78

75db0718  00040e82 00040e9a 00040eb2 00040ec6

000406dc is the address of the OriginalFirstThunk and the 00001000 is the address of the FirstThunk

Lets use the dd command to dump the OriginalFirstThunk.

kd> dd 000406dc+kernelbase
75db06dc  00040d38 00040d58 00040d78 00040d86
75db06ec  00040d90 00040da4 00040db2 00040dce
75db06fc  00040dee 00040e00 00040e12 00040e2e
75db070c  00040e42 00040e5e 00040e78 00040e82
75db071c  00040e9a 00040eb2 00040ec6 00040ede
75db072c  00040ef2 00040f0a 00040f36 00040f50
75db073c  00040f7a 00040f92 00040fa0 00040fb8

75db074c  00040fca 00040fda 00040ff4 00040ffe

The addresses shown are for the INT, and by using the dc command on one of the selected offsets with the base address, you will be able to view the name of the function. 

kd> dc 00040d38+kernelbase
75db0d38  745204c2 696e556c 65646f63 69727453  ..RtlUnicodeStri
75db0d48  6f54676e 69736e41 69727453 0000676e  ngToAnsiString..
75db0d58  74520268 736e416c 72745369 54676e69  h.RtlAnsiStringT
75db0d68  696e556f 65646f63 69727453 0000676e  oUnicodeString..
75db0d78  765f074c 70776e73 746e6972 077e0066  L._vsnwprintf.~.
75db0d88  736d656d 00007465 74520346 6572466c  memset..F.RtlFre
75db0d98  736e4165 72745369 00676e69 74520348  eAnsiString.H.Rt

75db0da8  6572466c 61654865 02da0070 446c7452  lFreeHeap...RtlD

We can display similar information with the FirstThunk, using FirstThunk can be a bit cleaner, since we can use the ln command with the address to find the exact function, which will be displayed cleanly with WinDbg.

kd> dd 00001000+kernelbase
75d71000  77bf9e8e 77bf572f 77c0caaa 77bd5340
75d71010  77bf3306 77bf2c6a 77bf9ac5 77bfa149
75d71020  77bf2dd6 77c3ea50 77c08b1b 77c07dfd
75d71030  77be6048 77be6678 77be54c8 77be6638
75d71040  77be55d8 77be66f8 77bf30fb 77bf7e74
75d71050  77be6398 77bf3063 77be63a8 77bf2fed
75d71060  77be5658 77be5cd8 77be6a18 77be5a08
75d71070  77be55a8 77be6018 77bd4500 77c0314e
kd> ln 77bf9e8e
(77bf9e8e)   ntdll!RtlUnicodeStringToAnsiString   |  (77bf9f5b)   ntdll!RtlpScanEnvironment
Exact matches:

    ntdll!RtlUnicodeStringToAnsiString (<no parameter info>)

Now, going back to the original output of the !dh, we see that the offset 0x1000 is also shown as:

1000 [       65C] address [size] of Import Address Table Directory

So this extension does give us the FirstThunk directly.

We can use the dds command to dump address range from the virtual address of the beginning of the IAT array. dds is equivalent to dd except that 's' stands for resolving symbols (if available). This means that if any of the addresses dumped in word format resolves to a known symbol, then the corresponding name would also be displayed. (Also read up on the dps command to see how it can also be used in this case).


kd> dds kernelbase + 1000 
75d71000  77bf9e8e ntdll!RtlUnicodeStringToAnsiString
75d71004  77bf572f ntdll!RtlAnsiStringToUnicodeString
75d71008  77c0caaa ntdll!_vsnwprintf
75d7100c  77bd5340 ntdll!memset
75d71010  77bf3306 ntdll!RtlFreeAnsiString
75d71014  77bf2c6a ntdll!RtlFreeHeap
75d71018  77bf9ac5 ntdll!RtlDeleteCriticalSection
75d7101c  77bfa149 ntdll!RtlInitializeCriticalSection
75d71020  77bf2dd6 ntdll!RtlAllocateHeap
75d71024  77c3ea50 ntdll!CsrVerifyRegion
75d71028  77c08b1b ntdll!CsrClientConnectToServer
75d7102c  77c07dfd ntdll!RtlCreateTagHeap
75d71030  77be6048 ntdll!ZwQueryInformationProcess
75d71034  77be6678 ntdll!NtSetInformationProcess
75d71038  77be54c8 ntdll!NtClose
75d7103c  77be6638 ntdll!ZwSetInformationFile
75d71040  77be55d8 ntdll!NtCreateIoCompletion
75d71044  77be66f8 ntdll!NtSetIoCompletion
75d71048  77bf30fb ntdll!RtlSetLastWin32Error
75d7104c  77bf7e74 ntdll!SbSelectProcedure
75d71050  77be6398 ntdll!NtRemoveIoCompletion
75d71054  77bf3063 ntdll!RtlDeactivateActivationContextUnsafeFast
75d71058  77be63a8 ntdll!NtRemoveIoCompletionEx
75d7105c  77bf2fed ntdll!RtlActivateActivationContextUnsafeFast
75d71060  77be5658 ntdll!ZwCreateNamedPipeFile
75d71064  77be5cd8 ntdll!NtOpenFile
75d71068  77be6a18 ntdll!ZwWaitForSingleObject
75d7106c  77be5a08 ntdll!ZwFsControlFile
75d71070  77be55a8 ntdll!ZwCreateEvent
75d71074  77be6018 ntdll!ZwQueryInformationFile
75d71078  77bd4500 ntdll!_allmul
75d7107c  77c0314e ntdll!RtlSetDaclSecurityDescriptor

So we see that we are getting the same output which ever method we prefer to use.

Try out these command sequences for other binary images to see the outputs. Beware, not all binaries import, though that would be an extremely rare occurrence. Also please do note, that in the above example I ignored the size of the import table 0x65C. The right way to dump would be to ask dds to only dump till the end of the import address table, else it might show garbage.



No comments:

Post a Comment