[PR #7008] [MERGED] feat(pe): inspect TLS callbacks and load configuration #7568

Closed
opened 2026-11-03 13:38:47 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/7008
Author: @mvanhorn
Created: 10/24/2026
Status: ✅ Merged
Merged: 10/24/2026
Merged by: @morluto

Base: main ← Head: fix/pe-tls-load-config


📝 Commits (2)

  • f2346e4 feat(pe): inspect TLS callbacks and load configuration
  • 4654912 fix(pe): retain snapshot facts and bound load-config section lookup

📊 Changes

33 files changed (+2717 additions, -2 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/SKILL.md (+8 -0)
📝 .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md (+13 -0)
📝 docs/.vitepress/config.ts (+1 -0)
📝 docs/cli.md (+10 -0)
📝 docs/index.md (+1 -1)
➕ docs/pe-load-config.md (+52 -0)
➕ docs/verification/pe-load-config-2026-10-24.md (+34 -0)
📝 package.json (+1 -0)
📝 scripts/lib/catalog-builders.mjs (+7 -0)
➕ scripts/verify-pe-load-config.mjs (+82 -0)
📝 src/application/CapabilityInventory.ts (+1 -1)
➕ src/application/binaryDiagnostics/PeLoadConfigPort.ts (+20 -0)
➕ src/application/binaryDiagnostics/PeLoadConfigService.test.ts (+122 -0)
➕ src/application/binaryDiagnostics/PeLoadConfigService.ts (+185 -0)
📝 src/cli/binaryDiagnosticsCommands.ts (+37 -0)
📝 src/cliCommandNames.ts (+1 -0)
📝 src/composition/binaryDiagnostics.ts (+5 -0)
📝 src/contracts/native/binaryDiagnosticsToolContracts.ts (+20 -0)
📝 src/contracts/toolEffects.test.ts (+1 -0)
📝 src/contracts/toolEffects.ts (+1 -0)

...and 13 more files

📄 Description

Summary

TLS callbacks run before a PE image's entry point, and the load configuration declares the security cookie, SafeSEH table and Control Flow Guard metadata. REA can inspect PE imports, resources and certificates, but had no static view of either directory. This adds inspect-pe-load-config (CLI) and inspect_pe_load_config (MCP) for PE32 and PE32+ images, read without executing the image or opening a disassembler. No related issue; this is a new static inspection within existing native authority.

Problem and expected behavior

An analyst asking "what runs before main?" or "which mitigations does this image declare?" today needs llvm-readobj, dumpbin or a disassembler. Expected: REA returns the TLS directory and its null-terminated callback array (each callback VA with RVA, file offset and section), and the load configuration fields its declared Size covers, with flag-derived mitigation claims labeled inferred.

Change and scope

src/native/pe/PeLoadConfigParser.ts reuses the shared PE image reader and file-mapping index. Callback VAs are hexadecimal strings to keep 64-bit precision; unmapped targets keep their VA with an unknown file offset. Load-config fields are reported only when Size covers the whole field.

MSVC x86 images commonly declare a 64-byte load-config data directory while the structure's Size is larger (188 bytes on the sample below). Like the Windows loader, the parser reads Size bytes when they are file-backed in one run and reports both values; otherwise it fails as malformed.

Budgets: 64 MiB file read (ceiling 512 MiB), 64 callbacks (ceiling 4096). Truncated, ambiguous or unmapped directory storage fails closed with no partial success. Not included: decoding CHPE or dynamic relocation tables, or verifying that CFG instructions are present. AI was used for assistance.

Follow the contribution scope.
Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the readiness and merge checklists
unchecked. Maintainers can help resolve open questions.

Contract and boundary impact

  • Semantic owner and earliest changed stage: native PE inspection (src/native/pe/) and the binary diagnostics service.
  • Contribution scope fit and linked maintainer decision: static format inspection of a local file within existing native authority; separate scope decision not applicable.
  • CLI and MCP/tool-catalog contract: new inspect-pe-load-config command and inspect_pe_load_config tool with path, max_file_bytes and max_callbacks inputs.
  • Provider, bridge, target-format, or platform compatibility: portable TypeScript, no provider or Windows host.
  • Evidence, artifact, provenance, or reconstruction contract: Evidence carries artifact SHA-256, directory and callback-array file ranges; guard-flag mitigations carry evidence_kind: "inferred".
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): unchanged; reads a stable regular file.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs and skills/; edit authored instructions in .agents/skills/reverse-engineer-anything/ and never commit build outputs): catalog builder entry, authored skill guidance in .agents/skills/reverse-engineer-anything/, and a verify:pe:load-config package script. No committed build outputs.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • 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: src/native/pe/PeLoadConfigParser.test.ts (PE32 and PE32+ synthetic images: callbacks, Size-covered fields, the legacy 64-byte PE32 directory, budgets, malformed and ambiguous storage), PeLoadConfigService.test.ts, tests/acceptance/peLoadConfigCli.test.ts, tests/boundary/mcp/peLoadConfigMcp.test.ts, tests/boundary/filesystem/peLoadConfig.test.ts, and the shared tool-contract and stable-artifact lists. Fixtures are synthetic; no binaries are committed.
  • Base reproduction or other evidence: the released CLI has no TLS or load-config inspection. Cross-checked against llvm-readobj --coff-tls-directory and --coff-load-config on local, uncommitted binaries: a MinGW-linked PE32+ TLS sample, and Sysinternals strings64.exe and strings.exe (MSVC). Details are in docs/verification/pe-load-config-2026-10-24.md.
  • User-visible CLI/MCP output (if applicable): see below.
  • Remaining proof gaps: no real image with XFG or EH-continuation tables was exercised (synthetic coverage only). Runtime enforcement of any declared mitigation is not verified.
