Recently I was stuck with a blocking issue that was putting a feature I worked on in jeopardy. To summarize the problem, the entire feature relied on a specific way a library is initialized, the library in question providing two different functions (that we will call Init and InitExtended) to initialize itself.

The code responsible for this initialization hardcodes calling one of the variant and is in a DLL that is not modifiable. To make things harder, the two initialization functions I mentioned have slightly different parameters which complicated things as it can’t be a simple function pointer swap.

For exploitation purposes, an important aspect is that the initialization function is not actually referenced directly in the DLL but instead it is located dynamically using the standard LoadLibrary/GetProcAddress combo.

When something is using LoadLibrary, the obvious thing to try first is to take advantage of the DLL loader lookup mechanism and provide a modified DLL that can be loaded instead of the original one. In this case though, LoadLibrary was used with a hardcoded path in a system directory which made this option impossible.

Which leaves trying to use GetProcAddress to masquerade things up. To do this there is a more exotic technique called IAT patching that relies on the way the PE (Portable Executable) format work for dynamic libraries.

What’s in a DLL

Like most other dynamically loadable libraries format, a DLL contains more than just the code that’s executed. The file is split between different sections that references several other type of data in addition to the code section.

PE format diagram

For instance the data section will contain a bunch of interesting tables, things like strings literals, the constant data in the program, thread local storage and so on.

When a DLL is loaded, those tables and sections are mapped into the calling process memory space and are accessible from user code although in practice you will always be using some system API instead of querying those parts of the memory yourself.

The Import Table

The table we are going to be interested in here is the Import Address Table (abbreviated IAT). The role of this table is to list all the external functions a DLL references from other libraries in a dictionary form keyed by library name (e.g. “user32.dll”) and values as function pointer thunks.

For instance, here is a snippet of the content of this table from a random DLL (the hint/ordinal values are used by the loader for faster processing):

$ pedump -I freetype.dll 

=== IMPORTS ===

MODULE_NAME      HINT   ORD  FUNCTION_NAME
KERNEL32.dll      1cb        GetCurrentThreadId
KERNEL32.dll      15b        FlsSetValue
KERNEL32.dll      18c        GetCommandLineA
KERNEL32.dll      425        RtlUnwindEx
KERNEL32.dll      2d3        HeapAlloc
KERNEL32.dll      208        GetLastError
KERNEL32.dll      2da        HeapReAlloc
KERNEL32.dll      2d7        HeapFree
KERNEL32.dll       ee        EncodePointer
KERNEL32.dll      15a        FlsGetValue
KERNEL32.dll      159        FlsFree
KERNEL32.dll      480        SetLastError
KERNEL32.dll      158        FlsAlloc
KERNEL32.dll      4c0        Sleep

In our case, both of LoadLibrary and GetProcAddress would be in this table keyed under the library they come from aka kernel32.dll.

The reason for this table is explained by the way dynamic libraries and the Windows DLL loader works. When compiling code to a DLL that depends on some other libraries, the actual memory layout where the dependencies are going to be loaded is not known in advance. Maybe that dependency has already been loaded by the program, maybe the DLL will be the one to load it, in any case the real locations for all those functions will ultimately be determined by the running context.

Thus, when some code references an external function, what the compiler will do is insert a call instruction that is not pointing directly to the real address of the function but instead will load the address pointed to by the thunk I mentioned earlier from the Import Address Table.

By doing so, the code contained inside the DLL is always valid and the Windows loader simply has to modify one piece of the overall loaded DLL (the IAT thunk) to point to the right function address at runtime instead of going throughout the entire code section and patch one-by-one every call instructions.

Below is an example of how the same DLL we saw earlier is calling the GetCommandLineA Win32 function contained in kernel32. Notice how the address for the call instruction is retrieved (qword) from a location in the data segment (ds) and how the disassembler is smart enough to name the thunk with a imp_ prefix:

