Skip to content

Repository files navigation

imprint

Read the Windows APIs a binary reaches for, and map them to behaviours and MITRE ATT&CK techniques — before you run it. A fast, zero-dependency, offline PE triage tool in pure C. It executes nothing; it reads bytes.

CI License: MIT Language: C11

  imprint sample.exe
   PE32+ executable, console subsystem
  64 imports from 12 modules  -  24 flagged  -  9 capabilities

  CAPABILITIES
  ● Process Injection                T1055
      Allocates/writes memory in another process and starts code there
      via: VirtualAllocEx, WriteProcessMemory, CreateRemoteThread
  ● Registry Persistence             T1547.001
      Writes a registry key, a classic autorun persistence location
      via: RegSetValueExW, RegCreateKeyExW
  ● Anti-Debugging                   T1622
      Checks for a debugger and may alter behaviour under analysis
      via: IsDebuggerPresent
  ...

The problem

When a malware analyst gets a new Windows sample, the first question is what can this thing do — before committing to a full dynamic run in a sandbox. A binary announces a lot about its intentions in its import table: the list of Windows APIs it links against. VirtualAllocEx + WriteProcessMemory + CreateRemoteThread is process injection. RegSetValueExW on a Run key is persistence. IsDebuggerPresent is an analyst-aware sample.

The famous tool for reading this by hand — rohitab's API Monitor — has not seen a release since 2013 and does not run on modern Windows. Reading imports manually with dumpbin or a hex editor works but is slow and remembers nothing about what each API means. imprint does that reading for you, and it knows what the functions are for.

What imprint does

imprint parses a PE file's import, delay-import and export directories, looks each imported function up in a curated Windows API behaviour database, and then composes the individual imports into higher-order capabilities mapped to MITRE ATT&CK.

Two layers, on purpose:

  1. Flagged imports — every imported API that carries meaning, tagged with a category (injection, persistence, anti-analysis, network…), a severity, and its ATT&CK technique. This is a fact about the file.
  2. Capabilities — the composition. A single import rarely proves intent; WriteProcessMemory alone is used by debuggers. Paired with a remote allocation and a remote thread it is injection. Each fired capability lists the exact imports that triggered it as evidence, so the finding is auditable, not a verdict from nowhere.

It reads bytes on disk. It never maps, relocates, or executes the image, makes no network calls, and is safe to run against a live malware sample on any machine.

Why C, and why static

  • Static analysis is instant and safe — no VM, no detonation, no risk. It is the right first pass on an unknown file, and it also works on samples that would refuse to run under analysis.
  • Pure C11, zero dependencies. One gcc command builds it. The PE parser reads untrusted bytes through a bounded reader that never casts a struct over attacker-controlled memory and never reads a byte outside the buffer — malformed samples are the norm, not the exception, and the parser is written for that.
  • Portable: the analyzer compiles and runs on Windows, Linux, and macOS. You can triage Windows malware from a Linux box.

Install / build

git clone https://github.com/hamodywe/imprint
cd imprint
make              # or: gcc -std=c11 -O2 src/*.c -o imprint

Requires a C11 compiler (gcc, clang, or MinGW). No libraries beyond libc.

Usage

imprint sample.exe                 # full report
imprint sample.exe --min notable   # hide low-signal (info) imports
imprint sample.exe --all           # also list every import, classified or not
imprint sample.exe --json          # machine-readable report
imprint sample.exe --strict        # exit 1 if any capability is inferred (for pipelines)

Options

Option Purpose
--json Emit a JSON report
--all List every import, not only flagged ones
--min <level> Hide flagged imports below info / notable / suspicious
--strict Exit 1 if any capability is inferred
--color / --no-color Force colour (default: auto)
-v, --version / -h, --help Version / help

Exit codes

Code Meaning
0 Analysed (or, with --strict, no capabilities)
1 --strict and at least one capability inferred
2 Usage or read/parse error

Capabilities detected

Composed from the import set, each with ATT&CK mapping and evidence:

Process Injection (T1055) · Process Hollowing (T1055.012) · APC Injection (T1055.004) · Dynamic API Resolution (T1027) · Anti-Debugging (T1622) · Sandbox / Timing Evasion (T1497) · Locale Geofencing (T1614) · Registry Persistence (T1547.001) · Service Persistence (T1543.003) · Keystroke Logging (T1056.001) · Screen Capture (T1113) · Clipboard Capture (T1115) · Credential Access / DPAPI (T1555) · File Encryption / ransomware shape (T1486) · Payload Download (T1105) · Network Communication (T1071) · Process Discovery (T1057) · Privilege Manipulation (T1134.001) · Embedded Payload in Resources (T1027.009) · Indicator Removal (T1070.004).

The underlying API database holds 130+ curated functions across memory, process, thread, injection, dynamic-load, hooking, registry, filesystem, service, network, crypto, anti-analysis, privilege, discovery, execution, desktop/capture, resources and COM.

JSON output

{
  "tool": "imprint",
  "version": "0.1.0",
  "file": "sample.exe",
  "format": { "arch": "PE32+", "machine": "x64", "type": "executable", "subsystem": "console", ... },
  "stats": { "imports": 64, "modules": 12, "capabilities": 9 },
  "capabilities": [
    { "name": "Process Injection", "attack": "T1055",
      "description": "Allocates/writes memory in another process and starts code there",
      "evidence": ["VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread"] }
  ],
  "imports": [
    { "name": "VirtualAllocEx", "dll": "kernel32.dll", "category": "injection",
      "severity": "suspicious", "attack": "T1055", "delay": false }
  ]
}

How it works

file bytes ──▶ pe.c (bounded PE parser) ──▶ imports[]
                                              │
                          apidb.c (API → category / ATT&CK / note)
                                              │
                          rules.c (compose imports → capabilities + evidence)
                                              │
                              report.c (text / JSON, control-char safe)
  • src/buf.h — the bounded little-endian reader every byte access goes through.
  • src/pe.c — DOS/NT/optional headers, sections, and the import/delay/export directories, with RVA→offset translation. Defensive against malformed input by construction.
  • src/apidb.c — the curated API knowledge base.
  • src/rules.c — capability composition, each rule collecting its evidence.
  • src/report.c — text and JSON, both sanitising control characters out of untrusted names so a crafted DLL name cannot rewrite your terminal.

See docs/architecture.md and docs/internals.md — the latter is a short guided tour of the PE format for anyone learning Windows internals from this code.

Limitations

Stated plainly, because a triage tool that oversells is worse than none:

  • Imports are intent, not proof of execution. A binary that imports CreateRemoteThread can inject; it does not prove it will. imprint reports capability, not behaviour.
  • The static import table is not the whole story. Malware that resolves APIs at runtime via LoadLibrary + GetProcAddress hides them from the table — so imprint flags that pattern itself (Dynamic API Resolution) as the tell, but it cannot name the functions hidden behind it.
  • Packed samples show the packer's imports, not the payload's. A tiny, capability-light import set on a large file is itself a signal worth noting; unpack first, then re-run.
  • Ordinal-only imports (common in ws2_32) are counted but not named — resolving ordinals to names is on the roadmap.
  • The database is curated, not exhaustive — it covers the functions that carry triage signal, and grows by pull request.

Roadmap

Ordinal→name resolution · a --diff of two samples' capability sets · export-name behaviour tags for DLLs · a companion dynamic API tracer (the live half of the vision) that records decoded call arguments in a sandbox. See ROADMAP.md.

Contributing

New API entries and capability rules are very welcome — the bar is that every entry is a claim checkable against Microsoft's documentation, and every rule ships with a test. See CONTRIBUTING.md.

License

MIT © hamodywe

About

Static PE triage: reads the Windows APIs a binary imports and maps them to behaviours and MITRE ATT&CK - before you run it. Pure C, zero deps, executes nothing.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages