[GH-ISSUE #4057] [Feature] Accept a PE/ELF export inventory as native Evidence in verify_managed_native_boundaries #1607

Closed
opened 2026-10-16 21:07:53 +00:00 by bsakaguchi · 4 comments
Owner

Originally created by @Rinkia on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4057

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal (PE exports, export table, DLL exports, verify_managed_native_boundaries): none found. #3351 and #3961/#3974 concern matching rules, not the evidence sources.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Evidence, artifacts, or reconstruction

Current limitation

verify_managed_native_boundaries accepts two native observation types (managedNativeVerificationMatch.ts, symbolsForEvidence):

  • inspect_macho exports. On Linux, rea inspect-macho returns capability_unavailable ("Native macOS utilities require macOS.").
  • analyze_function dossiers, one function per record.

Most P/Invokes target Windows DLLs or Linux .so files. On a non-macOS host, the only route is one analyze_function per declared import. Other Evidence that already holds the export table counts as unsupported and makes coverage partial. That includes inspect_with_objdump … symbols, which works on PE and ELF but returns raw output only.

LibGit2Sharp 0.31.0 lib/net8.0/LibGit2Sharp.dll declares 369 P/Invokes into git2-3f4182d. Against the matching LibGit2Sharp.NativeBinaries 2.0.323 libraries:

Native Evidence supplied accepted verified time
rea function libgit2-3f4182d.so git_libgit2_init --provider ghidra (ELF) 1 1 of 369 88 s (CLI, fresh import)
rea function git2-3f4182d.dll git_libgit2_init --provider ghidra (PE) 1 1 of 369 146 s (CLI, fresh import)
rea inspect-with-objdump libgit2-3f4182d.so symbols 0 (unsupported) 0 seconds

The objdump output REA retained contains all 369 declared entry points (checked by name against its symbol listing). Verifying them through the supported route takes 369 separate analyze_function records. MCP keeps the Ghidra session, so later calls are cheaper than the CLI timings above, but it's still 369 calls.

Use cases

  • Checking that a .NET app's P/Invoke surface matches the native libraries it ships (NuGet runtimes/*/native, app folders) on Windows and Linux hosts.
  • Comparing the same managed assembly against the win-x64, linux-x64 and osx builds of its native dependency.

Desired outcome and success criteria

  • One native observation per library supplies its whole export set (PE export directory names and ordinals; ELF .dynsym defined global functions), bound to the library's path and SHA-256, so verified can reach 369/369 in one call.
  • The producer works on any host, like the managed parsers.
  • Forwarded PE exports, ordinal-only exports and versioned ELF symbols stay distinguishable (e.g. inferred or explicitly unsupported) and aren't reported as exact matches.

Expected interaction with targets

Static parsing of the native file's export/dynamic symbol tables. No loading or execution.

Target and scope details (optional)

  • libgit2sharp.0.31.0.nupkg sha256 02fbec03776c0db706363a715973b91758b0f1b86b72d696434303da8252c890; lib/net8.0/LibGit2Sharp.dll sha256 28b694519a8a09bff6db8420236f9e835ec8b504d68bea76aa60beb5a4c41083.
  • libgit2sharp.nativebinaries.2.0.323.nupkg sha256 d2a16ac8d0b4bb4e5417e0c9fcb36f9e0e52babd6bc9c8bec0810685553feeb1; linux-x64/native/libgit2-3f4182d.so sha256 b1891c259a36c65a7b0a2de180282129d9f26d8537e1f7605ed8ccf34d90e020; win-x64/native/git2-3f4182d.dll sha256 44d849a123b1c8ada5aa893dbc6ae3702212c4a027c3ea76b90411c47918ede5.
  • Both packages' NuGet SHA-512 hashes were verified against the nuget.org catalog. MIT / GPLv2 with linking exception.

Verification available so far

Executed on rea-agents@6.5.0 (Linux Docker image). The accepted-operation list is unchanged on main @ 337d2e33c (source-derived).

Verification details (optional)

Module identity already works for both platforms: the declaration git2-3f4182d matched libgit2-3f4182d.so (lib prefix stripped for .so) and git2-3f4182d.dll, both with basis: exact-function-name.

Alternatives considered

  • Parsing inspect_with_objdump raw output in the verifier. That couples it to GNU objdump text formats, so a structured producer seems better.
  • inspect_binary_layout (ELF only, and it needs a caller-supplied pwntools Python).

Possible direction

A small host-independent export reader (PE export directory; ELF dynamic symbols) returning the same { name, address, … } items as inspect_macho exports. Add it to symbolsForEvidence as a third accepted operation with its own source value.

References

  • src/domain/managed/managedNativeVerificationMatch.ts (symbolsForEvidence, normalizeModule)
  • docs/managed-code-analysis.md ("Managed/native boundary rules")
Originally created by @Rinkia on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4057 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal (PE exports, export table, DLL exports, verify_managed_native_boundaries): none found. #3351 and #3961/#3974 concern matching rules, not the evidence sources. - [x] I understand the contribution scope must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Current limitation `verify_managed_native_boundaries` accepts two native observation types (`managedNativeVerificationMatch.ts`, `symbolsForEvidence`): - `inspect_macho` exports. On Linux, `rea inspect-macho` returns `capability_unavailable` ("Native macOS utilities require macOS."). - `analyze_function` dossiers, one function per record. Most P/Invokes target Windows DLLs or Linux `.so` files. On a non-macOS host, the only route is one `analyze_function` per declared import. Other Evidence that already holds the export table counts as `unsupported` and makes coverage `partial`. That includes `inspect_with_objdump … symbols`, which works on PE and ELF but returns raw output only. LibGit2Sharp 0.31.0 `lib/net8.0/LibGit2Sharp.dll` declares 369 P/Invokes into `git2-3f4182d`. Against the matching `LibGit2Sharp.NativeBinaries` 2.0.323 libraries: | Native Evidence supplied | accepted | verified | time | | --- | --- | --- | --- | | `rea function libgit2-3f4182d.so git_libgit2_init --provider ghidra` (ELF) | 1 | 1 of 369 | 88 s (CLI, fresh import) | | `rea function git2-3f4182d.dll git_libgit2_init --provider ghidra` (PE) | 1 | 1 of 369 | 146 s (CLI, fresh import) | | `rea inspect-with-objdump libgit2-3f4182d.so symbols` | 0 (unsupported) | 0 | seconds | The objdump output REA retained contains all 369 declared entry points (checked by name against its symbol listing). Verifying them through the supported route takes 369 separate `analyze_function` records. MCP keeps the Ghidra session, so later calls are cheaper than the CLI timings above, but it's still 369 calls. ### Use cases - Checking that a .NET app's P/Invoke surface matches the native libraries it ships (NuGet `runtimes/*/native`, app folders) on Windows and Linux hosts. - Comparing the same managed assembly against the win-x64, linux-x64 and osx builds of its native dependency. ### Desired outcome and success criteria - One native observation per library supplies its whole export set (PE export directory names and ordinals; ELF `.dynsym` defined global functions), bound to the library's path and SHA-256, so `verified` can reach 369/369 in one call. - The producer works on any host, like the managed parsers. - Forwarded PE exports, ordinal-only exports and versioned ELF symbols stay distinguishable (e.g. `inferred` or explicitly unsupported) and aren't reported as exact matches. ### Expected interaction with targets Static parsing of the native file's export/dynamic symbol tables. No loading or execution. ### Target and scope details (optional) - `libgit2sharp.0.31.0.nupkg` sha256 `02fbec03776c0db706363a715973b91758b0f1b86b72d696434303da8252c890`; `lib/net8.0/LibGit2Sharp.dll` sha256 `28b694519a8a09bff6db8420236f9e835ec8b504d68bea76aa60beb5a4c41083`. - `libgit2sharp.nativebinaries.2.0.323.nupkg` sha256 `d2a16ac8d0b4bb4e5417e0c9fcb36f9e0e52babd6bc9c8bec0810685553feeb1`; `linux-x64/native/libgit2-3f4182d.so` sha256 `b1891c259a36c65a7b0a2de180282129d9f26d8537e1f7605ed8ccf34d90e020`; `win-x64/native/git2-3f4182d.dll` sha256 `44d849a123b1c8ada5aa893dbc6ae3702212c4a027c3ea76b90411c47918ede5`. - Both packages' NuGet SHA-512 hashes were verified against the nuget.org catalog. MIT / GPLv2 with linking exception. ### Verification available so far Executed on `rea-agents@6.5.0` (Linux Docker image). The accepted-operation list is unchanged on `main` @ `337d2e33c` (source-derived). ### Verification details (optional) Module identity already works for both platforms: the declaration `git2-3f4182d` matched `libgit2-3f4182d.so` (`lib` prefix stripped for `.so`) and `git2-3f4182d.dll`, both with `basis: exact-function-name`. ### Alternatives considered - Parsing `inspect_with_objdump` raw output in the verifier. That couples it to GNU objdump text formats, so a structured producer seems better. - `inspect_binary_layout` (ELF only, and it needs a caller-supplied pwntools Python). ### Possible direction A small host-independent export reader (PE export directory; ELF dynamic symbols) returning the same `{ name, address, … }` items as `inspect_macho` exports. Add it to `symbolsForEvidence` as a third accepted operation with its own `source` value. ### References - `src/domain/managed/managedNativeVerificationMatch.ts` (`symbolsForEvidence`, `normalizeModule`) - `docs/managed-code-analysis.md` ("Managed/native boundary rules")
Author
Owner

@XinwangTao commented on GitHub (Oct 15, 2026):

This would be a big win for non-macOS hosts — the 369-vs-1 call gap you measured makes the pain point concrete, and the use case of diffing the same managed assembly against win-x64/linux-x64/osx builds is a good motivation. Agree that a structured, host-independent export reader is better than coupling the verifier to GNU objdump text output; that also keeps forwarded/ordinal-only/versioned symbols distinguishable from exact matches, consistent with the matching rules from #3961/#3974. Worth checking whether the PE/ELF parsing already present in inspect_binary_layout or the fixture builders can be reused so the new operation doesn't grow a second parser.

<!-- gh-comment-id:6189493715 --> @XinwangTao commented on GitHub (Oct 15, 2026): This would be a big win for non-macOS hosts — the 369-vs-1 call gap you measured makes the pain point concrete, and the use case of diffing the same managed assembly against win-x64/linux-x64/osx builds is a good motivation. Agree that a structured, host-independent export reader is better than coupling the verifier to GNU objdump text output; that also keeps forwarded/ordinal-only/versioned symbols distinguishable from exact matches, consistent with the matching rules from #3961/#3974. Worth checking whether the PE/ELF parsing already present in inspect_binary_layout or the fixture builders can be reused so the new operation doesn't grow a second parser.
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 22, 2026):

Picking this up. Proposal, split into two reviewable PRs:

  1. Producer — inspect_native_exports (CLI inspect-native-exports <path>): host-independent static read of one PE or ELF file, built on the existing src/native/metadata PE/ELF container readers (no objdump, no provider, no loading). Evidence is bound to the file path and SHA-256. Each export row keeps what the format states, without normalising it into a plain name:
    • PE: name (null for ordinal-only), ordinal, address (null for forwarders), forwarder ("DLL.Func" or null), plus the export directory's DLL name and ordinal base.
    • ELF: defined STB_GLOBAL/STB_WEAK .dynsym entries with name, address, type (function/ifunc/object/other), binding, and version ({name, hidden} from .gnu.version/.gnu.version_d, or null).
    • Coverage is complete only when the export table / dynamic symbol table was fully read. A missing table is reported as unsupported with a reason rather than as an empty export set, and malformed entries are counted rather than dropped.
  2. Verification — accept that Evidence in verify_managed_native_boundaries: an exact, named, non-forwarded, unversioned-or-default-version export is verified/exact-export-name. Forwarded exports, ordinal-only exports and non-default (hidden) versioned ELF symbols are kept distinguishable and never reported as exact matches (inferred, with the reason in limitations).

I'll measure on the LibGit2Sharp 0.31.0 / NativeBinaries 2.0.323 artifacts from the issue (369 declared P/Invokes).

<!-- gh-comment-id:6334133152 --> @kaoru0822-kitauji commented on GitHub (Oct 22, 2026): Picking this up. Proposal, split into two reviewable PRs: 1. **Producer — `inspect_native_exports` (CLI `inspect-native-exports <path>`)**: host-independent static read of one PE or ELF file, built on the existing `src/native/metadata` PE/ELF container readers (no objdump, no provider, no loading). Evidence is bound to the file path and SHA-256. Each export row keeps what the format states, without normalising it into a plain name: - PE: `name` (null for ordinal-only), `ordinal`, `address` (null for forwarders), `forwarder` (`"DLL.Func"` or null), plus the export directory's DLL name and ordinal base. - ELF: defined `STB_GLOBAL`/`STB_WEAK` `.dynsym` entries with `name`, `address`, `type` (`function`/`ifunc`/`object`/`other`), `binding`, and `version` (`{name, hidden}` from `.gnu.version`/`.gnu.version_d`, or null). - Coverage is complete only when the export table / dynamic symbol table was fully read. A missing table is reported as `unsupported` with a reason rather than as an empty export set, and malformed entries are counted rather than dropped. 2. **Verification — accept that Evidence in `verify_managed_native_boundaries`**: an exact, named, non-forwarded, unversioned-or-default-version export is `verified/exact-export-name`. Forwarded exports, ordinal-only exports and non-default (hidden) versioned ELF symbols are kept distinguishable and never reported as exact matches (`inferred`, with the reason in limitations). I'll measure on the LibGit2Sharp 0.31.0 / NativeBinaries 2.0.323 artifacts from the issue (369 declared P/Invokes).
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

