Anatomy of IAT and EAT Hooking

Nikhil gupta
7 min readAug 20, 2024

--

Import Address Table (IAT) and Export Address Table (EAT) hooking are techniques used to modify the flow of execution in a Windows process. These methods are often employed by malware to intercept and alter API calls, though they also have legitimate uses in debugging, instrumentation, and security tools.

IAT Hooking:

  • The Import Address Table is used by Windows processes to store the addresses of functions that are imported from external DLLs.
  • IAT hooking involves modifying the function pointers in the IAT so that they point to a different function (often one that is malicious or used to monitor the function calls).
  • This technique is applicable when you have access to the process memory, and it is usually done early during the process initialization when the imports are being resolved.

How Is Importing Done?

Import Address Table (IAT): When a program or DLL needs to use functions from an external DLL, it declares them in its Import Address Table (IAT). The IAT lists the functions the program will use and the DLLs that provide these functions.

Linking:

  • Static Linking: Occurs during the linking stage of the application’s build process. The linker embeds information in the executable about which DLLs it needs and which functions it will use.
  • Dynamic Linking: Occurs at runtime when the program is loaded. The Windows loader resolves the function addresses from the external DLLs and populates the IAT with the correct addresses.

LoadLibrary and GetProcAddress: Functions like LoadLibrary and GetProcAddress allow dynamic loading and importing of functions during runtime.

What Functions Are Imported?

An application imports functions it needs to use that are not part of its own codebase. For instance:

  • From kernel32.dll: Commonly imported functions include WriteFile, ReadFile, CreateFile, etc.
  • From user32.dll: Functions like MessageBox, CreateWindow, ShowWindow.
  • From gdi32.dll: Functions like CreateFont, BitBlt, SetPixel.

The specific functions imported depend on what functionality the application requires.

Where Are Functions Imported From?

Functions are imported from the DLLs that export them. The Windows operating system maintains a standard set of DLLs that are always available (like kernel32.dll, user32.dll, etc.), and applications rely on these DLLs for core functionality.

EAT Hooking:

  • The Export Address Table contains the addresses of functions that a DLL exports, allowing other modules to call these functions.
  • EAT hooking modifies the function pointers in the EAT to point to a different address. This is more challenging than IAT hooking as it requires modifying the DLL itself or the process that loaded the DLL.

What is Function Exporting?

  • Function exporting in the context of a DLL (Dynamic Link Library) like kernel32.dll refers to making specific functions available for other programs or modules to call. When a function is exported, it can be invoked by any other process that links to the DLL.

How Does kernel32.dll Export Functions?

  • Export Address Table (EAT): kernel32.dll, like any other DLL, has an Export Address Table (EAT), which contains the addresses of the functions it exports.
  • Export by Name and Ordinal: Functions can be exported by name (e.g., WriteFile) or by ordinal (a number that acts as an index into the EAT). Most functions are exported by name, but some might be exported by ordinal for efficiency.
  • PE (Portable Executable) Format: The export table is part of the PE file format of the DLL, specifically within the .edata section. This section contains metadata about exported functions, including their names, ordinals, and memory addresses.

What Functions Are Exported?

  • kernel32.dll exports a wide range of essential system functions, such as:
  • File I/O Functions: WriteFile, ReadFile, CreateFile.
  • Memory Management: VirtualAlloc, VirtualFree, HeapAlloc.
  • Process and Thread Management: CreateProcess, CreateThread, TerminateProcess.
  • Synchronization: EnterCriticalSection, LeaveCriticalSection, Sleep.
  • The list of exported functions can be viewed using tools like Dependency Walker, CFF Explorer, or dumpbin.

Where Are Functions Exported From?

Functions are typically exported from specific sections within the DLL where they are defined and implemented. In kernel32.dll, these functions are implemented in the source code of the DLL, compiled, and then made available for export through the EAT.

IAT Hooking Code Example

#include <windows.h>
#include <stdio.h>

// Function pointer typedef for the original WriteFile function
typedef BOOL (WINAPI *pWriteFile)(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);

// Global variable to store the original WriteFile function address
pWriteFile OriginalWriteFile = NULL;

// Custom WriteFile function
BOOL WINAPI MyWriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
) {
// Custom logic: logging the call or modifying the buffer before writing
printf("Intercepted WriteFile call\n");

// Call the original WriteFile function
return OriginalWriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}

// Function to perform IAT hooking
void HookIAT() {
// Get the base address of the current module (EXE)
HMODULE hModule = GetModuleHandle(NULL);
if (hModule == NULL) {
printf("Failed to get module handle\n");
return;
}

// Get the address of the IMAGE_DOS_HEADER (the base of the PE file)
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)hModule;

// Get the address of the IMAGE_NT_HEADERS
PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDOSHeader->e_lfanew);

// Get the address of the IMAGE_IMPORT_DESCRIPTOR (the IAT)
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(
(BYTE*)hModule + pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
);

// Iterate through the import descriptors to find kernel32.dll
while (pImportDescriptor->Name) {
char* moduleName = (char*)((BYTE*)hModule + pImportDescriptor->Name);
if (_stricmp(moduleName, "kernel32.dll") == 0) {
// Found kernel32.dll, now locate the IAT for WriteFile
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pImportDescriptor->FirstThunk);

while (pThunk->u1.Function) {
FARPROC* pFunction = (FARPROC*)&pThunk->u1.Function;

// Get the function name
if (*pFunction == (FARPROC)GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteFile")) {
// Found WriteFile, save the original function address
OriginalWriteFile = (pWriteFile)*pFunction;

// Change memory protection to allow writing to the IAT
DWORD oldProtect;
VirtualProtect(pFunction, sizeof(FARPROC), PAGE_EXECUTE_READWRITE, &oldProtect);

// Replace the IAT entry with our custom function
*pFunction = (FARPROC)MyWriteFile;

// Restore the original protection
VirtualProtect(pFunction, sizeof(FARPROC), oldProtect, &oldProtect);

printf("IAT hooking successful\n");
return;
}

pThunk++;
}
}