$ rea inspect-pe-load-config tls64.exe --json | jq ...
format: pe32-plus   tls.alignment: 8
callbacks: 0x140001000 .text  0x140001020 .text  0x140001470 .text  0x140001500 .text

$ rea inspect-pe-load-config strings.exe --json | jq ...
format: pe32   directory.size: 64   size: 188
security_cookie: 0x4560dc (.data)   se_handler_table: 0x453b28  count: 7
guard_flags: 0x100 IMAGE_GUARD_CF_INSTRUMENTED (inferred)

llvm-readobj --coff-load-config strings.exe reports Size: 0xBC, SecurityCookie: 0x4560DC, SEHandlerTable: 0x453B28, SEHandlerCount: 7, GuardFlags: 0x100.

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

Head f2346e4d5 on main d361c53c5; macOS arm64, Node 26.5.0, npm 12.2.0:

  • npx --no-install vitest run src/application/binaryDiagnostics/PeLoadConfigService.test.ts src/contracts/toolEffects.test.ts src/native/pe/PeLoadConfigParser.test.ts tests/acceptance/peLoadConfigCli.test.ts tests/acceptance/tools/localToolContracts.test.ts tests/boundary/filesystem/peLoadConfig.test.ts tests/boundary/mcp/peLoadConfigMcp.test.ts tests/process-global/stableArtifactConsumers.test.ts - passed.
  • npm run verify:pe:load-config with Zig 0.16.0 - PE32 and PE32+ samples inspected; neither image was started.
  • Sysinternals strings.exe (PE32, MSVC) matches the llvm-readobj fields shown above.

Compatibility, safety, and release

  • Breaking changes or migration steps: none; new command and tool only.
  • Real Hopper/Ghidra, browser, or OS coverage: macOS arm64; no provider involved.
  • Package or release metadata impact: one new package script (verify:pe:load-config).
  • Security, privacy, process, or containment review: the image is never executed; reads are bounded by named budgets and malformed storage fails closed.

Author readiness

Human owner: @mvanhorn

  • 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.

Demo

demo


🔄 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/7008 **Author:** [@mvanhorn](https://github.com/mvanhorn) **Created:** 10/24/2026 **Status:** ✅ Merged **Merged:** 10/24/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/pe-tls-load-config` --- ### 📝 Commits (2) - [`f2346e4`](https://github.com/morluto/rea/commit/f2346e4d5ced924d870874b263f693d8e6827f37) feat(pe): inspect TLS callbacks and load configuration - [`4654912`](https://github.com/morluto/rea/commit/465491220edcd70799fbacfb295d736e36e1c819) fix(pe): retain snapshot facts and bound load-config section lookup ### 📊 Changes **33 files changed** (+2717 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/SKILL.md` (+8 -0) 📝 `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md` (+13 -0) 📝 `docs/.vitepress/config.ts` (+1 -0) 📝 `docs/cli.md` (+10 -0) 📝 `docs/index.md` (+1 -1) ➕ `docs/pe-load-config.md` (+52 -0) ➕ `docs/verification/pe-load-config-2026-10-24.md` (+34 -0) 📝 `package.json` (+1 -0) 📝 `scripts/lib/catalog-builders.mjs` (+7 -0) ➕ `scripts/verify-pe-load-config.mjs` (+82 -0) 📝 `src/application/CapabilityInventory.ts` (+1 -1) ➕ `src/application/binaryDiagnostics/PeLoadConfigPort.ts` (+20 -0) ➕ `src/application/binaryDiagnostics/PeLoadConfigService.test.ts` (+122 -0) ➕ `src/application/binaryDiagnostics/PeLoadConfigService.ts` (+185 -0) 📝 `src/cli/binaryDiagnosticsCommands.ts` (+37 -0) 📝 `src/cliCommandNames.ts` (+1 -0) 📝 `src/composition/binaryDiagnostics.ts` (+5 -0) 📝 `src/contracts/native/binaryDiagnosticsToolContracts.ts` (+20 -0) 📝 `src/contracts/toolEffects.test.ts` (+1 -0) 📝 `src/contracts/toolEffects.ts` (+1 -0) _...and 13 more files_ </details> ### 📄 Description ## Summary TLS callbacks run before a PE image's entry point, and the load configuration declares the security cookie, SafeSEH table and Control Flow Guard metadata. REA can inspect PE imports, resources and certificates, but had no static view of either directory. This adds `inspect-pe-load-config` (CLI) and `inspect_pe_load_config` (MCP) for PE32 and PE32+ images, read without executing the image or opening a disassembler. No related issue; this is a new static inspection within existing native authority. ## Problem and expected behavior An analyst asking "what runs before `main`?" or "which mitigations does this image declare?" today needs `llvm-readobj`, `dumpbin` or a disassembler. Expected: REA returns the TLS directory and its null-terminated callback array (each callback VA with RVA, file offset and section), and the load configuration fields its declared `Size` covers, with flag-derived mitigation claims labeled inferred. ## Change and scope `src/native/pe/PeLoadConfigParser.ts` reuses the shared PE image reader and file-mapping index. Callback VAs are hexadecimal strings to keep 64-bit precision; unmapped targets keep their VA with an unknown file offset. Load-config fields are reported only when `Size` covers the whole field. MSVC x86 images commonly declare a 64-byte load-config data directory while the structure's `Size` is larger (188 bytes on the sample below). Like the Windows loader, the parser reads `Size` bytes when they are file-backed in one run and reports both values; otherwise it fails as malformed. Budgets: 64 MiB file read (ceiling 512 MiB), 64 callbacks (ceiling 4096). Truncated, ambiguous or unmapped directory storage fails closed with no partial success. Not included: decoding CHPE or dynamic relocation tables, or verifying that CFG instructions are present. AI was used for assistance. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). Unresolved scope, authority, side effects, or required verification block integration. Link the required maintainer scope decision for new providers or capabilities involving target execution, injection, mutation, or protection recovery; ordinary fixes within existing boundaries may state "not applicable". Draft PRs may leave the scope decision pending and the readiness and merge checklists unchecked. Maintainers can help resolve open questions. ## Contract and boundary impact - Semantic owner and earliest changed stage: native PE inspection (`src/native/pe/`) and the binary diagnostics service. - Contribution scope fit and linked maintainer decision: static format inspection of a local file within existing native authority; separate scope decision not applicable. - CLI and MCP/tool-catalog contract: new `inspect-pe-load-config` command and `inspect_pe_load_config` tool with `path`, `max_file_bytes` and `max_callbacks` inputs. - Provider, bridge, target-format, or platform compatibility: portable TypeScript, no provider or Windows host. - Evidence, artifact, provenance, or reconstruction contract: Evidence carries artifact SHA-256, directory and callback-array file ranges; guard-flag mitigations carry `evidence_kind: "inferred"`. - Target selection, authority, upstream behavior, and cleanup details (if applicable): unchanged; reads a stable regular file. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): catalog builder entry, authored skill guidance in `.agents/skills/reverse-engineer-anything/`, and a `verify:pe:load-config` package script. No committed build outputs. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] 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: `src/native/pe/PeLoadConfigParser.test.ts` (PE32 and PE32+ synthetic images: callbacks, Size-covered fields, the legacy 64-byte PE32 directory, budgets, malformed and ambiguous storage), `PeLoadConfigService.test.ts`, `tests/acceptance/peLoadConfigCli.test.ts`, `tests/boundary/mcp/peLoadConfigMcp.test.ts`, `tests/boundary/filesystem/peLoadConfig.test.ts`, and the shared tool-contract and stable-artifact lists. Fixtures are synthetic; no binaries are committed. - Base reproduction or other evidence: the released CLI has no TLS or load-config inspection. Cross-checked against `llvm-readobj --coff-tls-directory` and `--coff-load-config` on local, uncommitted binaries: a MinGW-linked PE32+ TLS sample, and Sysinternals `strings64.exe` and `strings.exe` (MSVC). Details are in `docs/verification/pe-load-config-2026-10-24.md`. - User-visible CLI/MCP output (if applicable): see below. - Remaining proof gaps: no real image with XFG or EH-continuation tables was exercised (synthetic coverage only). Runtime enforcement of any declared mitigation is not verified. ```text $ rea inspect-pe-load-config tls64.exe --json | jq ... format: pe32-plus tls.alignment: 8 callbacks: 0x140001000 .text 0x140001020 .text 0x140001470 .text 0x140001500 .text $ rea inspect-pe-load-config strings.exe --json | jq ... format: pe32 directory.size: 64 size: 188 security_cookie: 0x4560dc (.data) se_handler_table: 0x453b28 count: 7 guard_flags: 0x100 IMAGE_GUARD_CF_INSTRUMENTED (inferred) ``` `llvm-readobj --coff-load-config strings.exe` reports `Size: 0xBC`, `SecurityCookie: 0x4560DC`, `SEHandlerTable: 0x453B28`, `SEHandlerCount: 7`, `GuardFlags: 0x100`. 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 Head `f2346e4d5` on main `d361c53c5`; macOS arm64, Node 26.5.0, npm 12.2.0: - `npx --no-install vitest run src/application/binaryDiagnostics/PeLoadConfigService.test.ts src/contracts/toolEffects.test.ts src/native/pe/PeLoadConfigParser.test.ts tests/acceptance/peLoadConfigCli.test.ts tests/acceptance/tools/localToolContracts.test.ts tests/boundary/filesystem/peLoadConfig.test.ts tests/boundary/mcp/peLoadConfigMcp.test.ts tests/process-global/stableArtifactConsumers.test.ts` - passed. - `npm run verify:pe:load-config` with Zig 0.16.0 - PE32 and PE32+ samples inspected; neither image was started. - Sysinternals `strings.exe` (PE32, MSVC) matches the `llvm-readobj` fields shown above. ## Compatibility, safety, and release - Breaking changes or migration steps: none; new command and tool only. - Real Hopper/Ghidra, browser, or OS coverage: macOS arm64; no provider involved. - Package or release metadata impact: one new package script (`verify:pe:load-config`). - Security, privacy, process, or containment review: the image is never executed; reads are bounded by named budgets and malformed storage fails closed. ## Author readiness Human owner: @mvanhorn - [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. ## Demo ![demo](https://raw.githubusercontent.com/mvanhorn/pr-demo-assets/main/rea/2026-10-24-012-feat-pe-tls-load-config/preview.gif) <video src="https://mvanhorn.github.io/pr-demo-assets/rea/2026-10-24-012-feat-pe-tls-load-config/out.mp4" controls width="720"></video> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-03 13:38:47 +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#7568
No description provided.