Part 0 - IAT Rebuilding and the Five Stages of Grief: What is the IAT

This post is born from caos. I was following the legendary Lena151 reverse engineering course for beginners (that you can find here) and at episode 21 the subject is dumping packed software. The idea of packing software is pretty straight forward…if you do not consider the fact that the packer will mess with Windows Loader and do basically whatever it wants when we consider the mapping of the program onto the virtual memory. Following the Lena tutorial is not complicated but it glosses over some details that I feel are important for a someone that wants to have a better understanding of the loader mechanisms. Note: when reviewing part 21 and the following, I found that some of what I learned from other sources were indeed already explained by Lena, even though not clearly. Still I think it is valuable having everything explained in one place from top to bottom.

So, the idea is to explain IAT rebuilding and, in general, how to manage dynamic linking when packers/protectors try to make it difficult. I will refresh concept like dynamic and static linking, the PE header structure, and packing and dumping…I just hope you are not hearing them from here first.

This will be a series of 6 posts loosely based on the 5 stages of grief, plus an introductory one (you are reading it right now)

When IAT rebuilding is needed

Let’s refresh what is packing and what is meant with “dumping”:

  • Packing: the process of compressing or encrypting a PE file’s code, data, and resources in order to reduce its size, obfuscate its content, or make it more difficult for reverse engineers to analyze. The packed file includes a small decompression or decryption routine (unpacker) that unpacks the original code into memory at runtime.
  • Dumping: extracting the original, unpacked code from memory after the packed file has been executed and unpacked at runtime. This is done to retrieve the original PE file for analysis, typically bypassing the protection mechanisms imposed by the packer.

TL;DR, sometimes we want to strip the packer from the executable and have the running program without all the packer’s hussle. Among the various tricks a packer can use to make dumping harder, one is messing with imports.

Before the analysis on how a packer can destroy the imports and interfere with dumping, we need to understand what happens when a non-packed executable is loaded in memory and how its imports are managed by the Windows Loader.

A problem to solve: function imports

When we write a program more complex than a simple hello world, we are bound to use functions written by other people. These functions can be found in libraries and in Windows those libraries are probably defined as DLLs: libraries that contains code and data that can be used by more than one program by virtue of dynamic linking. Moroever, to access the functionalities provided by Windows we need its APIs, provided themselves as DLLs. But let’s focus on how this is translated in the assembly of an ideal program.

I have my code, all good and dandy, but at a certain point I need to call for a function. This function is present in a DLL. In the high level code this is described with a import line of some sort that calls a name, but in assembly we need the specific address of the function (by “in assembly” i mean in the real low level representation of the program). The specific address cannot be know at compile time. This is because we do not see the address space of the DLL. We do not see any address space really because the program is not yet loaed. The advantages of dynamic linking instead of static linking (aka having all in the executable from the start) are various. For example, you can change the DLL content and maintain only the API or use the same DLL for different loaded programs

Note: if you put the code of a library inside your PE file, that library will be statically linked and not dynamically linked. Linking will be made at compile time and everything will be intuitive and inefficient or (sometimes, as for Windows API code) outright impossible.

So how can we use DLLs code if we do not know where it is? Here the Windows Loader comes helping. We write in our code which DLLs and function we need for our program and the Windows Loader arranges the memory so that the DLLs code will be mapped in the virtual space of the running process and that the functions will be called correctly.

PE Header and Windows Loader

The information of what DLLs and function are needed for the program to run is found in the PE header and is put there by the compiler at compile time. The PE header is rather interesting and studying it is always great fun, but today we care about one field in particular. The Import Table field found in the Optional Header (if you need a refresher on the PE header and the “not so optional” header go here, if you just need a visual reference go here or check the images below). If you are an acute observer you might notice that there is another field in the PE header that might interest us, the Import Address Table, found again in the Optional Header. Keep it in mind but let’s focus on the Import field first. This field contains a pointer (or better, an offset to use together with the base pointer) to the Import Table, which funnily enough is not the IAT and the subject of this post.

Note on the Import Address Table address found in the PE Optional Header: This field is largely undocumented. It’s not clear to me and apparently to someone else too what is the purpose of this field. There are ideas sure, but no one seems certain. Probably something about the binding process? It’s not very clear. So, please ignore it…as far as I understand everyone agrees that the loader generally does not use it or at least we should not care. Not Ideal I know but it is what it is, I’ll maybe update this post if I find something convincing.

Windows Loader Import Basics

Now we can explain in detail how the Windows Loader operates and how it uses the previously discussed fields to load the process with its needed functions. Please reference the Figure.

First, it reads the import field in the optional header. This field points to the Import Table (NOT the IAT). This table contains so called “import descriptors”. Import descriptors are made of 5 DWORDS plus an empty one. They come in a list and the list is ended by a whole 5 empty DWORDs. Each import descriptors has 5 fields, one for each DWORD. We care only about 3 of them, the others are used for binding which is an interesting process available to speed up dll dynamic linking. Check out the old new thing by Raymond Chen for that. So we care about the first field, called “original thunk”, the fourth field, called “Dll name” and the last field called just “thunk”. The Dll name field is straight forward and represent the name of the Dll. The first field points to a structure we can call “Hint array” while the last field points to our IAT. The Hint array and the IAT contain pointers to the same things: the real names of the functions or the cardinal number of the function inside its Dll. And all of this is what is in memory without the loader touching it. What the loader does is one simple things: it modifies the IAT and instead of making it point to names and cardinal numbers, it makes it point to the real loaded address of that function in the loaded Dll.

Note: why 2 things that point to the same thing? Again, binding. To make it short, to make binding work we need a way to remember the IAT before loading and binding modifies the IAT so we need another way to preserve it.

The loader simply takes the name or the cardinals and after having mapped the Dll puts the correct address of each function. That’s it.

In reality there is another optimization that is done. In the code section it’s usually present a jump table where all the function calls arrive. This is just a way to make it easier for the loader to change addresses in case of changes of the IAT (it just needs to change an address instead of however many calls to that function have been made).