pImportDescriptor++;
}

printf("Failed to find WriteFile in the IAT\n");
}

int main() {
// Perform the IAT hooking
HookIAT();

// Test the hook by calling WriteFile
HANDLE hFile = CreateFile("test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
DWORD written;
const char* data = "Hello, world!";
WriteFile(hFile, data, strlen(data), &written, NULL);
CloseHandle(hFile);
}

return 0;
}

EAT Hooking Code Example

#include <windows.h>
#include <stdio.h>

// Function pointer typedef for the original WriteFile function
typedef BOOL (WINAPI *pWriteFile)(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);

// Global variable to store the original WriteFile function address
pWriteFile OriginalWriteFile = NULL;

// Custom WriteFile function
BOOL WINAPI MyWriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
) {
// Custom logic: logging the call or modifying the buffer before writing
printf("Intercepted WriteFile call\n");

// Call the original WriteFile function
return OriginalWriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}

// Function to perform EAT hooking
void HookEAT() {
// Get the base address of kernel32.dll
HMODULE hModule = GetModuleHandle("kernel32.dll");
if (hModule == NULL) {
printf("Failed to get module handle for kernel32.dll\n");
return;
}

// Get the address of the IMAGE_DOS_HEADER (the base of the PE file)
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)hModule;

// Get the address of the IMAGE_NT_HEADERS
PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDOSHeader->e_lfanew);

// Get the address of the IMAGE_EXPORT_DIRECTORY (the EAT)
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(
(BYTE*)hModule + pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
);

// Get the list of function names and addresses
DWORD* pNames = (DWORD*)((BYTE*)hModule + pExportDirectory->AddressOfNames);
DWORD* pFunctions = (DWORD*)((BYTE*)hModule + pExportDirectory->AddressOfFunctions);
WORD* pOrdinals = (WORD*)((BYTE*)hModule + pExportDirectory->AddressOfNameOrdinals);

// Iterate through the function names to find WriteFile
for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++) {
char* functionName = (char*)((BYTE*)hModule + pNames[i]);
if (strcmp(functionName, "WriteFile") == 0) {
// Found WriteFile, now get the function's address
DWORD functionRVA = pFunctions[pOrdinals[i]];
OriginalWriteFile = (pWriteFile)((BYTE*)hModule + functionRVA);

// Modify the EAT to point to our custom function
DWORD oldProtect;
VirtualProtect(&pFunctions[pOrdinals[i]], sizeof(DWORD), PAGE_EXECUTE_READWRITE, &oldProtect);
pFunctions[pOrdinals[i]] = (DWORD)((BYTE*)MyWriteFile - (BYTE*)hModule);
VirtualProtect(&pFunctions[pOrdinals[i]], sizeof(DWORD), oldProtect, &oldProtect);

printf("EAT hooking successful\n");
return;
}
}

printf("Failed to find WriteFile in the EAT\n");
}

int main() {
// Perform the EAT hooking
HookEAT();

// Test the hook by calling WriteFile
HANDLE hFile = CreateFile("test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
DWORD written;
const char* data = "Hello, world!";
WriteFile(hFile, data, strlen(data), &written, NULL);
CloseHandle(hFile);
}

return 0;
}

Explanation of the Code:

  1. pWriteFile Type Definition: This defines the function pointer type pWriteFile that matches the signature of the WriteFile function. This allows us to store the address of the original WriteFile function.
  2. OriginalWriteFile Global Variable: This variable will hold the address of the original WriteFile function after we hook it.
  3. MyWriteFile Custom Function: This is our custom implementation of WriteFile. It will be called instead of the original WriteFile function. You can log, modify, or intercept the data before passing it to the original function.
  4. HookIAT Function:
  • Get the Base Address of the Module: The GetModuleHandle(NULL) function retrieves the base address of the current module (the executable).
  • Locate the IMAGE_DOS_HEADER: This is the start of the PE (Portable Executable) file.
  • Locate the IMAGE_NT_HEADERS: This structure contains the PE header.
  • Locate the Import Directory: The IMAGE_IMPORT_DESCRIPTOR structure contains information about the DLLs that the executable imports.
  • Iterate Through the Import Descriptors: The code loops through the import descriptors to find kernel32.dll.
  • Find the WriteFile Function: The code then iterates through the import address table (IAT) to find the entry for WriteFile.
  • Hook the Function: If WriteFile is found, its address is saved in OriginalWriteFile, and the IAT entry is modified to point to MyWriteFile.
  • Memory Protection: VirtualProtect is used to change the memory protection of the IAT so that it can be modified, and then it is restored to its original protection.

5. main Function:

  • The HookIAT function is called to apply the hook.
  • The WriteFile function is then called, which will now execute MyWriteFile instead of the original WriteFile.

#IAT #EAT #Hooking #ReverseEngineering

--

--

Nikhil gupta
Nikhil gupta

Written by Nikhil gupta

Incident Response, Threat Hunting, and Reverse Engineering professional, writing things to learn them better. https://www.linkedin.com/in/nikhilnow/

No responses yet