SuperAPI AI Directory

re-imports

Security & auditing

View repository

Updated 10-15 21:34

About this skill

Import/Export Tables and Library Fingerprints

When to Use / When Not to Use

  • Use: to determine which libraries/APIs a program links; match compiler and library versions by import characteristics; locate suspicious imports (injection/network/crypto APIs); analyze export interfaces of plugins and DLLs
  • Don't use: when you only need the internal logic of a function (use the decompilation skill directly)
  • Don't use: when a packer statically hides imports (dynamically resolved APIs are not in the IAT—combine with [[re-tracing]] / [[re-memdump]] to retrieve them from memory)

Tool Preparation

Refer to [[re-analyze/platform-tips]]—import table analysis is a static step, no sandbox needed; anything involving execution (verifying dynamically resolved APIs) must go into a sandbox per the highest principle.

objdump / readelf(binutils)

  • Linux: apt install binutils / dnf install binutils / pacman -S binutils
  • macOS: brew install binutils(gobjdump); for Mach-O use otool -L + nm -u
  • WSL: Linux version
  • Verify: objdump -V

pefile(Python)

  • All platforms: pip install pefile
  • Verify: python3 -c "import pefile; print(pefile.__version__)"

rizin / rz-bin

  • For the installation matrix see [[re-radare2]]; after installation use rizin -v to verify
  • Windows: the official release works out of the box after extraction; radare2 users can use choco install radare2 for compatibility (commands below)
  • Verify: rz-bin -V

Ghidra FLIRT plugin (ghidra_flirt)

  • All platforms: git clone https://github.com/nneonneo/ghidra_flirt && cd ghidra_flirt && make(requires Ghidra to be installed, see [[re-ghidra]])
  • Verify: the ghidra_flirt extension appears within Ghidra

Operation Steps

  1. List imported functions:

    • PE:
      objdump -p sample.exe | grep 'DLL Name' -A3 | head -30
      
      import pefile
      pe = pefile.PE('sample.exe')
      for e in pe.DIRECTORY_ENTRY_IMPORT:
          print(e.dll, [i.name for i in e.imports if i.name][:15])
      
    • ELF:
      objdump -T sample | grep UND            # 未定义符号 = 导入
      readelf -s sample | grep -i ' UND '
      
    • Mach-O:
      otool -L sample      # 依赖 dylib 列表
      nm -u sample         # 未定义符号 = 导入
      
  2. Match libraries/compilers by import characteristics (FLIRT approach):

    • FLIRT signatures: IDA File > Load file > FLIRT signature file...(comes with a sig directory) automatically annotates library functions; for Ghidra use the ghidra_flirt plugin (Tool Preparation)
    • libc version fingerprint: readelf -s sample | grep -c '' together with strings sample | grep -E 'GLIBC_[0-9.]+'(GLIBC symbol version numbers);strings libc.so.6 | grep -m1 version on the runtime library side
    • Language fingerprint: Go (runtime.main / go1.2x string), Rust (_ZN/__rust_*)、C++(_Z mangled)、Delphi(@System@)、.NET(mscoree + metadata)
    • Rich Header (PE) cross-validates the compiler version, see [[re-format-pe]]
  3. Locate suspicious imports:

    • Process injection category: OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread(kernel32);NtCreateThreadEx(ntdll)
    • Network callback category: WSAStartup/socket/connect/HttpSendRequestA/WinHttpOpen
    • Crypto/data-theft category: CryptEncrypt/CryptDecrypt/BCryptEncrypt/CryptExportKey(export keys)
    • Anti-debugging category: IsDebuggerPresent/NtQueryInformationProcess/OutputDebugStringA(exception-triggered detection)
    • ELF side: ptrace/socket/execve/dlopen/fork; Mach-O side: task_for_pid/_dyld_*
    • Annotate any hits to the analysis notes; the next step is to go to the decompilation skill to locate the call sites
  4. Export table analysis (plugin/service-type samples):

    • PE:
      objdump -p sample.dll | grep -A30 'Export Table'
      
      import pefile
      pe = pefile.PE('sample.dll')
      for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
          print(hex(exp.address), exp.name)
      
    • ELF .so: readelf -s sample.so | grep -v UND | grep FUNC(exported global functions)
    • Mach-O dylib: nm -gU sample.dylib
    • The exported interface names of plugin-type samples reveal functionality (e.g., internal commands of AV/injectors/toolkits)
  5. Verify against system libraries(to determine whether system APIs are mixed in with suspicious parameters):

    strings sample.exe | grep -iE '\.dll|\.so|\.dylib'   # 附加库路径线索
    

Cross-Domain Combination

  • [[re-binary-core]]: fixed invocation at step 4 of the workflow
  • [[re-malware]]: screening for malicious import characteristics (injection/data theft/callback)
  • [[re-cracking]]: find API call sites related to validation/registration
  • Connects with [[re-format-pe]] / [[re-format-elf]] / [[re-format-macho]]; when a packer hides imports, switch to [[re-anti-analysis]] + [[re-memdump]]

Common Pitfalls and Traps

  • The import table is redirected/encrypted by a packer: The statically read IAT is the packer's placeholder/encrypted—the real imports must be retrieved from memory after runtime ([[re-memdump]])
  • Dynamically resolved APIs are not in the IAT:GetProcAddress/dlopen+dlsym resolved only at runtime, not findable in the static import table—combine with [[re-tracing]] to observe
  • String comparison of library versions is more reliable: symbols can be stripped/obfuscated, but Go/OpenSSL version strings (go1.21、OpenSSL 3.0.x) are in the strings
  • Functions imported by ordinal have only numbers and no names—need to compare against Microsoft ordinal tables or runtime library export tables
  • The runtime GOT/IAT may have been rewritten by constructors: symptom—the static import table is clean, but after runtime it calls abnormal functions; cause—malicious constructors (.init_array/TLS callbacks) overwrite GOT/IAT entries before entry (e.g. strcmp@GOT) to install hooks, invisible in the static view; countermeasure—compare import conclusions with the runtime memory view ([[re-memdump]]/[[re-tracing]]); when a sample has initialization callbacks, list the callbacks first before drawing conclusions
  • Family clustering uses imphash rather than import name lists: symptom—variants of the same family differ greatly in import names, and clustering by name fails; cause—import name lists are sensitive to linker version/packing; countermeasure—use pefile's imphash(hash after normalizing imported functions) as a fingerprint; note that imphash is distorted for samples whose IAT is taken over by a packer, so unpack first before calculating