[PR #3965] [MERGED] feat(ghidra): admit ARM PE targets on Windows #5969

Closed
opened 2026-10-31 01:10:49 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/3965
Author: @N0zoM1z0
Created: 10/15/2026
Status: ✅ Merged
Merged: 10/15/2026
Merged by: @N0zoM1z0

Base: main ← Head: feat/windows-ghidra-arm-pe-2186


📝 Commits (4)

  • 8adc2c3 feat(ghidra): admit ARM PE targets on Windows
  • b52d529 refactor(ghidra): derive PE fixture bases from shared layout
  • 6f60eca fix(ghidra): bind Windows ARM admission to COFF machines
  • aeab4a1 fix(ghidra): preserve language selection provenance

📊 Changes

19 files changed (+496 additions, -112 deletions)

View changed files

📝 docs/ghidra-provider.md (+5 -1)
📝 docs/installation.md (+1 -1)
📝 docs/managed-code-analysis.md (+1 -1)
📝 docs/native-investigation.md (+1 -1)
📝 docs/windows-ghidra-p0.md (+51 -3)
📝 scripts/create-ghidra-windows-fixture.mjs (+101 -45)
📝 scripts/lib/ghidra-windows-byte-search.mjs (+1 -2)
➕ scripts/lib/ghidra-windows-fixture-options.d.mts (+21 -0)
➕ scripts/lib/ghidra-windows-fixture-options.mjs (+50 -0)
📝 scripts/verify-packaged-ghidra-windows.mjs (+12 -14)
📝 scripts/verify-real-ghidra-windows.mjs (+53 -26)
📝 src/domain/binaryTarget.ts (+3 -0)
📝 src/domain/binaryTargetTypes.ts (+2 -0)
📝 src/ghidra/GhidraAnalysisProfile.ts (+11 -0)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+126 -15)
📝 src/ghidra/GhidraProvider.ts (+15 -2)
📝 src/ghidra/GhidraProviderCapabilities.ts (+5 -1)
📝 tests/boundary/filesystem/binaryTarget.test.ts (+20 -0)
➕ tests/conformance/providers/ghidra/windowsFixtureSelection.test.ts (+17 -0)

📄 Description

Summary

Fixes #2186. Builds on #2631, adding real Windows verification, ARMNT/Thumb fixtures, shared fixture coordinates, and a scoped analysis-profile discriminator.

Windows x64 Ghidra P0 now admits native ARMNT (Thumb-2) and ARM64 PE applications/DLLs through its existing read-only workflow. Previously, a real Microsoft Coreinfo ARM64 PE was rejected with Windows Ghidra P0 accepts x86 and x86-64 PE targets only.

Change and scope

  • Retain the original COFF machine at the PE parsing boundary and through file-backed target resolution. Windows ARM admission requires observed ARMNT 0x01C4 or ARM64 0xAA64; legacy ARM 0x01C0, Thumb 0x01C2, and missing machine facts fail closed.
  • Own the admitted PE architecture list at the Ghidra capability boundary. Preserve managed-image, role, NTFS, native-addon, source-identity, and process-ownership constraints, including the existing raw NMOS 6502 lane.
  • Let Ghidra's PE loader select ARMNT/Thumb or ARM64 language when no processor override is selected. Preserve explicit language/compiler choices and record their provenance as explicit-language-v1; retain the default profile identity. The source fixture verifier checks complete language IDs, not family prefixes. Commit the original machine to the Windows ARM PE profile and version only that lane; preserve other architecture/host cache identities.
  • Share encoded fixture layout and flag selection across source/packaged verifiers. Remove x64-only caller and byte-search address assumptions. Reject conflicting architecture flags without treating positional paths as flags.
  • Honor explicitly selected disabled analyzers in the source verifier and preserve completed primary facts if its separate file-offset lane fails. Reuse main's unified verifier timeout policy.

