Skip to content

Bound Import Directory

Data directory 11

IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: DataDirectory[11] in the Optional header. All data directories

Binding is a legacy load-time optimization: a tool (bind.exe, or the Windows setup process) resolves the imports in advance and writes the real function addresses into the IAT on disk. The bound import directory records which DLL versions those addresses came from, identified by their timestamps. At load time, if every listed DLL still has the same timestamp and loads at its preferred base, the loader skips resolution. Otherwise it resolves the imports normally.

ASLR made binding mostly useless, so it's rare in modern software. When it's there, it's a useful dating and provenance clue.

How the structure works

IMAGE_BOUND_IMPORT_DESCRIPTOR  { TimeDateStamp, OffsetModuleName, NumberOfModuleForwarderRefs }
    IMAGE_BOUND_FORWARDER_REF  { TimeDateStamp, OffsetModuleName, Reserved }   × NumberOfModuleForwarderRefs
... next descriptor ...
all-zero terminator
  • OffsetModuleName is relative to the start of the bound import directory, not an RVA.
  • Forwarder refs list DLLs that a bound DLL forwards to, for example KERNEL32.dll → NTDLL.DLL, because those timestamps must match too.
  • The matching import descriptors have TimeDateStamp = FFFFFFFF ("bound, see the bound import directory").
  • The directory normally sits in the header area, after the section table, so it's mapped even though it belongs to no section.

What PPEE shows

  • Upper list, one row per bound DLL: NameOffset · Name · #Forwarders · TimeDateStamp · Comment (the timestamp as a date) · Description.
  • Lower list: the forwarder references of the selected DLL (NameOffset · Name · Reserved · TimeDateStamp · Comment).
  • Tree: DIR_ENTRY_BOUND_IMPORT (n). Follow in Hex View (Ctrl+H) and editing work on every cell.

Windows screenshot: Bound imports of a rogue antivirus: user32, MSVBVM60 and kernel32 with 2008–2009 timestamps, and kernel32's forwarder to NTDLL

Bound imports of a rogue antivirus: user32, MSVBVM60 and kernel32 with 2008–2009 timestamps, and kernel32's forwarder to NTDLL

A Visual Basic 6 rogue antivirus ([email protected]) is bound against user32.dll, MSVBVM60.DLL and kernel32.dll. The Comment column turns each timestamp into a date: April 2008 and March 2009, the Windows XP SP3 era, so it was bound on a machine of that time. Selecting kernel32.dll lists its forwarder reference, NTDLL.DLL, in the lower list.

Reading bound imports like an analyst

Observation What it suggests
Bound DLL timestamps from a specific date range The Windows build the file was bound against: shipped with, or installed on, that version. Useful for dating OS components and old installers
Bound imports on a file that claims to be recent The file was bound on an old system, or its components were taken from old media
Bound imports in a third-party DLL Old build tooling, or a file re-bound on a specific machine (which also changed its IAT bytes, and therefore its hash)
Directory outside the header area or overlapping other data Hand-edited or malformed: a parser-confusion trick
OffsetModuleName pointing outside the directory, or huge forwarder counts Malformed: crafted to crash or mislead analysis tools
Import descriptors with TimeDateStamp = FFFFFFFF but no bound import directory The IAT may contain stale absolute addresses. Treat them as untrusted

Real sample: a Windows component

$ ppee-cli --bound-imports wab.exe
Bound imports (8 module(s)):
  ADVAPI32.dll (TimeDateStamp=4CE7C455) - 0 forwarder(s)
  KERNEL32.dll (TimeDateStamp=4CE7C78B) - 1 forwarder(s)
    -> NTDLL.DLL
  GDI32.dll (TimeDateStamp=4CE7C651) - 0 forwarder(s)
  USER32.dll (TimeDateStamp=4CE7C9F1) - 1 forwarder(s)
    -> NTDLL.DLL
The directory is at RVA 2C0 (size BC), inside the 0x400-byte header area, and every import descriptor has TimeDateStamp = FFFFFFFF. The bound KERNEL32.dll timestamp 4CE7C78B is 2010-11-20, the Windows 7 SP1 build era, so the file was bound against Windows 7 SP1 system DLLs.

CLI and JSON

JSON: boundImports[] → name, timeDateStamp, forwarders[] (name, timeDateStamp).

# Bound DLLs with human-readable dates
ppee-cli --json --bound-imports f.exe | jq -r '.boundImports[] | "\(.name)\t\(.timeDateStamp)"' | while IFS=$'\t' read -r n t; do
  printf '%-16s %s  %s\n' "$n" "$t" "$(date -u -d @$((16#$t)) +%F)"; done

# Is the image bound? (import descriptors marked FFFFFFFF)
ppee-cli --json --imports f.exe | jq '[.imports[].timeDateStamp] | any(. == "FFFFFFFF")'

Related: --bound-imports · Import directory · Headers (timestamps)

References