Nice split — keeping the producer host-independent and the matcher honest is exactly right. Particularly good that a missing export table is reported as unsupported with a reason rather than an empty set, and that malformed entries are counted rather than dropped; that keeps non-exact exports distinguishable from exact matches, consistent with the #3961/#3974 matching rules. Looking forward to the 369/369 measurement on LibGit2Sharp.

<!-- gh-comment-id:6335861017 --> @XinwangTao commented on GitHub (Oct 22, 2026): Nice split — keeping the producer host-independent and the matcher honest is exactly right. Particularly good that a missing export table is reported as unsupported with a reason rather than an empty set, and that malformed entries are counted rather than dropped; that keeps non-exact exports distinguishable from exact matches, consistent with the #3961/#3974 matching rules. Looking forward to the 369/369 measurement on LibGit2Sharp.
Author
Owner

@Rinkia commented on GitHub (Oct 26, 2026):

Tried the new inspect-native-exports (#6278) on rea-agents@6.8.0. The producer works. Verification doesn't use it yet, so the original goal is still open.

Producer. On libgit2-3f4182d.so (LibGit2Sharp.NativeBinaries 2.0.323, linux-x64), it reports 944 exports. That equals objdump -T's defined DF/DO symbols (944). Rows carry value, size, type and binding (for example git_merge_file_result_free, function, global, 0x8a3c0, size 49). Thanks, kaoru0822-kitauji.

Consumer. LibGit2Sharp 0.31.0 lib/net8.0/LibGit2Sharp.dll declares 355 distinct P/Invoke import names, and 353 of them are in that export table. Passing the export Evidence as the only native_observations entry to verify-managed-native-boundaries gives:

{"verified":0,"inferred":0,"unresolved":371,"contradicted":0,"native_body_unresolved":0}

Passing one analyze_function record instead still gives verified: 1. So export rows aren't matched yet; the 6.8.0 docs don't describe export Evidence as a verification input either.

The proposal on this issue was two PRs, producer then matcher. Only the producer is merged, and the issue closed with it. Is the matcher tracked somewhere else, or should this be reopened for it?

<!-- gh-comment-id:6412666134 --> @Rinkia commented on GitHub (Oct 26, 2026): Tried the new `inspect-native-exports` (#6278) on `rea-agents@6.8.0`. The producer works. Verification doesn't use it yet, so the original goal is still open. **Producer.** On `libgit2-3f4182d.so` (LibGit2Sharp.NativeBinaries 2.0.323, linux-x64), it reports 944 exports. That equals `objdump -T`'s defined `DF`/`DO` symbols (944). Rows carry value, size, type and binding (for example `git_merge_file_result_free`, function, global, `0x8a3c0`, size 49). Thanks, kaoru0822-kitauji. **Consumer.** LibGit2Sharp 0.31.0 `lib/net8.0/LibGit2Sharp.dll` declares 355 distinct P/Invoke import names, and 353 of them are in that export table. Passing the export Evidence as the only `native_observations` entry to `verify-managed-native-boundaries` gives: ```json {"verified":0,"inferred":0,"unresolved":371,"contradicted":0,"native_body_unresolved":0} ``` Passing one `analyze_function` record instead still gives `verified: 1`. So export rows aren't matched yet; the 6.8.0 docs don't describe export Evidence as a verification input either. The proposal on this issue was two PRs, producer then matcher. Only the producer is merged, and the issue closed with it. Is the matcher tracked somewhere else, or should this be reopened for it?
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#1607
No description provided.