Search This Blog

Tuesday, 9 September 2014

WinDbg : Trying To Find The Export Address Table (EAT) Of A Binary

WinDbg : Trying To Find The Export Address Table (EAT) 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. That article taught us how to parse the import address table (IAT) of a loaded binary. We will extend that study here, to try and learn one can parse the export address table (EAT) of a loaded binary image.

Commands used in this article are:


  1. dt
  2. !dh
  3. dd
  4. da
Before we begin, a brief introduction about the EAT is desirable. The EAT works in the same way as the IAT, apart from the fact that the library will be exporting the functions to the image executable, in which the program will import into the IAT. The key data structure that is used by Windows to map the EAT is _IMAGE_EXPORT_DIRECTORY. Now, we know from our previous learning that to see the types created by us, we can use the dt command. A quick recall of that exercise can be found here. Unfortunately, WinDbg(atleast the current version) cannot find this type! So we need to calculate the offsets manually. the structure is defined in the header file WinNt.H. I am copying the same here for convenience.

//

// Export Format
//

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

The AddressOfFunctions is the field we are interested in. the comment says that the array contains Relative virtual Addresses (RVAs) from the base of the image. It is the the PE loaders responsibility to translate these to real addresses for the binary using it in the IAT.

The AddressOfNames and AddressOfNameOrdinals are loaded alongside each other, to provide a linkage between the address of the function and the name of the function. The AddressOfNames is a pointer to a array of function names, and the AddressOfNameOrdinals is a pointer to a array used to index into the AddressOfFunctions to obtain the addresses for the function names. Now, lets try to use this knowledge to see if we can actually find the names of the functions which a particular binary exports. Not all binaries will necessarily export, in such cases the number of entries in the EAT will be zero.

kd> !dh lsass.exe -f

File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
     14C machine (i386)
       4 number of sections
4A5BBF3E time date stamp Tue Jul 14 04:41:58 2009

       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
     122 characteristics
            Executable
            App can handle >2gb addresses
            32 bit word machine

OPTIONAL HEADER VALUES
     10B magic #
    9.00 linker version
    4200 size of code
    1000 size of initialized data
       0 size of uninitialized data
    2F3D address of entry point
    1000 base of code
         ----- new -----
00d90000 image base
    1000 section alignment
     200 file alignment
       2 subsystem (Windows GUI)
    6.01 operating system version
    6.01 image version
    6.01 subsystem version
    9000 size of image
     600 size of headers
    6193 checksum
00040000 size of stack reserve
00006000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
    8140  DLL characteristics
            Dynamic base
            NX compatible
            Terminal server aware
    1EC4 [      6B] address [size] of Export Directory
    4478 [     154] address [size] of Import Directory
    7000 [     700] address [size] of Resource Directory
         0 [         0] address [size] of Exception Directory
         0 [         0] address [size] of Security Directory
    8000 [     368] address [size] of Base Relocation Directory
    5080 [      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
    3F90 [      40] address [size] of Load Configuration Directory
      280 [    294] address [size] of Bound Import Directory
    1000 [    1AC] 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

Ok, now lets dump the contents at the offset of 0x1EC4 to see what they contain.
kd> dd 1EC4+lsass
00d91ec4  00000000 4a5bbf3e 00000000 00001f00
00d91ed4  00000001 00000002 00000002 00001eec
00d91ee4  00001ef4 00001efc 00002ef6 000023c9
00d91ef4  00001f0a 00001f1a 00010000 7361736c
00d91f04  78652e73 734c0065 74654761 65746e49
00d91f14  63616672 734c0065 67655261 65747369
00d91f24  74784572 69736e65 90006e6f 90909090
00d91f34  8b55ff8b 565351ec 57ff3357 00008a68

This is the _IMAGE_EXPORT_DIRECTORY structure. The three highlighted fields are the following:
00001f00Name
00001ef4 : AddressOfFunctions
00001efc : AddressOfNames
00002ef6 : AddressOfNameOrdinals

To verify whether we have indeed the right offsets, lets dump out the contents of the 'Name' to see if it is indeed lsass.exe.


kd> da 00001f00+lsass

00d91f00  "lsass.exe"

Looks like we did get he right members. Then lets use the other addresses to find the names of all the functions exported. Lets use the dd command to dump the addresses first.

kd> dd 00001ef4+lsass
00d91ef4  00001f0a 00001f1a 00010000 7361736c
00d91f04  78652e73 734c0065 74654761 65746e49
00d91f14  63616672 734c0065 67655261 65747369
00d91f24  74784572 69736e65 90006e6f 90909090
00d91f34  8b55ff8b 565351ec 57ff3357 00008a68
00d91f44  20206800 7fe800d9 33000009 ff5343db
00d91f54  d910fc15 41186800 15ff00d9 00d910f8
00d91f64  ff535753 d9108415 0cc48300 458d046a

Now we can use the da command to dump the name of each function that is exported. it is a manual process to walk the arrays or we can write scripts for it. Scripts are a slightly advanced topic, which will be covered in future posts, we can revisit the problem at that time.

kd> da 00001f0a+lsass
00d91f0a  "LsaGetInterface"








2 comments:

  1. Your RVA locations are off by 4; the actual offsets in your case are:
    00001f00 : Name
    00001eec : AddressOfFunctions
    00001ef4 : AddressOfNames
    00001efc : AddressOfNameOrdinals

    That's why using 0x1ef4 you get to a function name, not function body.

    ReplyDelete
  2. An easy way to check your work is to use !dh -e

    ReplyDelete