Skip to content

feat(vm): native VM reconstruction foundations and forensic PE/dump/xref APIs - #1

Merged
SWORDIntel merged 131 commits into
mainfrom
feat/themida-vm-structural-20261015
Oct 15, 2026
Merged

SWORDIntel merged 131 commits into
mainfrom
feat/themida-vm-structural-20261015

Conversation

@SWORDIntel

@SWORDIntel SWORDIntel commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Goal

Extend Angryier's native reverse-engineering capabilities without expanding target-specific Lua scripts or rewriting METHRA/radare3. This PR stays draft until its tests and code quality checks pass.

Implemented: Themida/VM analysis foundations

  • Fix signed negative displacements in CFG RIP-relative/base address calculations, regression on a table located before its dispatch.
  • Tighten LEA provenance: RIP/absolute = constant, register-relative = derived context, multi-register inputs = explicitly unknown.
  • Opt-in Runtime::inspect_vm_window joins existing Themida indirect-exit classification with VPC candidate heuristics. Structural candidates are not verified VPCs.
  • VmAnalysisSession: explicit transition evidence, VPC/context/code-state-sensitive keys, atomic state/edge budget admission, evidence aggregation and observational CFG projection.
  • reduce_vm_effects: bounded backward dependency slice from explicitly identified register/memory/flags/dispatch effects and guards, reusing existing expression simplifier without dropping memory address dependencies.

Implemented: native forensic workflow

  • Strict PeAddressMap separating VA, RVA, disk offset and virtual zero-fill, with checked section bounds and overlap rejection.
  • XrefIndex for source-backed direct calls/branches and RIP-relative memory/IAT SLOT references (without manufacturing callee targets).
  • In-memory MINIDUMP_MEMORY_LIST/MEMORY64_LIST extraction and streaming MiniDumpFile reader, with explicit errors for uncaptured pages and allocation bounds.
  • Runtime inspect_xrefs_window alongside inspect_vm_window.
  • Lightweight CLI inspection commands to replace repeated Capstone/pefile/dump one-liners:
cargo build --release -p angryier-cli --no-default-features --features inspect
./target/release/angryier pe-address advisor_unpacked_state.exe 0x140404e10 16
./target/release/angryier dump-read diag_1892.dmp 0x094464c0 64

The above are executable commands for the user's local file paths, not assertions that the Sarin dump was present or the singleton predicate was solved.

Validation

  • New regressions cover VM context isolation, dispatch classification, graph budgets, output-rooted algebra, signed displacements, PE file-offset mapping, missing dump bytes, and streaming memory64 reads.
  • Branch adds a focused VM test workflow so existing unrelated workspace rustfmt/clippy debt cannot mask compilation and behavioral test status.
  • Ordinary root CI remains enabled and reports any existing baseline failures. No bypass or suppression on the main CI job.
  • Link to exact workflow job results before marking ready for review.

Non-goals / what is not solved

  • No Themida encrypted-bytecode recovery, handler semantic extraction, complete VM CFG or automatic protector bypass.
  • No assertion of live process restoration from the dump: Windows thread context, module relocation state, uncaptured page policy and synchronization models are not implemented.
  • No proven Sarin Advisor wait/signal predicate, singleton-object values, or GDI+ success path.
  • METHRA integration remains a later change. No new database, ISA engine, scheduler or decompiler was introduced.

Suggested review gates

  1. cargo check -p angryier-cfg -p angryier-expr -p angryier-loader -p angryier-runtime -p angryier-cli --all-targets
  2. cargo test -p angryier-cfg, cargo test -p angryier-expr, cargo test -p angryier-loader, cargo test -p angryier-cli
  3. cargo test -p angryier-runtime --features "xed script" --test lua_api
  4. Inspect and resolve any Clippy warnings in new code. Existing older warnings should be tracked separately, not misrepresented as fixes to this PR.

2026-10-15 — Living evidence history reconciliation

The private analyst history was reviewed but not copied into this repository. The following changes are generic engine features, not application-specific bypasses:

  • The original PE/heap data reconstruction was corrected after a mistaken dump-to-image mapping. The runtime now accepts bounded, captured-memory snapshots or analyst-verified VA-backed heap slices through one atomic overlay interface; uncaptured memory must not be silently initialized to zero. Persistent Memory object IDs are unique across repeated imports.
  • main commit b3df3e5 fixing indirect-call frame mirroring was incorporated via a two-parent merge commit; its four updated Lua script sources are retained.
  • Generic executable-region and explicit-hook validation replaced hard-coded executable ranges in RET recovery. Ambiguous stack windows fail closed instead of taking the closest code-like address; every accepted noncanonical recovery remains Concretized debt.
  • Win64 calling-convention arguments, auto/manual-reset event state, zero-time polling, and explicit pending/unknown waits are implemented with tests. These are not a thread scheduler.
  • CreateThread's synthetic success stub is rejected by both concrete and symbolic execution when no explicit model is installed. Lazy function summarization may not erase PE IAT import boundaries.
  • Call/return step audits and identity-gated first-divergence comparison preserve missing stack evidence instead of silently equating incomplete traces.
  • IsProcessorFeaturePresent's approximate success surrogate remains available for exploration but now emits bounded fidelity debt. Its result must not be interpreted as exact Windows behavior.
  • angryier doctor reports compiled revision, working checkout revision, build features and binary SHA-256 without printing user paths by default.
  • Exact call/return comparison tests disable function-summary collapsing; otherwise one symbolic step may correctly summarize multiple native instructions and PC-by-PC comparison is invalid.