Indirect IAT calling

And here is a view of the actual array containing all the thunks for kernel32 functions that will be patched by the Windows loader when the DLL is processed at run-time:

IAT thunks

Patching the Import Address Table

Reminder: we are trying to influence a program execution by manipulating GetProcAddress so that instead of calling an Init function it calls a different InitExtended function.

Now that we know how external functions are called it’s easy to see that taking control of this IAT indirection is the key to do what we want. By patching the IAT thunk for the function we want to modify, we ensure the relevant code will be redirected to use our modifications.

Provided you have already the ability to load custom code in the process via another mean (if not lookup DLL injection techniques), the recipe to hijack a IAT thunk is as follow:

  • Create your custom function in your own code that mimics the prototype of the real one
  • Prevent or delay the codepath you are trying to change from accessing the function before your patching is done
  • Retrieve a pointer to the module (containing all the tables) of the DLL/executable that you are trying to hijack
  • Locate the Import Address Table
  • Iterate over it to find the right thunk
  • Overwrite the value given by the DLL loader with your own custom function address

Below is the translated and annotated version of this recipe in C using the imagehlp library (that defines all the right C structs to modelize the IAT table layout):

// Our custom Init function that will now internally
// call the InitExtended function we wanted to use
static void MyCustomInitFunction (/* parameters */)
{
  InitExtended (/* other parameters */);
}

// Our hooked version of GetProcAddress with the
// same prototype (and same calling convention)
static FARPROC MyGetProcAddress (HMODULE hModule, LPCSTR lpProcName)
{
  if (strcmp (lpProcName, "Init") == 0)
    return (FARPROC)MyCustomInitFunction;
  return GetProcAddress (hModule, lpProcName);
}

static BOOL HackGetProcAddress ()
{
  // Get the handle to the module we want to divert 
  HMODULE module = GetModuleHandle (L"TheDllToHijack.dll");
  if (module == NULL)
    return FALSE;

  // Get a reference to the import table to locate the kernel32 entry
  ULONG size;
  PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx (module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size, NULL);

  // In the import table find the entry that corresponds to kernel32
  BOOL found = FALSE;
  while (importDescriptor->Characteristics && importDescriptor->Name) {
    PSTR importName = (PSTR)((PBYTE)module + importDescriptor->Name);
    if (_stricmp (importName, "kernel32.dll") == 0) {
      found = TRUE;
      break;
    }
    importDescriptor++;
  }
  if (!found)
    return FALSE;

  // We use this value as a comparison
  PROC baseGetProcAddress = (PROC)GetProcAddress (GetModuleHandle (L"kernel32.dll"), "GetProcAddress");

  // From the kernel32 import descriptor, go over its IAT thunks to
  // find the one used by the rest of the code to call GetProcAddress
  PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((PBYTE)module + importDescriptor->FirstThunk);
  while (thunk->u1.Function) {
    PROC* funcStorage = (PROC*)&thunk->u1.Function;
    // Found it, now let's patch it
    if (*funcStorage == baseGetProcAddress) {
      // Get the memory page where the info is stored
      MEMORY_BASIC_INFORMATION mbi;
      VirtualQuery (funcStorage, &mbi, sizeof (MEMORY_BASIC_INFORMATION));

      // Try to change the page to be writable if it's not already
      if (!VirtualProtect (mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect))
        return FALSE;

      // Store our hook
      *funcStorage = (PROC)MyGetProcAddress;

      // Restore the old flag on the page
      DWORD dwOldProtect;
      VirtualProtect (mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);

      // Profit
      return TRUE;
    }

    thunk++;
  }

  return FALSE;
}

Conclusion

This was an example of how you can use IAT patching to circumvent legacy code without having to recompile or otherwise damage the original binary.

This technique is well known so if you want to do something more complicated/reliable you can always use an existing library like EasyHook or something as full-featured as Microsoft’s Detours.

References: