Skip to content

feat: add MSVC RTTI walker and module-range helpers#85

Merged
tkhquang merged 3 commits into
mainfrom
feat/rtti-walker-and-module-utils
May 26, 2026
Merged

feat: add MSVC RTTI walker and module-range helpers#85
tkhquang merged 3 commits into
mainfrom
feat/rtti-walker-and-module-utils

Conversation

@tkhquang
Copy link
Copy Markdown
Owner

@tkhquang tkhquang commented May 26, 2026

Summary

  • New DetourModKit::Rtti module that walks MSVC x64 RTTI from a runtime vtable (vtable[-1] -> COL -> TypeDescriptor) to recover the mangled type name without typeid or dynamic_cast. Image base is recovered via COL.pSelf (canonical IDA/Ghidra approach), with GetModuleHandleExW only as a fallback. Entry points: type_name_of, type_name_into (zero-alloc), vtable_is_type, find_in_pointer_table (optional caller-owned vtable cache, configurable stride).
  • DetourModKit::Memory extended with typed SEH-guarded reads (seh_read<T>, seh_read_bytes) and PE module-range queries (ModuleRange, module_range_for, own_module_range, host_module_range, contains). MSVC uses a single __try frame; MinGW falls back to per-region VirtualQuery.
  • 62 new tests (33 RttiTest, 29 MemoryTest) using a synthetic MSVC RTTI fixture so the walker is validated under both toolchains.
  • Docs: docs/misc/rtti-walker.md (ABI layout, usage patterns, perf notes) plus README and AGENTS updates.

Test plan

  • MinGW debug full suite: 1153 / 1153 pass
  • MSVC debug new tests: 63 / 63 pass
  • MSVC debug full suite: 1150 / 1150 pass (3 pre-existing ConfigTest ordering failures unrelated to this change excluded)
  • No new compiler warnings on either toolchain

Summary by CodeRabbit

  • New Features

    • Added MSVC RTTI introspection for vtable-based type discovery and pointer-table scanning
    • Enhanced memory utilities with SEH-guarded safe reads, module-range queries, and range membership helpers
  • Documentation

    • Expanded README and added a detailed MSVC RTTI walker doc with usage examples
    • Updated project inventory and performance-critical path guidance
  • Tests

    • Added comprehensive tests for new memory and RTTI behaviors

Review Change Stack

- DetourModKit::Rtti: type_name_of, type_name_into, vtable_is_type,
  find_in_pointer_table (with optional caller-owned vtable cache and stride).
- DetourModKit::Memory additions: ModuleRange, module_range_for,
  own_module_range, host_module_range, contains, seh_read<T>, seh_read_bytes.
- 62 new tests using a synthetic MSVC RTTI fixture so the walker is
  validated under both MSVC and MinGW.
- Docs: docs/misc/rtti-walker.md plus README and AGENTS updates.
@tkhquang tkhquang self-assigned this May 26, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e3b03463-19c4-4cdd-a1db-a62e80c0ba52

📥 Commits

Reviewing files that changed from the base of the PR and between c2a9bbf and 10d6673.

📒 Files selected for processing (4)
  • README.md
  • src/memory.cpp
  • src/rtti.cpp
  • tests/test_rtti.cpp
✅ Files skipped from review due to trivial changes (1)
  • README.md

📝 Walkthrough

Walkthrough

This PR introduces a complete MSVC RTTI introspection module with foundational SEH-safe memory utilities. The changes add RTTI-aware vtable type name reading, module range queries, and pointer-table scanning via noexcept APIs that return null/false/0 on failure rather than throwing exceptions.

Changes

MSVC RTTI Introspection Module with Memory Safety