Outstanding fidelity work (do not claim complete)

  1. Windows userland thread scheduler: CreateThread/ResumeThread/Wait semantics with per-process thread state, stack and handle ownership, and deterministic replay. Without it, asynchronous provider initialization cannot be claimed reproduced.
  2. Per-state Win32 handle/table snapshots: current models share a registry-owned handle table, which is not yet a proof-preserving multi-path snapshot model.
  3. Windows user-mode bootstrap: imports, OS loader, TEB/PEB, exception/TLS callbacks, module rebasing and synchronization; load_pe_driver is not a full Windows userland loader.
  4. Verified snapshot identity: associate dump/slices with the exact image, thread context, acquisition epoch, page versions, hashes and captured page permissions. A user-provided slice identity is not authenticated by its bytes.
  5. VM semantic extraction: VPC-sensitive observed graphs and output-rooted simplification are foundations only; automatic handler effect discovery, bytecode decryption and METHRA output integration are not implemented.
  6. Quality gates: focused VM suite now runs runtime unit, PE call-return and Lua integration tests. Full repository fmt/clippy debt remains separately reported by original root CI; do not disable it or label it passed.

The independent analysis corpus demonstrates deeper execution but still does not establish complete application initialization or a successful validated UI workflow.

2026-10-15 — Native AngryIR handler-footprint milestone (continued)

  • Added crates/angryier-ir/src/vm_footprint.rs and exported collect_vm_ir_effects as an opt-in, single-block structural-effect inventory, backed by BasicIrVerifier.
  • Reports all explicit architectural writes, ordered alias-possible stores, reads (including dead potentially faulting reads and masked-load dependencies), direct/indirect transfers, branch conditions, calls, returns and traps.
  • Walks the SSA dependency closure without recursion, retaining external register/ExprRef/memory-read sources, partial-parent write dependencies and hard limits. Records unresolved primitive/handler-boundary caveats; does not claim semantic equivalence, multi-block summaries or recovered Themida bytecode.
  • Added panic-free regression tests for aliasing stores, dead reads, masked-load masks, indirect dispatch, parent-register dependence, malformed SSA and resource limits. Focused VM CI passed on the pre-format commit ac7c6cf4082: https://github.com/SWORDIntel/Angryier/actions/runs/38763176062 . Rustfmt applied only to the new file in 221be5fc7e30; check its latest CI separately.
  • Focused workflow now tests angryier-ir and checks its compilation/clippy alongside the existing CFG, expressions, loader, runtime and forensic suites.
  • Existing full-repository CI is still blocked by pre-existing fmt diffs in multiple files. Keep that failure visible; do not conflate with the focused VM test results.

Next explicit gate: capture real handler-bounded execution with exact entry state/VPC and per-block SSA namespaces; map retained IR values to symbolic ExprId (including memory widths/fault effects), and compare reduced handler summaries by deterministic replay and solver equivalence before replacing interpreter execution. Do not automatically infer VM-internal registers from native register names.

Continued P0 execution — native VM handler pipeline (2026-10-15)

  • Implemented scoped, bounded multi-block AngryIR trace footprints and opt-in native execution observation with recorded VPC/context/code identity and preserved COW entry/final states.
  • Exposed per-block SSA expression values through the existing symbolic evaluator, then connected trace-backed, ordered register/memory/branch/dispatch effects to the existing MBA reducer.
  • Implemented per-write post-state expression capture to handle repeated register writes and partial-parent writes without using an incorrect end-of-block register expression.
  • Fixed a cross-engine semantic mismatch: AngryIR Concat uses (low, high), while the expression DAG uses (high, low). The lowering bridge and partial-register splice now preserve correct operand order.
  • Fixed first-use x86 32-bit zero-extension: MOV EAX now materializes to the architectural 64-bit RAX parent, clearing upper bits even when the previous RAX upper half was nonzero.
  • Kept per-write tracing opt-in so the ordinary symbolic execution hot path avoids allocating a write log.
  • Added tests for multi-block/repeated/partial effects, concat concrete-symbolic parity, nonzero RAX upper-half clearing, and actual XED-executed two-instruction handler replay, not just mock expected states.
  • Widened focused CI to run all angryier-execution and angryier-ir tests, plus existing native/runtime/forensic/VM tests.
  • Latest tested code HEAD: d182bf233ef5e246ca1aafc483210a2df1bc6c86.
  • Focused validation: https://github.com/SWORDIntel/Angryier/actions/runs/38781390555 — PASS.
  • Repository-wide CI: https://github.com/SWORDIntel/Angryier/actions/runs/38781390552 — FAIL (format gate). Keep PR draft; root formatting debt has not been resolved.

Remaining explicit gates: input-independent symbolic handler relations, differential multi-input/SMT equivalence, fault/alias and full memory-write coverage, measured VPC/context boundary validation, and versioned METHRA semantic handoff. No general Themida auto-devirtualization, universal summary correctness, or Windows threading fidelity is claimed.

@SWORDIntel
SWORDIntel merged commit d182bf2 into main Oct 15, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant