Skip to content

Delay-Load Import Directory

Data directory 13

IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: DataDirectory[13] in the Optional header. All data directories

Delay-loaded DLLs aren't mapped when the process starts. The linker (/DELAYLOAD:x.dll) generates a small helper (__delayLoadHelper2), and the first call to one of the DLL's functions loads the DLL and patches that IAT slot. Two consequences matter for analysis:

  1. Capabilities hide here. Tools and analysts that only read the normal import table miss them. A binary can look harmless in its static imports while delay-loading WININET.dll or CRYPT32.dll.
  2. A missing delay-loaded DLL doesn't stop the program from starting. It only fails when the code path runs. If the DLL doesn't exist on the system, anyone who can write to a directory in the search order can supply it: phantom DLL hijacking.

How the structure works

ImgDelayDescr (one per DLL, zero-terminated):

Field Meaning
Attributes 1 = the fields below are RVAs (all modern linkers). 0 = old VA-based format
DllNameRVA The DLL name
ModuleHandleRVA Where the helper caches the HMODULE
ImportAddressTableRVA (IAT) Slots that initially point at the helper thunk, then at the real function
ImportNameTableRVA (INT) Hint/name or ordinal entries, like the normal import OFT
BoundImportAddressTableRVA Optional pre-bound addresses
UnloadInformationTableRVA Copy of the original IAT, used by __FUnloadDelayLoadedDLL2
TimeDateStamp Bound DLL timestamp, or 0

What PPEE shows

  • Upper list, one row per DLL: Attributes · Dll Name Addr · Dll Name · Imported functions (count with a share bar) · HMODULE Addr · IAT · INT · Bound IAT · Unload IAT · TimeDateStamp · Description (read from file).
  • Lower list: the functions of the selected DLL: OFT · Hint · Name · Demangled name · Ordinal.
  • On Windows, DLLs not found in the System or Windows directories are shown in the warning color. For a delay-loaded DLL that is a direct pointer to a potential phantom-DLL hijack.
  • Follow in Hex View (Ctrl+H) and editing work on every cell.

Windows screenshot: Delay imports of a ransomware sample: five delay-loaded DLLs, WININET.dll selected with its HTTP and FTP functions and their call sites

Delay imports of a ransomware sample: five delay-loaded DLLs, WININET.dll selected with its HTTP and FTP functions and their call sites

A ransomware sample (68b0e193….exe) delay-loads WININET.dll: InternetOpenW, HttpOpenRequestW, HttpSendRequestW, FtpOpenFileW and more. None of them are in its normal import table (14 DLLs, no WININET), so its network capability appears only here. The Call sites column (from the background code scan) shows that the code really calls them. msi.dll, imported by ordinal, hints at an installer-based builder.

Reading delay-load imports like an analyst

Observation What it suggests
Network, crypto or injection APIs only in delay imports The interesting capability is deferred. Include delay imports in every triage
Delay-loaded DLL that doesn't exist on a standard Windows install Phantom-DLL hijack opportunity (for red teams) or a sideloading target (for defenders to monitor)
A signed, trusted program delay-loading a DLL by a bare name Search-order hijacking candidate when the program runs from a user-writable folder
Attributes = 0 Old VA-based descriptor, from a very old linker or crafted by hand
IAT slots that don't point at the helper thunk on disk Patched or tampered IAT

Real sample: explorer.exe

explorer.exe delay-loads 60 modules. Among them:

WINTRUST.dll!WTGetSignatureInfo
CRYPTSP.dll!CryptAcquireContextW  CRYPTSP.dll!CryptCreateHash  CRYPTSP.dll!CryptHashData
SndVolSSO.DLL!#1  #2  #3  #4        (by ordinal only)
None of these crypto or signature APIs appear in its normal import table, which is why triage must include delay imports.

CLI and JSON

$ ppee-cli --delay-imports explorer.exe
Delay-load imports (60 module(s)):
  SndVolSSO.DLL (attrs=1) - 4 function(s)
    Ordinal #1
  WINTRUST.dll (attrs=1) - 1 function(s)
    WTGetSignatureInfo (hint=91)

JSON: delayImports[] → name, attributes, timeDateStamp, functions[] (hint + name, or ordinal).

Hunting recipes

# Complete capability picture: static + delay-loaded, tagged
ppee-cli --json --imports --delay-imports f.exe | jq -r '
  (.imports[]      | .name as $m | .functions[] | "static\t\($m)!\(.name // "#\(.ordinal)")"),
  (.delayImports[] | .name as $m | .functions[] | "delay\t\($m)!\(.name // "#\(.ordinal)")")'

# APIs that appear ONLY as delay imports
ppee-cli --json --imports --delay-imports f.exe | jq -r '
  ([.imports[].functions[].name // empty]) as $s
  | [.delayImports[].functions[].name // empty] - $s | .[]'

# Phantom-DLL candidates: delay-loaded DLLs missing from a reference System32 listing
ls /mnt/win/Windows/System32 | tr 'A-Z' 'a-z' > system32.txt
ppee-cli --json --delay-imports f.exe | jq -r '.delayImports[].name | ascii_downcase' \
  | grep -v -e '^api-ms-' -e '^ext-ms-' | grep -vxF -f system32.txt

Related: --delay-imports · Import directory · Load Config (protected delay-load IAT)

References