Since each IMAGE_IMPORT_DESCRIPTOR already points to a RVA for the resolved virtual addresses the IAT seems to be redundant. Why is this data directory included? If I overwrite the RVA/size for the IAT data directory the binary is still able to run properly.

Am I missing something? Is there a scenario where having the IAT data directory is crucial to the running of a binary?

  • I haven't worked with the PE format in a long time. But if I remember correctly, the IAT directory holds the RVA to the import address table. This table contains the address of each imported function. It may be filled with the RVA of the function assuming the DLL is loaded at its preferred address. But the system may have to load the DLL at a different address if two DLL have overlapping virtual address, in this case it will relocate it it and the addresses in the IAT will be wrong. The PE loaded takes care of overwriting the IAT on load in this case I think. Nov 28 '17 at 13:31
  • please refrain from answering in comments, thanks.
    – Igor Skochinsky
    Nov 30 '17 at 13:32

First, a couple of clarifications

The Import Address Table is a table that only consists of the imported function's addresses. Unlike the Import Table (which consists of IMAGE_IMPORT_DESCRIPTORs) that also details where to lookup those imports.

To reiterate: (this is slightly oversimplified) The Import Address Table is an array of the imported functions' actual addresses in memory in the current process. Not divided to modules, no extra information like the ordinal or name. Just an array of the addresses of all imported functions.

The IMAGE_IMPORT_DESCRIPTORS are way more structured, clean and partitioned. They are what enables the loader to find the function it's looking for. The "RVAs of the resolved virtual address" as you called them (There are actually two, Characteristics and FirstThunk, in the IMAGE_IMPORT_DESCRIPTOR structure) is not pointing to the actual imported function in the remote DLL, but to the location in the Import Address Table of the first item in that IMAGE_IMPORT_DESCRIPTOR. Subsequent items in the same IMAGE_IMPORT_DESCRIPTOR are also the following items in the Import Address Table.

As the loader goes over the IMPORT_TABLE and the different IMAGE_IMPORT_DESCRIPTORs, it resolves each function and places that function's final address in the function's designated Import Address Table location. FirstThunk is how the loader knows the Import Address Table location of a function described in IMAGE_IMPORT_DESCRIPTORs.

but, why? (AKA answering your questions)

So as you can see, the FirstThunk only holds one address for multiple imported functions, and it only points to the address (and not holding it directly as your question implies), so we can't easily "use the thunk" instead of the Import Address Table.

As to why you can overwrite the actual content in the IAT for a PE on disk and everything will still work - that was answered implicitly but I'll answer it explicitly as well. The data on disk is the same data as in the Characteristics structure, the pointers are the same. However that data should not ever be actually read according to Microsoft's own documentation, the IAT is mostly made to be populated during the PE loading process. I would guess the reason it is initialized with the relevant data was kind of a thought of "why the hell not" by the developers.

but still, why?

Now, if you've been following, you should be asking a similar question (and you were, that's what this question is still roughly about):

That structure is still more complicated than it could've been, and the main reason the Import Address Table was created in a condensed manner (instead of being included as addresses in the different IMPORT_NAME or other similar structures) is actually an optimization.

I'm glad you asked! ;) I'll explain.

As you may know, DLLs are efficiently loaded into the same addresses and use a windows mechanism known as Copy On Write to only actually have a single copy of most Modules. This is also true when Modules are mapped to different Virtual Addresses. However, when the need to populate imported functions for a Module, writes to PE structures are required. No matter where you hold the address, it's gotta be changed. The Import Address Table is there to to only trigger Copy On Write for as few pages as possible (those being the actual Import Address Table pages themselves).

This is, by the way, partially the reason there's that extra level of indirection in the PE to begin with.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.