Complete Tour of PE and ELF: Directories
In Part 1 and Part 2 of this series we have done a walkthrough of almost 70 percent of E structure. There are still some import sections remaining like exports, relocations, etc. In this article, we will cover all the remaining sections of the PE structure. Just for a refresher, we covered the following sections of PE
So let’s start with DataDirectory i.e. exports section which is one of the most important data directories of the PE. This data directory is named as IMAGE_DIRECTORY_ENTRY_EXPORT
As usual, it points to a VirtualAddress of the export structure named _IMAGE_EXPORT_DIRECTORY and its size. Unlike import sections where there are multiple structures for each DLL, there will be a single export section.
Few important points before we dig deeper into this structure:
- Functions can be exported by name
- Functions can be exported by ordinals which are just an index to functions. This approach is more efficient but harder for programmers to implement
Important fields in _IMAGE_EXPORT_DIRECTORY
- TimeDateStamp: if you remember Part 2 section bound imports, there it shows what timedatestamp version of DLL it is importing. That timestamp is got filled from this field. This is only changed when the export information regarding a DLL is changing for example RVA to a particular export function.
- Base: This field points to the base of AddressOfFunctions by subtracting from the ordinal
- NumberOfFunctions: This field shows the number of functions export by this DLL. It includes both export by ordinal and export by name
- NumberOfNames: This field shows the number of functions exported by names only.
- AddressOfFunctions: This points to an array of the addresses of exported functions marked by RVA. This is called Export Address Table (EAT).
- AddressOfNames: This point to an array of DWORD RVA’s which point to the strings corresponding to function names. This corresponds to Export Names Table(ENT).
- AddressOfNamesOrdinals: This point to an array which holds WORD sized ordinals. They start from zero.
So just to give a clear indication of how it works is like whenever a function is imported by name, it just finds the matching index entry in ENT and then use that index to find value in Ordinal’s table (AddressOfNamesOrdinals) and the using this value as an index into EAT. As you would have guessed by now that importing by ordinal only works when the DLL remains same.
Also, if you remember, there was a ForwardedImport, which tells the sort of pointing to a chain of DLL’s while importing. Similarly, in ForwardedExports, as we saw AddresssofFunctions points to actual function code used in the image, there might be an RVA that points out some function string in AddressOfNames section which says to the loader that this string is in some other DLL. If you have come across Stuxnet code, then it heavily uses ForwardedExports.
So moving onto the next data directory in optional header i.e. IMAGE_DIRECTORY_ENTRY_DEBUG, which is entry .
As usual, it points to the next structure and its size.
This is structure being pointed to by the IMAGE_DIRECTORY_ENTRY_DEBUG
Important fields in this structure are:
- TimeDateStamp: This is the TimeDateStamp information which is related to last debug. In most of the cases, it should be same to TimeDateStamp in File Header.
- Type: This has several values but most common is 2(IMAGE_DEBUG_TYPE_CODEVIEW). It points to a PDB structure (one of 2 structs: CV_INFO_PDB20 and CV_INFO_PDB70)which points to debug symbols.
- SizeOfData: This point to the size of that PDB struct.
- AdressOfRawData: This is offset to debug Info in memory
- PointerToRawData: This is offset to debug info on disk
Below is the .pdb file
Next data directory that we are going to discuss is IMAGE_DIRECTORY_ENTRY_TLS
When threads execute, they run in their respective contexts and thus it is desirable to have their storage for storing local variables. This storage space will be initialized/destroyed on thread creation/destruction
VirtualAddress points to _IMAGE_TLS_DIRECTORY whose structure looks like below
Important fields are:
- StartAddressOfRawData: This points to the Actual Virtual Address (AVA) where the code starts
- EndAddressOfRawData: This points to the Actual Virtual Address(AVA) where the code ends
- AddressOfCallBacks: This is an AVA, which points to PIMAGE_TLS_CALLBACK function pointers. These functions are called at thread initialization.
Moving onto next section, we have Resources section named IMAGE_DIRECTORY_ENTRY_RESOURCE which contains things like icons, etc. and looks like below
IMAGE_DIRECTORY_ENTRY_RESOURCE points to _IMAGE_RESOURC_DIRECTORY
Important fields are:
- NumberOfNamedEntries: Resources Represented by names
- NumberOfIdEntries: Resources represented by ids.
Immediately following the IMAGE_RESOURCE_DIRECTORY is an array named as _IMAGE_RESOURCE_DIRECTORy_ENTRY which is an array of NumberOfNamedEntries + NumberOfIdEntries. This contains DWORDS out of which if MSB of the first DWORD is set then-then the lower 31 bits points to an offset to a string which is a resource. If MSB is not set then, it will be treated as ID. For 2nd DWORD if MSB is set then the lower 31 bits will point to another IMAGE_RESOURCE_ DIRECTORY. However if the 2nd DWORD MSB is not set, then it is treated as an offset to actual data.
Notice the MSB of these bits where it is not zero. First DWORD point to offset for string and 2nd DWORD points to a directory. Note that offset is translated as RVA from 80000020 to 0005C20
This is the ultimate resource in the binary.
Moving onto the next section is IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
This points to another data structure IMAGE_LOAD_CONFIG_DIRECTORY32. Important fields in this struct are:
- SecurityCookie: this is AVA, and it points to the location where the stack cookie is used with /GS flag. This is placed on the attack right after stack base point pointer is saved. It is used to overcome buffer flows( the main objective is to overwrite EIP) as when the sack is being pooped out this cooked will be checked with original value and since in bufferoverflow this value will be overwritten it shows that the EIP can be corrupted.
- SEHandlerTable: This is an AVA, and it points to a table of RVA’s which specify the exception handler which is valid to use with Structured Exception Handler(SEH) for this binary to handle exceptions.
- SEHandlerCount: This points to number of entries in the table pointed to by SEHandlerTable
So over to the last section which we will be discussing for PE is IMAGE_DIRECTORY_ENTRY_SECURITY, which is used to point to the certificate if the code is signed.
Below we can see that Microsoft signs the exe.
Just look at how much we have covered in the PE Structure.
So this article concludes our PE structure. From next article, we will start parsing ELF structure