Monday, March 3, 2014

Dissecting the PE File Format - 9

Navigating Imports On Disk

 In the hex editor, we navigate the import table. The RVA of the import directory is stored in the DWORD 80h bytes from the PE header which in our example is offset 180h and the RVA is 2D000H (as in the Data Directory post). We now have to convert that RVA to a raw offset to peruse the correct area of our file on disk. Check the Section Table to see which section the address of the import directory lies in. In our case, the Import Directory starts at the beginning of the .idata section and we know that the section table holds the raw offset in the PointerToRawData field. In our example, the offset is 2AC00h









The difference between the RVA and the Raw offset is 2D000h - 2AC00h = 2400h. Make a note for this as it will be used for converting further offsets.

At offset 2AC00 we have the import directory - an array of IMAGE_IMPORT_DESCRIPTORS each of 20 bytes and repeating for each import library (DLL) until terminated by 20 bytes of zeroes. 















Each group of 5 dwords represent 1 IMAGE_IMPORT_DESCRIPTOR. The first shows that in this PE file OriginalFIrstThunk, TimeDateStamp and ForwarderChain are set to 0. Eventually we come to a set of 5 DWORDS all set to 0 (highlighted in red) signifying the end of the array. We see that we are importing from 8 DLLs. 

NOTE - The OriginalFirstThunk fields in our example are all set to zero. This is common for executables made with the Borland compiler and linker and is noteworthy for the following reason. In a packed executable the FirstThunk pointers will have been destroyed but can sometimes be rebuilt by copying the duplicate OriginalFirstThunks, There is a utility called First_Thunk Rebuilder which will do this. However, with Borland created files this is not possible because the OriginalFirstThunks are all zeros and there is no INT.

Back to our example above, the Name1 field of the first IMAGE_IMPORT_DESCRIPTOR contains the RVA 00 02  DF 30h (NB Reverse Byte Order). Convert this to a raw offset by subtracting 2400h (remember above) and we have 2b130h. If we look at our PE file we see the name of out DLL.


To continue, the FirstThunk field contains the RVA 00 02 D0 B4h which converts to Raw Offset 2ACB4h. Remember, this si the offset to array of DWORD-size IMAGE_THUNK_DATA structures - the IAT. This will either have its most significant bit set and the lower part will contain the ordinal number of the imported function, or if the MSB is not set it will contain yet another RVA to the name of the function (IMAGE_IMPORT_BY_NAME)

In our file, the DWORD at 2ACB4h is 00 02 D5 3E







This is another RVA which converts to Raw Offset 2B13E. This time it should be a null-terminated ASCII string. In our file, we see








So, the name of the first API imported from kernel32.dll is DeleteCriticalSection. You may notice the two  zero bytes before the function name. This is the hint element which is often set to  00 00.

All of this can be verified using PEBrowse Pro to parse the IAT as shown:
























If the file has been loaded into memory, dumped and examined with the hex editor then the DWORD at RVA 2D0B4h which contained 3E D5 02 00 on disk would have been overwritten by the loader with the address of DeleteCriticalSection in kernel32.dll







Allowing for reverse byte order this is 7C91188A
NOTE:  functions in system DLLs always tend to start at addresses 7xxxxxx and stay the same each time the program is loaded.  However, they tend to change if you reinstall your OS and differ from one computer to another.

The addresses also differ according to OS, for example: 
OS                           Base of kernel32.dll 
Win XP SP1                  77E60000H 
Win XP SP2                  7C000000H 
Win 2000 SP4                79430000H 

Windows updates also sometimes change the base location of system DLLs. 

















Navigating Imports in Memory

Load the example in Olly and look at the memory map







Note the address of the idata section is 42D000 which corresponds to the RVA 2D000 shown at the top of this post as VOffset.  This size has been rounded up to 200 to fit memory page boundaries.

The main (CPU) window of Olly will only show the IAT if it lies in the executable CODE section, however in most cases it will be in its own section e,g, idata. You can view the IAT in Olly's hex-dump by right clicking the appropriate section in memory map and selecting Dump in CPU. Now rightclick in the hex window and select Long>Address and you will see the IAT in a readable list. 



















This makes finding the beginning and the end of the IAT easy and is useful when using ImpREC as the IAT AutoSearch function can be inaccurate. It is good to be able to check the beginning and endpoint to avoid having to type in a large size value which will give many false negatives.

The names window (ctrl + N) will show you imported functions:

Rightclicking any of these and selecting find references to Import will show you the jump thunk stub and the instances in the code where the function is called (only 1 in this case)

Note: In the comment column you will see that Olly has determined that the kernel32.dll function DeleteCriticalSection is actually forwarded to RtlDeleteCriticalSection in ntdll.dll 
Rightclicking and selecting Follow Import in Disassembler will show you the address in the appropriate DLL where the function's code starts e.g. starts at 7C91188A in ntdll.dll

If we look at the call to DeleteCriticalSection at 00401B12 we see:


This is really "CALL 00401314" but Olly has already substituted the function name for us. 401314 is the address of the jump stub pointing to the IAT. Note it is a part of the jump thunk table described previously:

This is really "JMP DWORD PTR DS:[0042D0B4]" but again Olly has substituted the symbolic name for us. Address 0042D0B4 contains the Image_Thunk_Data Structure in the IAT which has been overwritten by the loader with the actual address of the function in kernel32.dll: 7C91188A. This is what we found earlier by rightclicking and selecting follow Import in Disassembler and also from the dumped file above.

In the next post we talk about Adding Code to a PE File

No comments:

Post a Comment