Reverse Engineering

Dynamic IAT Resolution and Import Hashing in Obfuscated Binaries

On this page

Dynamic IAT resolution (import hashing) is an anti-analysis technique where a binary hides which OS APIs it uses by resolving them at runtime from numeric hashes instead of listing them in a normal Import Address Table. Combined with encrypted strings, it leaves a binary whose imports view is nearly empty and whose strings view is meaningless - a deliberate roadblock for static reverse-engineering. Recovering the real behaviour means decrypting the strings, then walking each DLL's Export Address Table (EAT) to match the binary's hashes back to real function names. This is a standard defensive malware-analysis skill; the public case study here is the REvil ransomware sample reversed in birk.blog's "Reversing REvil" Part 1, analyzed purely for education.

Quick facts

SymptomNear-empty imports view + calls into "data" addresses holding invalid-looking constants
StringsNo valid strings - typically RC4-encrypted arrays decrypted on demand
Import hashingEach API stored as a hash (e.g. seed 0x2B, h = h*0x10F + byte) resolved at runtime
ResolutionHash -> DLL DOS header -> Export Address Table walk -> function address
ToolingBinary Ninja MLIL + scripted annotation; rebuild the IAT as a struct

Two roadblocks: encrypted strings and a missing IAT

Loading an obfuscated binary, two tells appear immediately. The imports view is sparse and functions "call into data" - e.g. a call to an address holding a constant like 0x42a2897c rather than a named import - which signals a dynamic IAT. And the strings view has nothing meaningful, implying encrypted strings. The strings come first: tracing from the entry point to a decryptor reveals a wrapper around an RC4 routine that decrypts arrays of data given a base pointer, a key index, a key length, and a data length. Because every call references the same encrypted blob with different arguments, you can script the recovery: enumerate cross-references to the decrypt function, read each call's constant arguments, RC4-decrypt, and annotate the call site with the plaintext.

Resolving the import hashes

With strings readable, DLL names (kernel32.dll, winhttp.dll, crypt32.dll...) surface, used by the import resolver. Each import is stored as a hash, not a name. The resolver formats the hash, derives a DLL hash to locate that module's DOS header, then walks its Export Address Table - the list of exported function names - hashing each export name with the same function until it matches, and returns that function's address. To rebuild the table, you reimplement the two small hash functions (here a seed of 0x2B and multiplier 0x10F over the name bytes, plus a formatting step), brute-force every entry in the binary's hash array against real DLL exports, and emit a struct of resolved names you apply over the IAT region. The empty void* slots become CreateFileW, WinHttpConnect, CryptAcquireContextW, GetKeyboardLayoutList, and so on.

Why this matters beyond malware

Once the imports are named, the binary's intent becomes legible from its API surface alone - file enumeration, crypto, WinHTTP networking, service control, keyboard-layout checks - which is the whole point of recovering it. Import hashing and string encryption are not malware-only tricks; the same anti-analysis ideas appear in commercial packers, DRM, and aggressive software protection, so the technique generalises to any heavily-obfuscated binary. It is the native-code cousin of the bytecode virtualization and deobfuscation work that hides logic in scripting languages: in both cases the analyst restores a readable view by recovering the layer the protector removed - here the import table, there the instruction set. Analysing samples like this defensively is how detection and mitigation get built.

Code example

cpp
// The binary stores APIs as hashes, not names. Reproduce the hash...
uint32_t import_hash(const char* name) {
    uint32_t h = 0x2B;
    while (*name) { h = h * 0x10F + (uint8_t)*name; ++name; }
    return h;
}

// ...then walk each DLL's Export Address Table and match by hash:
//   1. LoadLibraryExA(dll, DONT_RESOLVE_DLL_REFERENCES)
//   2. parse IMAGE_EXPORT_DIRECTORY -> AddressOfNames
//   3. for each export name: if ((import_hash(name) & 0x1FFFFF) == target) -> resolved
// Emit a struct you apply over the IAT region (0x41fc88) in the disassembler:
struct mw_IAT { void* CreateFileW; void* WinHttpConnect; void* GetKeyboardLayoutList; /* ... */ };

// Strings are RC4 arrays: decrypt(base, key_index, key_len, data_len) and annotate the call.

Related terms

Concept map

How Dynamic IAT Resolution (Import Hashing) connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Reverse Engineering
Building map…

Frequently asked questions

What is a dynamic IAT, and how do you spot one?

A dynamic Import Address Table is built at runtime instead of being listed in the binary's headers, so the imports view looks nearly empty. Tell-tale signs are calls that target data addresses holding invalid-looking constants (the stored hashes) and a loop that resolves those constants into function pointers before use. The constants are API hashes the binary maps to addresses at runtime.

How do you recover function names from import hashes?

You reimplement the binary's hashing function, then for each DLL it uses, walk the Export Address Table and hash every exported name until one matches the stored value - that export is the resolved import. Brute-forcing the whole hash array against real DLL exports lets you rebuild the import table as a struct and apply it over the binary in your disassembler.

Is import hashing only used by malware?

No. While it is common in malware as an anti-analysis measure, the same import-hashing and string-encryption techniques appear in commercial packers, DRM, and software protection. The recovery workflow - decrypt strings, then resolve imports by walking export tables - generalises to any heavily obfuscated native binary, which is why it is a core defensive reverse-engineering skill.

Last updated: 2026-02-16