Contract and boundary impact

  • Semantic owner: existing Ghidra provider target admission and analysis-profile identity.
  • Contribution scope: maintainer-requested extension of an existing static, read-only provider (#2186); no target execution, injection, mutation, protection recovery, or new provider authority. Additional execution-authority decision: not applicable.
  • CLI/MCP schemas and catalog: unchanged. Existing operations become available for admitted ARM PE targets.
  • Evidence and provenance: original target identity, source mappings, PE role, profile, and limitations retained. No new bulk response or eager inventory is added.
  • Cleanup: existing immutable snapshots, native handles, private runtimes, authenticated loopback, and Job Object ownership reused.
  • Generated/package metadata: no dependency, native ABI, installer, or generated-output change.

Expected effects:

  • Read existing files or captured data.
  • Launch an owned Ghidra/JVM process and run the existing bridge scripts.
  • Write owned ephemeral snapshots/projects/runtime files; never modify the selected input.
  • Use the existing authenticated IPv4 loopback transport; no remote target service.

Evidence and regression coverage

Executed on real Windows x64, Ghidra 12.1.4, JDK 21 and Node 24.18.0. All target analysis was static; neither application nor DLL/installer entrypoints were executed.

Target Observed result
Encoded ARM64 EXE All 26 read-only operations; return-42 decompilation; paged masked byte search; CLI/MCP file offsets, including ambiguous mappings; source unchanged; cleanup complete.
Encoded ARMNT/Thumb DLL All 26 operations; exported caller/callee and addition of one; return-42 decompilation; byte search; CLI/MCP file offsets; source unchanged; cleanup complete. Ambiguous mappings unavailable in its single-section layout.
Microsoft-signed Coreinfo64a.exe (498,480 bytes) Focused MCP entrypoint decompilation, identical repeated readback, loaded bytes matching source, unchanged input, same analysis run, cleanup complete.
Microsoft-signed VC++ 2013 ARM MSVCR120 DLL (715,760 bytes) Exported abs decompiles to absolute-value arithmetic; repeated readback, source-mapped bytes, unchanged input, same analysis run, cleanup complete.

Public inputs: Coreinfo, Microsoft VC++ 2013 redistributable. Installer contents were extracted as data without execution. SHA-256:

  • Coreinfo64a.exe: e7a3c5cd591d62815bbcbf97f1be7f8f58ff93e0bc069ff8d519d87d1e03e536
  • MSVCR120 ARM: 844870dfd6ddc13dfcc4d01fa54a2ea7ba8d26fc6695872683e5b67cc0c0ddfb

Real Windows admission regression after Codex review: the pre-fix public MCP accepted both legacy machines as arm; the rebuilt candidate rejects both, still admits both Microsoft public inputs, and records their exact machine values in their profiles. The four input hashes were unchanged and the runtime directory was empty after close. See review follow-up.

Performance and limits

Two logical CPUs, BelowNormal priority, 512 MiB Node/JVM heaps, explicit 900-second startup budget, and REA_GHIDRA_DISABLED_ANALYZERS=WindowsResourceReference were used. Product defaults are unchanged.

Public MCP query First query including cold analysis Identical same-session read Evidence text
Coreinfo ARM64 entrypoint 434.2 s 22.9 ms 4,622 bytes
MSVCR120 ARM abs 601.5 s 11.2 ms 4,789 bytes
COFF-bound MSVCR120 follow-up 407.4 s 15.3 ms 4,830 bytes

The COFF-bound follow-up selected an explicit 1,800-second startup budget after a preserved additional 900-second run returned provider_timeout; both attempts left the input unchanged and cleaned their runtimes. CPU, priority and heap bounds were unchanged. The successful query decompiled abs, matched source-mapped bytes, retained identical repeated readback and closed cleanly. A final-commit Windows MCP check revalidated all four admission cases and proved that its default MSVCR120 profile exactly equals the successful engine run's profile, including digest f45a85cbd9eb0f5a335424313b717bdc8c57379a1e16ca0a978fce902f64b98c.

These are individual observations on a constrained host, not a general speed guarantee or an attribution to a particular cache. Analysis sessions are reused for focused follow-ups. Failed attempts and input/runtime hashes were retained locally; transient deployment/download intermediates were removed.

Meaningful gaps: default-analyzer runs exceeded the startup budget; resource-reference analysis and default-configuration completion are unverified. No Windows-on-Arm host, ARM64EC/ARM64X, compiler-produced fixture matrix, x86-only compact-reference battery on ARM, or complete installed-package Ghidra verification is claimed. Explicit processor overrides are covered by catalog-backed profile tests, without real configured-processor analysis claims. Source fixture runs covered ARM64 EXE and ARM32 DLL; local header/admission tests cover all eight architecture/role combinations. The matching prebuilt native addon was reused from source 0f55f247c; the newer main volume-root write-sharing change was not in that binary. This feature changes no native code.

Validation performed

  • Final-head CI, attempt 2 — SUCCESS, including CI required, Windows capability/package and native build lanes. Initial Apple job failure was the unchanged .hop target-free MCP workflow hitting its 10-second test deadline; same-head failed-item rerun passed without code or timeout edits. That case took 5.583 s on retry; Apple regression results: 230 passed, 4 skipped across 18 passing files.
  • npm run build:cached — PASS.
  • Focused provider, Windows capability, fixture selector and verifier-timeout tests — 183 PASS, 1 macOS-only skip / 6 files.
  • npm run check:fast — PASS (typecheck/lint).
  • npm run docs:check — PASS, including 5 README consistency tests.
  • npm run verify:test-discovery — PASS, 1,299 files discovered exactly once.
  • Changed-file formatter check and staged whitespace check — PASS.
  • Real source verifier --arm64 and --arm --dll — exit 0; separate real public MCP probe — exit 0. Verification scope/configuration is stated above; raw host logs are kept private.

Author readiness

Human owner: @N0zoM1z0

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • Concrete requested enhancement and focused Conventional Commit title.
  • Scope and authority preserved; related issue linked.
  • Changed behavior and failure paths covered; owning docs updated.
  • Representative real CLI/MCP results and meaningful gaps recorded.
  • Final diff checked for unrelated changes, private host/license data and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/3965 **Author:** [@N0zoM1z0](https://github.com/N0zoM1z0) **Created:** 10/15/2026 **Status:** ✅ Merged **Merged:** 10/15/2026 **Merged by:** [@N0zoM1z0](https://github.com/N0zoM1z0) **Base:** `main` ← **Head:** `feat/windows-ghidra-arm-pe-2186` --- ### 📝 Commits (4) - [`8adc2c3`](https://github.com/morluto/rea/commit/8adc2c3e4daaed506dab3179e2415e0bde71662c) feat(ghidra): admit ARM PE targets on Windows - [`b52d529`](https://github.com/morluto/rea/commit/b52d5299c91a02d438969bd36dd7470bbca52d1e) refactor(ghidra): derive PE fixture bases from shared layout - [`6f60eca`](https://github.com/morluto/rea/commit/6f60ecad6f244f0dc71d4b071743acd08f523d19) fix(ghidra): bind Windows ARM admission to COFF machines - [`aeab4a1`](https://github.com/morluto/rea/commit/aeab4a16774bd5ddf9d525861d13ba9dfc1c81f2) fix(ghidra): preserve language selection provenance ### 📊 Changes **19 files changed** (+496 additions, -112 deletions) <details> <summary>View changed files</summary> 📝 `docs/ghidra-provider.md` (+5 -1) 📝 `docs/installation.md` (+1 -1) 📝 `docs/managed-code-analysis.md` (+1 -1) 📝 `docs/native-investigation.md` (+1 -1) 📝 `docs/windows-ghidra-p0.md` (+51 -3) 📝 `scripts/create-ghidra-windows-fixture.mjs` (+101 -45) 📝 `scripts/lib/ghidra-windows-byte-search.mjs` (+1 -2) ➕ `scripts/lib/ghidra-windows-fixture-options.d.mts` (+21 -0) ➕ `scripts/lib/ghidra-windows-fixture-options.mjs` (+50 -0) 📝 `scripts/verify-packaged-ghidra-windows.mjs` (+12 -14) 📝 `scripts/verify-real-ghidra-windows.mjs` (+53 -26) 📝 `src/domain/binaryTarget.ts` (+3 -0) 📝 `src/domain/binaryTargetTypes.ts` (+2 -0) 📝 `src/ghidra/GhidraAnalysisProfile.ts` (+11 -0) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+126 -15) 📝 `src/ghidra/GhidraProvider.ts` (+15 -2) 📝 `src/ghidra/GhidraProviderCapabilities.ts` (+5 -1) 📝 `tests/boundary/filesystem/binaryTarget.test.ts` (+20 -0) ➕ `tests/conformance/providers/ghidra/windowsFixtureSelection.test.ts` (+17 -0) </details> ### 📄 Description ## Summary Fixes #2186. Builds on #2631, adding real Windows verification, ARMNT/Thumb fixtures, shared fixture coordinates, and a scoped analysis-profile discriminator. Windows x64 Ghidra P0 now admits native ARMNT (Thumb-2) and ARM64 PE applications/DLLs through its existing read-only workflow. Previously, a real Microsoft Coreinfo ARM64 PE was rejected with `Windows Ghidra P0 accepts x86 and x86-64 PE targets only.` ## Change and scope - Retain the original COFF machine at the PE parsing boundary and through file-backed target resolution. Windows ARM admission requires observed ARMNT `0x01C4` or ARM64 `0xAA64`; legacy ARM `0x01C0`, Thumb `0x01C2`, and missing machine facts fail closed. - Own the admitted PE architecture list at the Ghidra capability boundary. Preserve managed-image, role, NTFS, native-addon, source-identity, and process-ownership constraints, including the existing raw NMOS 6502 lane. - Let Ghidra's PE loader select ARMNT/Thumb or ARM64 language when no processor override is selected. Preserve explicit language/compiler choices and record their provenance as `explicit-language-v1`; retain the default profile identity. The source fixture verifier checks complete language IDs, not family prefixes. Commit the original machine to the Windows ARM PE profile and version only that lane; preserve other architecture/host cache identities. - Share encoded fixture layout and flag selection across source/packaged verifiers. Remove x64-only caller and byte-search address assumptions. Reject conflicting architecture flags without treating positional paths as flags. - Honor explicitly selected disabled analyzers in the source verifier and preserve completed primary facts if its separate file-offset lane fails. Reuse main's unified verifier timeout policy. ## Contract and boundary impact - Semantic owner: existing Ghidra provider target admission and analysis-profile identity. - Contribution scope: maintainer-requested extension of an existing static, read-only provider (#2186); no target execution, injection, mutation, protection recovery, or new provider authority. Additional execution-authority decision: not applicable. - CLI/MCP schemas and catalog: unchanged. Existing operations become available for admitted ARM PE targets. - Evidence and provenance: original target identity, source mappings, PE role, profile, and limitations retained. No new bulk response or eager inventory is added. - Cleanup: existing immutable snapshots, native handles, private runtimes, authenticated loopback, and Job Object ownership reused. - Generated/package metadata: no dependency, native ABI, installer, or generated-output change. Expected effects: - [x] Read existing files or captured data. - [x] Launch an owned Ghidra/JVM process and run the existing bridge scripts. - [x] Write owned ephemeral snapshots/projects/runtime files; never modify the selected input. - [x] Use the existing authenticated IPv4 loopback transport; no remote target service. ## Evidence and regression coverage Executed on real Windows x64, Ghidra 12.1.4, JDK 21 and Node 24.18.0. All target analysis was static; neither application nor DLL/installer entrypoints were executed. | Target | Observed result | | --- | --- | | Encoded ARM64 EXE | All 26 read-only operations; return-42 decompilation; paged masked byte search; CLI/MCP file offsets, including ambiguous mappings; source unchanged; cleanup complete. | | Encoded ARMNT/Thumb DLL | All 26 operations; exported caller/callee and addition of one; return-42 decompilation; byte search; CLI/MCP file offsets; source unchanged; cleanup complete. Ambiguous mappings unavailable in its single-section layout. | | Microsoft-signed Coreinfo64a.exe (498,480 bytes) | Focused MCP entrypoint decompilation, identical repeated readback, loaded bytes matching source, unchanged input, same analysis run, cleanup complete. | | Microsoft-signed VC++ 2013 ARM MSVCR120 DLL (715,760 bytes) | Exported `abs` decompiles to absolute-value arithmetic; repeated readback, source-mapped bytes, unchanged input, same analysis run, cleanup complete. | Public inputs: [Coreinfo](https://learn.microsoft.com/en-us/sysinternals/downloads/coreinfo), [Microsoft VC++ 2013 redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=40784). Installer contents were extracted as data without execution. SHA-256: - Coreinfo64a.exe: `e7a3c5cd591d62815bbcbf97f1be7f8f58ff93e0bc069ff8d519d87d1e03e536` - MSVCR120 ARM: `844870dfd6ddc13dfcc4d01fa54a2ea7ba8d26fc6695872683e5b67cc0c0ddfb` Real Windows admission regression after Codex review: the pre-fix public MCP accepted both legacy machines as `arm`; the rebuilt candidate rejects both, still admits both Microsoft public inputs, and records their exact machine values in their profiles. The four input hashes were unchanged and the runtime directory was empty after close. See [review follow-up](https://github.com/morluto/rea/pull/3965#discussion_r4280289691). ### Performance and limits Two logical CPUs, BelowNormal priority, 512 MiB Node/JVM heaps, explicit 900-second startup budget, and `REA_GHIDRA_DISABLED_ANALYZERS=WindowsResourceReference` were used. Product defaults are unchanged. | Public MCP query | First query including cold analysis | Identical same-session read | Evidence text | | --- | --- | --- | --- | | Coreinfo ARM64 entrypoint | 434.2 s | 22.9 ms | 4,622 bytes | | MSVCR120 ARM `abs` | 601.5 s | 11.2 ms | 4,789 bytes | | COFF-bound MSVCR120 follow-up | 407.4 s | 15.3 ms | 4,830 bytes | The COFF-bound follow-up selected an explicit 1,800-second startup budget after a preserved additional 900-second run returned `provider_timeout`; both attempts left the input unchanged and cleaned their runtimes. CPU, priority and heap bounds were unchanged. The successful query decompiled `abs`, matched source-mapped bytes, retained identical repeated readback and closed cleanly. A final-commit Windows MCP check revalidated all four admission cases and proved that its default MSVCR120 profile **exactly equals** the successful engine run's profile, including digest `f45a85cbd9eb0f5a335424313b717bdc8c57379a1e16ca0a978fce902f64b98c`. These are individual observations on a constrained host, not a general speed guarantee or an attribution to a particular cache. Analysis sessions are reused for focused follow-ups. Failed attempts and input/runtime hashes were retained locally; transient deployment/download intermediates were removed. Meaningful gaps: default-analyzer runs exceeded the startup budget; resource-reference analysis and default-configuration completion are unverified. No Windows-on-Arm host, ARM64EC/ARM64X, compiler-produced fixture matrix, x86-only compact-reference battery on ARM, or complete installed-package Ghidra verification is claimed. Explicit processor overrides are covered by catalog-backed profile tests, without real configured-processor analysis claims. Source fixture runs covered ARM64 EXE and ARM32 DLL; local header/admission tests cover all eight architecture/role combinations. The matching prebuilt native addon was reused from source `0f55f247c`; the newer main volume-root write-sharing change was not in that binary. This feature changes no native code. ## Validation performed - [Final-head CI, attempt 2](https://github.com/morluto/rea/actions/runs/38703547232) — **SUCCESS**, including `CI required`, Windows capability/package and native build lanes. Initial Apple job failure was the unchanged `.hop` target-free MCP workflow hitting its 10-second test deadline; same-head failed-item rerun passed without code or timeout edits. That case took 5.583 s on retry; Apple regression results: 230 passed, 4 skipped across 18 passing files. - `npm run build:cached` — PASS. - Focused provider, Windows capability, fixture selector and verifier-timeout tests — **183 PASS, 1 macOS-only skip / 6 files**. - `npm run check:fast` — PASS (typecheck/lint). - `npm run docs:check` — PASS, including 5 README consistency tests. - `npm run verify:test-discovery` — PASS, 1,299 files discovered exactly once. - Changed-file formatter check and staged whitespace check — PASS. - Real source verifier `--arm64` and `--arm --dll` — exit 0; separate real public MCP probe — exit 0. Verification scope/configuration is stated above; raw host logs are kept private. ## Author readiness Human owner: @N0zoM1z0 - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] Concrete requested enhancement and focused Conventional Commit title. - [x] Scope and authority preserved; related issue linked. - [x] Changed behavior and failure paths covered; owning docs updated. - [x] Representative real CLI/MCP results and meaningful gaps recorded. - [x] Final diff checked for unrelated changes, private host/license data and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:10:49 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#5969
No description provided.