Thursday, March 6, 2014

Dissecting the PE File Format - 11

Adding Import To an Executable


This is most often used in the context of patching a target app where we dont have the APIs we need. To recap, the minimum information needed by the loader to produce a valid IAT is:


1. Each DLL must be declared with an IMAGE_IMPORT_DESCRIPTOR(IID), remembering to close the Import Directory with a null-filled one.
2. Each IID nneds at least Name1 and FirstThunk fields, the rest can be set to 0 (setting OriginalFirstThunk = FIrstThunk i.e. duplicating the RVAs also works)
3. Each entry of the FIrstThunk must be an RVA to an Image_Thunk_Data (the IAT) which in turn further contains an RVA to the API name. The name will be a null-terminated ASCII string of variable length and preceded by 2 bytes (hint) which can be set to 0.
4. if IIDs have been added then the isize field of the import table in the Data Directory may need changing. The IAT entries in the Data Directory need not be altered.

Writing new import data in a hex editor and then pasting into your target can be very time-consuming. There are tools which can automate this (SnippetCreator, IIDKing etc) but to understand the concept its best to do it manually. The main task is to append a new IID onto the end of the import table - you need 20 bytes for each DLL used, not forgetting 20 bytes for the null-terminator. In nearly all cases there will be no space at the end of the existing import table so we will make a copy and relocate it somewhere there is space.

Step 1 - Create space for a new IID

This involves the following steps:

  1. Move all the IIDs to a location where there is plenty of space. This can be anywhere; at the end of the current .idata section or an entirely new section. 
  2. Update the RVA of the new Import Directory in the Data Directory of the PE header.
  3. If necessary, round up the size of the section where you've put the new Import table so everything is mapped in memory (e.g. VirtualSize of the .idata section rounded up 1000h)
  4. Run it and if it works we move on to step 2. if it doesn't check the injected descriptors are mapped in memory and that the RVA of the import directory is correct.
NOTE - The IIDs, FirstThunk and OriginalFirstThunk contain RVAs - RELATIVE ADDRESSES - which means you can cut and paste the Import Directory (IIDs) wherever you want in the PE file (taking into account the destination has to be mapped into memory) and simply changing the RVA (and size if necessary) of the Import Directory in the Data Directory will make the app run perfectly.

Back to the hex editor, the first IID and the null terminator are outlined in red and you see that there is no space after the null IID.



However, there is a large amount of space at the end of the .idata section before .rdata starts. We will copy and paste the existing IIDs shown above to offset 2C500h at this new location:

 
















To convert the new offset to an RVA

VA = Raw Offset - RawOffsetOfSection + VirtualOffsetOfSection
 = 2C500 - 2AC00 + 2D000 = 2E900h

So change the virtual address of the import table in the data directory from 2D000 to 2E900. Now edit the .idata section header and make virtual size equal to RawSize so the loader will map the whole section in. Run the app to test it.


Step 2 - Add the new DLL and function details

This involves the following steps:
  1. Add null-terminated ASCII string of the names of your DLL and function to a free space in the .idata section. The function name will actually be an Image_Import_By_Name structure preceded by a null word (the hint field)
  2. Calculate the RVAs of the above strings
  3. Add the RVA of the DLL name to the Name1 field of your new IID
  4. Find another DWORD sized space and put in it the RVA of the hint/function name. This becomes the Image_Thunk_Data or IAT of our new DLL.
  5. Calculate the RVA of the above Image_Thunk_Data DWORD and add it tot he FirstThunk field of your new IID.
  6. Run the app to test and your new API is ready to be called.
In order to fill our new IID we need at the very least Name1 and FirstThunk fields (the others can be nulled). The Name1 field contains the RVA of the name of the DLL in null-terminated ASCII. The FirstThunk field contains the RVA of an Image-Thunk Data Structure which in turn contains yet another RVA of the name of the function in null-terminated ASCII. The name is preceded by 2 bytes (Hint) which can be set to zero.

For e.g. we want to use the function LZCopy which copies a source file to a destination file. if the source file is compressed with the Microsoft File Compression Utility, this function creates a decompressed destination file. If the source file is not compressed, this function duplicates the original file.

This function resides in lz32.dll which is not currently used by our app. Therefore we first need to add strings for the names "Lz32.dll" and "LZCopy". We scroll upwards in the hex editor from the new import table towards the end of the preexisting data and add the DLL name then the function name onto the end. Note the null bytes after each string and the null WORD before the function name.


















Now we need to calculate the RVAs of these:

RVA = RawOffset - RawOffsetOfSection + VirtualOffsetOfSection + ImageBase

RVA of DLL Name - 2c420 - 2ac00 + 2d000 = 2E820 (20 E8  02 00 in reverse)
RVA of Function name - 2c430 - 2ac00 + 2d000 = 2E830 (30 E8 02 00 in reverse)

The first one can go into the name1 field of our new IID but the second must go into an Image_Thunk_Data structure, the RVA of which we can put into the FirstThunk field (and OriginalFirstThunk) of our new IID. We will put the Image_Thunk_Data structure below the function name string at the offset 2C440 and calculate the RVA which we will put in the FirstThunk

RVA of Image_Thunk_Data = 2C440 - 2AC00 + 2D00 = 2E840 (40 E8 02 00 in reverse)

If we fill the data in the hexeditor:

Finally save changes, run the app to test and re-examine the imported functions in PEBrowse

In order to call the new function, we can use the following code:

CALL DWORD PTR [XXXXXXX] where XXXXXXXX = RVA of Image_Thunk_Data + ImageBase

Final Note: Even if we had added a function used by a DLL which was already in use e.g. kernel32.dll we would still need to create a new IID for it to enable us to create a new IAT at a convenient location as above.


In the next pose we talk about packers.


No comments:

Post a Comment