Layer / File(s) Summary
Memory layer: SEH-guarded reads and module range helpers
include/DetourModKit/memory.hpp, src/memory.cpp, tests/test_memory.cpp
ModuleRange struct and utilities for safe memory reading via seh_read_bytes and templated seh_read<T> constrained to trivially copyable types. Module range resolution via PE header parsing with concurrent std::shared_mutex caching for HMODULE lookups. Extensive tests cover null/low/wraparound addresses, page boundaries, freed memory, and cross-module consistency.
RTTI walker: type name and vtable introspection
include/DetourModKit/rtti.hpp, src/rtti.cpp, tests/test_rtti.cpp
Four public APIs: type_name_of (allocating), type_name_into (zero-allocation buffer write), vtable_is_type (exact mangled-name match), and find_in_pointer_table (with optional atomic vtable caching and stride support). Implementation parses MSVC x64 RTTICompleteObjectLocator layout, recovers image base via pSelf RVA, reads NUL-terminated names across page boundaries, and validates all pointer arithmetic. Tests use synthetic RTTI structures to validate behavior across valid/invalid/boundary cases.
Documentation and public header integration
docs/misc/rtti-walker.md, include/DetourModKit.hpp, AGENTS.md, README.md
New rtti-walker.md documents ABI contract, common patterns (identity probing, pointer-table lookup, zero-allocation logging), performance characteristics, and MinGW unsupport rationale. Central header exposes DetourModKit/rtti.hpp and adds DMKRtti namespace alias. Project guides updated with module inventory and performance-critical path expectations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tkhquang/DetourModKit#26: Both PRs update AGENTS.md and README.md to document module structure and performance characteristics.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main features added: MSVC RTTI walker and module-range helpers, which are the primary focus of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/rtti.cpp (1)

18-18: ⚡ Quick win

Remove the file-wide using namespace directive.

