[GH-ISSUE #1596] [Bug] Managed RVAs past section virtual size decode as present #538

Closed
opened 2026-10-15 18:25:59 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @Adolanium on GitHub (Oct 10, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1596

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

Managed RVA mapping treats a section as max(VirtualSize, SizeOfRawData) bytes and keeps the first match. An RVA in the raw padding past VirtualSize is converted to PointerToRawData + (RVA - VirtualAddress).

For a section at virtual address 0x2000 with raw offset 0x200, RVA 0x2010 in that padding maps to file offset 0x210. A later section at virtual address 0x3000 can be shadowed by the earlier raw size and map to file offset 0x1200.

rvaAvailableBytes returns the rest of SizeOfRawData. methodBody accepts that extent. When the CIL bytes decode, the method status is present and normalized_il_sha256 is set. docs/managed-code-analysis.md says that hash exists only for status present, meaning the entire CIL stream decoded.

VirtualSize 0 is treated as the raw size, so the section still addresses its file bytes.

Reproduction

  1. Build the managed PE fixture with section virtual address 0x2000, raw offset 0x200, and VirtualSize 0x800. Leave SizeOfRawData large enough to cover the method at file offset 0x0a00.
  2. The CLI header stays at RVA 0x2000 and the metadata root at RVA 0x2100. The method RVA stays 0x2800, the first byte outside [0x2000, 0x2800).
  3. Run inspect_managed_members on those bytes.
  4. Observe method status present and a normalized CIL hash taken from the padding.

Expected behavior

A section addresses [virtualAddress, virtualAddress + virtualSize). The loader's file view of that range is min(VirtualSize, SizeOfRawData). Raw bytes past VirtualSize are file-alignment padding and belong to a later section only when that section's virtual range contains the RVA.

An RVA inside VirtualSize and past SizeOfRawData still fails with the existing "occupies virtual bytes without file-backed data" error. VirtualSize 0 addresses no RVA. A method whose RVA is outside the section is malformed, with normalized_il_sha256 null, and member coverage is partial. A method that sits inside both sizes stays present.

Impact and scope

inspect_managed_members can report decoded CIL and a normalized hash for bytes the section does not virtually contain, and it can hide a later section behind an earlier section's raw padding. Metadata and CLI headers that already lie inside VirtualSize are unaffected. The reader does not execute the image.

REA version

6.3.0, revision 02ef2b1c41e80364b6482e4586ae40e575c6d5a5

Runtime and platform

Node 22 or 24. PE parsing is platform-independent.

Target and artifact context

Synthetic PE32/CLI fixture. No third-party assembly.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Method status present and a non-null normalized_il_sha256 for RVA 0x2800 when the section VirtualSize ends at that RVA and the raw bytes still hold the body.

Typed outcome or recovery expected

Method status malformed, normalized_il_sha256 null, and coverage state partial. Issue text method.body RVA is not covered by a PE section.

Analysis or suspected cause

Source-derived. mapRva in src/dotnet/ManagedPeReader.ts sets mappedSize to Math.max(section.virtualSize, section.rawSize) and returns on the first section that contains the RVA. rvaAvailableBytes then uses the remaining rawSize.

Additional context

The fix is in https://github.com/morluto/rea/pull/1595. No existing issue describes this mapping.

Originally created by @Adolanium on GitHub (Oct 10, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1596 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior Managed RVA mapping treats a section as `max(VirtualSize, SizeOfRawData)` bytes and keeps the first match. An RVA in the raw padding past `VirtualSize` is converted to `PointerToRawData + (RVA - VirtualAddress)`. For a section at virtual address `0x2000` with raw offset `0x200`, RVA `0x2010` in that padding maps to file offset `0x210`. A later section at virtual address `0x3000` can be shadowed by the earlier raw size and map to file offset `0x1200`. `rvaAvailableBytes` returns the rest of `SizeOfRawData`. `methodBody` accepts that extent. When the CIL bytes decode, the method status is `present` and `normalized_il_sha256` is set. `docs/managed-code-analysis.md` says that hash exists only for status `present`, meaning the entire CIL stream decoded. `VirtualSize` 0 is treated as the raw size, so the section still addresses its file bytes. ### Reproduction 1. Build the managed PE fixture with section virtual address `0x2000`, raw offset `0x200`, and `VirtualSize` `0x800`. Leave `SizeOfRawData` large enough to cover the method at file offset `0x0a00`. 2. The CLI header stays at RVA `0x2000` and the metadata root at RVA `0x2100`. The method RVA stays `0x2800`, the first byte outside `[0x2000, 0x2800)`. 3. Run `inspect_managed_members` on those bytes. 4. Observe method status `present` and a normalized CIL hash taken from the padding. ### Expected behavior A section addresses `[virtualAddress, virtualAddress + virtualSize)`. The loader's file view of that range is `min(VirtualSize, SizeOfRawData)`. Raw bytes past `VirtualSize` are file-alignment padding and belong to a later section only when that section's virtual range contains the RVA. An RVA inside `VirtualSize` and past `SizeOfRawData` still fails with the existing "occupies virtual bytes without file-backed data" error. `VirtualSize` 0 addresses no RVA. A method whose RVA is outside the section is `malformed`, with `normalized_il_sha256` null, and member coverage is `partial`. A method that sits inside both sizes stays `present`. ### Impact and scope `inspect_managed_members` can report decoded CIL and a normalized hash for bytes the section does not virtually contain, and it can hide a later section behind an earlier section's raw padding. Metadata and CLI headers that already lie inside `VirtualSize` are unaffected. The reader does not execute the image. ### REA version 6.3.0, revision `02ef2b1c41e80364b6482e4586ae40e575c6d5a5` ### Runtime and platform Node 22 or 24. PE parsing is platform-independent. ### Target and artifact context Synthetic PE32/CLI fixture. No third-party assembly. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Method `status` `present` and a non-null `normalized_il_sha256` for RVA `0x2800` when the section `VirtualSize` ends at that RVA and the raw bytes still hold the body. ### Typed outcome or recovery expected Method `status` `malformed`, `normalized_il_sha256` null, and coverage `state` `partial`. Issue text `method.body RVA is not covered by a PE section`. ### Analysis or suspected cause Source-derived. `mapRva` in `src/dotnet/ManagedPeReader.ts` sets `mappedSize` to `Math.max(section.virtualSize, section.rawSize)` and returns on the first section that contains the RVA. `rvaAvailableBytes` then uses the remaining `rawSize`. ### Additional context The fix is in https://github.com/morluto/rea/pull/1595. No existing issue describes this mapping.
bsakaguchi 2026-10-15 18:25:59 +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#538
No description provided.