[PR #7144] feat(ghidra): admit DOS MZ targets on Windows P0 #7671

Open
opened 2026-11-03 13:39:32 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/7144
Author: @dark13b
Created: 10/25/2026
Status: 🔄 Open

Base: main ← Head: fix/ghidra-windows-dos-mz


📝 Commits (3)

  • ee3a895 feat(ghidra): admit DOS MZ targets on Windows P0
  • dc3f9e4 test(ghidra): check naming refusal in the read-only DOS lane
  • c92c7c8 test(ghidra): unwrap failed profile Result before asserting

📊 Changes

11 files changed (+151 additions, -46 deletions)

View changed files

📝 docs/ghidra-dos.md (+3 -2)
📝 docs/installation.md (+2 -1)
📝 docs/native-investigation.md (+2 -1)
📝 docs/testing.md (+4 -3)
📝 docs/windows-ghidra-p0.md (+4 -3)
📝 scripts/lib/real-ghidra-address-boundaries.d.mts (+5 -1)
📝 scripts/lib/real-ghidra-address-boundaries.mjs (+75 -24)
📝 scripts/verify-real-ghidra-dos.mjs (+14 -6)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+36 -2)
📝 src/ghidra/GhidraProvider.ts (+5 -2)
📝 src/ghidra/GhidraProviderCapabilities.ts (+1 -1)

📄 Description

Summary

Refs #7121. On Windows, open_binary detects a DOS MZ executable (dos-mz / x86), but Windows Ghidra P0 admits only native PE and raw 6502 images, so every later operation fails. This PR admits dos-mz on Windows P0 through the existing DOS import path and makes verify:ghidra:dos run on Windows.

Refs rather than Fixes: explicit COM and opening an existing Ghidra project (the other parts of the report) are not included.

Problem and expected behavior

Trigger: open_binary on any DOS MZ executable on a Windows host, then any provider operation. inspectWindowsP0TargetSupport returns target_format_unsupported, although the MzLoader import, the x86:LE:16:Real Mode language attestation and the independent load-image evidence don't depend on the host platform.

Expected: the same read-only DOS MZ analysis that Linux and macOS provide, under the existing Windows P0 native ownership and path-admission boundary. Annotation and naming edits stay refused, because Windows P0 has no mutation authority.

Change and scope

  • src/ghidra/GhidraProvider.ts: inspectWindowsP0TargetSupport admits dos-mz before the PE-only checks, as #2079 did for raw 6502. Profile resolution, launcher arguments and language attestation are the existing DOS path. The Windows-specific source path, loopback transport and Job Object ownership don't depend on the format.
  • scripts/verify-real-ghidra-dos.mjs runs on Windows:
    • passes the full system environment to child processes there (Windows children need SystemRoot and the profile paths);
    • expects annotation and naming edits to be refused with capability_unavailable;
    • skips verifier process-lineage observation, as verify:ghidra:6502 does on Windows.
    • Behaviour on Linux and macOS is unchanged.
  • scripts/lib/real-ghidra-address-boundaries.mjs takes { mutable }. Read-only truncation checks still run on Windows, and annotate_native_function, set_address_name and set_addresses_names must each be refused with capability_unavailable without changing the entry name. The .d.mts declares the option. The alias-naming edits move into a helper that runs only when edits are allowed. The CLI truncation checks now run before the alias edits instead of after.
  • The Windows P0 limitation text and the docs (Windows P0, DOS, installation, native investigation, testing) list DOS MZ.

Not included:

  • Explicit COM on Windows. verify:ghidra:com is built around annotation edits. It needs a separate read-only port before COM can be admitted with real-provider evidence.
  • Opening an existing Ghidra project. A .gpr file is a zero-byte marker; the lane always imports into an ephemeral project. Supporting it would be a new target kind.
  • The provider_unavailable wording when unconfigured providers could support the target. Not changed here.

Contribution scope: this extends an existing read-only lane to another host within the Windows P0 boundary. There is no new execution, injection, mutation or protection-recovery capability. It does widen the documented Windows P0 format set, so maintainers may want to confirm that scope.

Contract and boundary impact

  • Semantic owner and earliest changed stage: GhidraProvider.inspectTargetSupport (Windows P0 admission). Consumers: provider selection, snapshot replay and session admission all route through it. No superseded paths.
  • Contribution scope fit and linked maintainer decision: see the scope note above. No separate decision is linked.
  • CLI and MCP/tool-catalog contract: no schema change. DOS MZ targets on Windows now return analysis instead of a selection error.
  • Provider, bridge, target-format, or platform compatibility: Windows Ghidra P0 gains dos-mz. dos-com and other non-PE formats stay refused there.
  • Evidence, artifact, provenance, or reconstruction contract: unchanged. The DOS load-image attestation and analysis profile are the same as on POSIX, apart from the existing host-specific profile fields.
  • Target selection, authority, upstream behavior, and cleanup details: Windows P0 native path admission, private DACLs and Job Object ownership apply unchanged. Database mutation stays unavailable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process. (Ghidra headless, as for every Ghidra target)
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated: GhidraProvider.behavior.test.ts has a new case, "Ghidra Windows DOS admission". On a win32 installation host it checks that dos-mz is supported and resolves the MzLoader / x86:LE:16:Real Mode / load segment 0x1000 profile, and that dos-com is still target_format_unsupported. The old assertion that pinned DOS MZ as unsupported on Windows is removed.
  • Base reproduction: with the base GhidraProvider.ts restored, the new case fails (1 failed, 167 passed); with the change it passes.
  • Real provider: npm run verify:ghidra:dos on Windows generates the source-owned MZ fixture (no copyrighted binary) and checks the following through real MCP and CLI calls:
    • load image verified;
    • near and segment-relocated far calls;
    • function bodies and non-empty pseudocode;
    • CLI/MCP dossier parity;
    • truncation rejection;
    • annotation and naming edits refused;
    • source bytes unchanged;
    • owned cleanup.
  • Remaining proof gaps: the POSIX DOS lane was not re-run after the helper refactor. verify:ghidra:com was not run. Process-lineage observation is unavailable on Windows (provider jobs are owned natively, as in the 6502 lane).

Boundary regressions covered: target admission per host, and malformed or oversized coordinates through real reads (Windows). Lifecycle: owned project and runtime cleanup observed by the real lane.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Host: Windows 11 Pro x64, Ghidra 12.1.4, Temurin JDK 21.0.12, Node 24.21.0. That Node is below the pinned 24.22.1, so dependencies were installed with npm ci --engine-strict=false.

  • npm ci --engine-strict=false — succeeded
  • npm run build:cached — passed
  • npm run test:local -- src/ghidra/GhidraProvider.behavior.test.ts — 168 passed (re-run on b8ce3c74c together with src/contracts/toolInputReferences.test.ts: 202 passed)
  • npm run check:fast — passed (run on 6381aa0b1)
  • npm run verify:test-discovery — all 2485 test files discovered exactly once
  • npm run verify:ghidra:dos (Windows, before the rebase) — ok: true, cleanup: complete
  • npm run verify:ghidra:dos (Windows, rebased on b8ce3c74c, at 078a6e7ef) — ok: true, cleanup: complete, including the naming-refusal checks
  • npm run test:local -- src/ghidra/GhidraProvider.behavior.test.ts src/ghidra/GhidraDolProfile.test.ts and npm run test:focused -- tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts tests/boundary/providers/ghidra/ghidraClient.test.ts — 4 and 66 failures on this Windows checkout. Restoring the base files gave identical failure lists (POSIX-only fixtures, as CONTRIBUTING notes); no new failures.
  • Not run: npm run check, npm run docs:check, the POSIX real DOS lane.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on Windows 11 x64 only. Linux and macOS rely on CI.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new authority. Windows P0 stays read-only, and the native path and process boundary is unchanged.

Author readiness

Human owner: @dark13b

  • 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

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 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/7144 **Author:** [@dark13b](https://github.com/dark13b) **Created:** 10/25/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/ghidra-windows-dos-mz` --- ### 📝 Commits (3) - [`ee3a895`](https://github.com/morluto/rea/commit/ee3a895beaf72f21613d6eb01015d18fb06b1c4e) feat(ghidra): admit DOS MZ targets on Windows P0 - [`dc3f9e4`](https://github.com/morluto/rea/commit/dc3f9e48c053e0d7c56361fbcee469470fa71625) test(ghidra): check naming refusal in the read-only DOS lane - [`c92c7c8`](https://github.com/morluto/rea/commit/c92c7c8c4c35e8ebb447aea7831d87da81debc6b) test(ghidra): unwrap failed profile Result before asserting ### 📊 Changes **11 files changed** (+151 additions, -46 deletions) <details> <summary>View changed files</summary> 📝 `docs/ghidra-dos.md` (+3 -2) 📝 `docs/installation.md` (+2 -1) 📝 `docs/native-investigation.md` (+2 -1) 📝 `docs/testing.md` (+4 -3) 📝 `docs/windows-ghidra-p0.md` (+4 -3) 📝 `scripts/lib/real-ghidra-address-boundaries.d.mts` (+5 -1) 📝 `scripts/lib/real-ghidra-address-boundaries.mjs` (+75 -24) 📝 `scripts/verify-real-ghidra-dos.mjs` (+14 -6) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+36 -2) 📝 `src/ghidra/GhidraProvider.ts` (+5 -2) 📝 `src/ghidra/GhidraProviderCapabilities.ts` (+1 -1) </details> ### 📄 Description ## Summary Refs #7121. On Windows, `open_binary` detects a DOS MZ executable (`dos-mz` / `x86`), but Windows Ghidra P0 admits only native PE and raw 6502 images, so every later operation fails. This PR admits `dos-mz` on Windows P0 through the existing DOS import path and makes `verify:ghidra:dos` run on Windows. `Refs` rather than `Fixes`: explicit COM and opening an existing Ghidra project (the other parts of the report) are not included. ## Problem and expected behavior Trigger: `open_binary` on any DOS MZ executable on a Windows host, then any provider operation. `inspectWindowsP0TargetSupport` returns `target_format_unsupported`, although the MzLoader import, the `x86:LE:16:Real Mode` language attestation and the independent load-image evidence don't depend on the host platform. Expected: the same read-only DOS MZ analysis that Linux and macOS provide, under the existing Windows P0 native ownership and path-admission boundary. Annotation and naming edits stay refused, because Windows P0 has no mutation authority. ## Change and scope - `src/ghidra/GhidraProvider.ts`: `inspectWindowsP0TargetSupport` admits `dos-mz` before the PE-only checks, as #2079 did for raw 6502. Profile resolution, launcher arguments and language attestation are the existing DOS path. The Windows-specific source path, loopback transport and Job Object ownership don't depend on the format. - `scripts/verify-real-ghidra-dos.mjs` runs on Windows: - passes the full system environment to child processes there (Windows children need `SystemRoot` and the profile paths); - expects annotation and naming edits to be refused with `capability_unavailable`; - skips verifier process-lineage observation, as `verify:ghidra:6502` does on Windows. - Behaviour on Linux and macOS is unchanged. - `scripts/lib/real-ghidra-address-boundaries.mjs` takes `{ mutable }`. Read-only truncation checks still run on Windows, and `annotate_native_function`, `set_address_name` and `set_addresses_names` must each be refused with `capability_unavailable` without changing the entry name. The `.d.mts` declares the option. The alias-naming edits move into a helper that runs only when edits are allowed. The CLI truncation checks now run before the alias edits instead of after. - The Windows P0 limitation text and the docs (Windows P0, DOS, installation, native investigation, testing) list DOS MZ. Not included: - **Explicit COM on Windows.** `verify:ghidra:com` is built around annotation edits. It needs a separate read-only port before COM can be admitted with real-provider evidence. - **Opening an existing Ghidra project.** A `.gpr` file is a zero-byte marker; the lane always imports into an ephemeral project. Supporting it would be a new target kind. - **The `provider_unavailable` wording when unconfigured providers could support the target.** Not changed here. Contribution scope: this extends an existing read-only lane to another host within the Windows P0 boundary. There is no new execution, injection, mutation or protection-recovery capability. It does widen the documented Windows P0 format set, so maintainers may want to confirm that scope. ## Contract and boundary impact - Semantic owner and earliest changed stage: `GhidraProvider.inspectTargetSupport` (Windows P0 admission). Consumers: provider selection, snapshot replay and session admission all route through it. No superseded paths. - Contribution scope fit and linked maintainer decision: see the scope note above. No separate decision is linked. - CLI and MCP/tool-catalog contract: no schema change. DOS MZ targets on Windows now return analysis instead of a selection error. - Provider, bridge, target-format, or platform compatibility: Windows Ghidra P0 gains `dos-mz`. `dos-com` and other non-PE formats stay refused there. - Evidence, artifact, provenance, or reconstruction contract: unchanged. The DOS load-image attestation and analysis profile are the same as on POSIX, apart from the existing host-specific profile fields. - Target selection, authority, upstream behavior, and cleanup details: Windows P0 native path admission, private DACLs and Job Object ownership apply unchanged. Database mutation stays unavailable. - Generated metadata, package, or installation impact: none. Expected effects: - [x] Read existing files or captured data. - [x] Launch or attach to a process. (Ghidra headless, as for every Ghidra target) - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: `GhidraProvider.behavior.test.ts` has a new case, "Ghidra Windows DOS admission". On a `win32` installation host it checks that `dos-mz` is supported and resolves the MzLoader / `x86:LE:16:Real Mode` / load segment `0x1000` profile, and that `dos-com` is still `target_format_unsupported`. The old assertion that pinned DOS MZ as unsupported on Windows is removed. - Base reproduction: with the base `GhidraProvider.ts` restored, the new case fails (1 failed, 167 passed); with the change it passes. - Real provider: `npm run verify:ghidra:dos` on Windows generates the source-owned MZ fixture (no copyrighted binary) and checks the following through real MCP and CLI calls: - load image `verified`; - near and segment-relocated far calls; - function bodies and non-empty pseudocode; - CLI/MCP dossier parity; - truncation rejection; - annotation and naming edits refused; - source bytes unchanged; - owned cleanup. - Remaining proof gaps: the POSIX DOS lane was not re-run after the helper refactor. `verify:ghidra:com` was not run. Process-lineage observation is unavailable on Windows (provider jobs are owned natively, as in the 6502 lane). Boundary regressions covered: target admission per host, and malformed or oversized coordinates through real reads (Windows). Lifecycle: owned project and runtime cleanup observed by the real lane. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed Host: Windows 11 Pro x64, Ghidra 12.1.4, Temurin JDK 21.0.12, Node 24.21.0. That Node is below the pinned 24.22.1, so dependencies were installed with `npm ci --engine-strict=false`. - `npm ci --engine-strict=false` — succeeded - `npm run build:cached` — passed - `npm run test:local -- src/ghidra/GhidraProvider.behavior.test.ts` — 168 passed (re-run on `b8ce3c74c` together with `src/contracts/toolInputReferences.test.ts`: 202 passed) - `npm run check:fast` — passed (run on `6381aa0b1`) - `npm run verify:test-discovery` — all 2485 test files discovered exactly once - `npm run verify:ghidra:dos` (Windows, before the rebase) — `ok: true`, `cleanup: complete` - `npm run verify:ghidra:dos` (Windows, rebased on `b8ce3c74c`, at `078a6e7ef`) — `ok: true`, `cleanup: complete`, including the naming-refusal checks - `npm run test:local -- src/ghidra/GhidraProvider.behavior.test.ts src/ghidra/GhidraDolProfile.test.ts` and `npm run test:focused -- tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts tests/boundary/providers/ghidra/ghidraClient.test.ts` — 4 and 66 failures on this Windows checkout. Restoring the base files gave identical failure lists (POSIX-only fixtures, as CONTRIBUTING notes); no new failures. - Not run: `npm run check`, `npm run docs:check`, the POSIX real DOS lane. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on Windows 11 x64 only. Linux and macOS rely on CI. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new authority. Windows P0 stays read-only, and the native path and process boundary is unchanged. ## Author readiness Human owner: @dark13b - [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 - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#7671
No description provided.