Please qualify the DetourModKit symbols here, or use narrow using DetourModKit::Memory; / using DetourModKit::Rtti; declarations instead. As per coding guidelines, **/*.cpp: Do not add using namespace directives in any file. In implementation files, they are not needed if the header already declares the namespace.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rtti.cpp` at line 18, The file-wide "using namespace DetourModKit;" in
rtti.cpp must be removed; instead qualify references to DetourModKit
types/functions (e.g., change bare Memory, Rtti, or any other symbols to
DetourModKit::Memory, DetourModKit::Rtti) or add narrow using declarations like
"using DetourModKit::Memory;" for any frequently used single symbols; update all
occurrences in rtti.cpp accordingly so no global using namespace directive
remains.
tests/test_rtti.cpp (1)

17-17: ⚡ Quick win

Remove the file-wide using namespace directive.

Please qualify the DetourModKit symbols in this test or add narrow using declarations for the specific namespaces you need. As per coding guidelines, **/*.cpp: Do not add using namespace directives in any file. In implementation files, they are not needed if the header already declares the namespace.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_rtti.cpp` at line 17, Remove the file-wide "using namespace
DetourModKit;" from tests/test_rtti.cpp; instead qualify all DetourModKit
symbols with the DetourModKit:: prefix or add narrow using declarations (e.g.,
using DetourModKit::Foo; using DetourModKit::Bar;) for only the specific
types/functions used in this test so no global namespace import remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Line 155: The sentence claiming "failures produce `std::nullopt`" is
misleading because not all RTTI APIs return optionals; update the README
sentence to list the actual return semantics per API (e.g., entry points are
noexcept and SEH-guarded; unreadable pages, missing COLs, and zero RVAs will not
fault but will return `std::nullopt` for functions that return std::optional,
`false` for boolean APIs such as `vtable_is_type`, and `0` for size/length APIs
such as `type_name_into`), and mention these specific symbols (`vtable_is_type`,
`type_name_into`) so readers know which functions map to which return values.

In `@src/rtti.cpp`:
- Around line 81-112: The code trusts COL fields (p_self, p_type_descriptor) and
computes name_addr = image_base + head_opt->p_type_descriptor without validating
that image_base/name_addr lie inside the module that owns vtable; fix by
querying the owning module's base and size (e.g., via GetModuleHandleExW +
GetModuleInformation/VirtualQuery) and reject (return 0) if either the computed
image_base or the final name_addr (image_base + head_opt->p_type_descriptor +
TD_NAME_OFFSET) is outside that module's [base, base+size) range; apply the same
bounds check for the p_self recovery path (COL_SIGNATURE_X64) and add a
regression test that supplies a bogus p_type_descriptor/p_self RVA to ensure
type_name_of() fails rather than reading from another mapped region.
- Around line 122-155: read_name_seh currently copies each successful chunk
straight into out and if a later Memory::seh_read_bytes() fails before a NUL is
found it returns the truncated prefix (contradicting callers' failure contract).
Change the function to accumulate bytes into a temporary buffer (e.g.
std::vector<char> tmp or a fixed-size stack buffer with offset) instead of
memcpy'ing directly into out; only memcpy the accumulated data into out and set
out[written] = '\0' when a NUL is encountered or the full allowed length is
read; if any chunk read fails before a NUL is seen, clear out[0] = '\0' and
return 0. Keep the existing loop logic, PAGE_MASK calculations and variables
(addr, written, max_chars, buf, this_read, copy_len, nul) but defer writing to
out until success is confirmed.

---

Nitpick comments:
In `@src/rtti.cpp`:
- Line 18: The file-wide "using namespace DetourModKit;" in rtti.cpp must be
removed; instead qualify references to DetourModKit types/functions (e.g.,
change bare Memory, Rtti, or any other symbols to DetourModKit::Memory,
DetourModKit::Rtti) or add narrow using declarations like "using
DetourModKit::Memory;" for any frequently used single symbols; update all
occurrences in rtti.cpp accordingly so no global using namespace directive
remains.

In `@tests/test_rtti.cpp`:
- Line 17: Remove the file-wide "using namespace DetourModKit;" from
tests/test_rtti.cpp; instead qualify all DetourModKit symbols with the
DetourModKit:: prefix or add narrow using declarations (e.g., using
DetourModKit::Foo; using DetourModKit::Bar;) for only the specific
types/functions used in this test so no global namespace import remains.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a367701c-866b-4eac-933f-ecd4ecc78c15

📥 Commits

Reviewing files that changed from the base of the PR and between 77d6ab5 and c2a9bbf.

📒 Files selected for processing (10)
  • AGENTS.md
  • README.md
  • docs/misc/rtti-walker.md
  • include/DetourModKit.hpp
  • include/DetourModKit/memory.hpp
  • include/DetourModKit/rtti.hpp
  • src/memory.cpp
  • src/rtti.cpp
  • tests/test_memory.cpp
  • tests/test_rtti.cpp

Comment thread README.md Outdated
Comment thread src/rtti.cpp Outdated
Comment thread src/rtti.cpp Outdated
tkhquang added 2 commits May 26, 2026 10:59
…ss reads

Reviewer findings:

- resolve_name_site now anchors every COL-derived address (col pointer,
  pSelf-recovered image base, computed name buffer) against the vtable's
  owning module range via Memory::module_range_for + Memory::contains. A
  forged COL can no longer redirect the walker into another loaded module
  or unmapped memory.
- read_name_seh accumulates into a stack-local temporary and only memcpys
  into the caller buffer when a NUL is seen or accum_cap is filled. A read
  fault before either condition now reports clean failure (out NUL-empty,
  return 0) instead of a truncated prefix.
- Drop file-wide using namespace from rtti.cpp; wrap definitions in
  namespace DetourModKit { ... } so Memory:: and Rtti:: resolve without
  the directive.
- Drop file-wide using namespace from test_rtti.cpp; replace with narrow
  namespace aliases for Memory and Rtti.
- Move SyntheticVtable storage from heap to a pool inside the test exe's
  data segment so module_range_for resolves the test exe's PE range
  (required by the new bound-check guard).
- README: rewrite the RTTI return-semantics sentence to list the actual
  failure return per API.

New regression tests: ResolveRejectsPoisonedTypeDescriptorRva,
ResolveRejectsPoisonedSelfRva, ResolveRejectsHeapAllocatedVtable.
AGENTS.md requires every closing namespace brace to carry a trailing
comment. Two anonymous namespaces added in this branch (seh_read internals
and module-range internals) were missing their markers; aligns them with
the existing convention (e.g. memory.cpp:826 "anonymous namespace (cache
internals)").
@tkhquang
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tkhquang tkhquang merged commit 7390525 into main May 26, 2026
2 checks passed
@tkhquang tkhquang deleted the feat/rtti-walker-and-module-utils branch May 26, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant