Skip to content

feat(rtti): add reverse RTTI dissection and self-healing offset resolver - #95

Merged
tkhquang merged 2 commits into
mainfrom
feat/rtti-self-heal
Jun 8, 2026
Merged

feat(rtti): add reverse RTTI dissection and self-healing offset resolver#95
tkhquang merged 2 commits into
mainfrom
feat/rtti-self-heal

Conversation

@tkhquang

@tkhquang tkhquang commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds rtti_dissect.hpp: reverse RTTI dissection (identify_pointee_type, reverse_scan_block) and a self-healing offset resolver (heal_landmark/heal_offset, solve_fingerprint) built on the walker's shared, SEH-guarded COL prelude.
  • Every entry point is noexcept and fails closed; ships with a full gtest suite and the docs/misc/rtti-self-heal.md guide.

Summary by CodeRabbit

  • New Features

    • Added RTTI self-healing module enabling reverse pointer slot identification and recovery of drifted struct field offsets across binary patches.
  • Documentation

    • Added RTTI Self-Heal guide with detailed feature description.
    • Updated module matrix and guides list.
  • Tests

    • Added comprehensive test suite for RTTI dissection with synthetic fixture setup.

Builds rtti_dissect.hpp on the shared SEH-guarded COL prelude: identify_pointee_type, reverse_scan_block, heal_landmark/heal_offset, and solve_fingerprint. Every entry point is noexcept and fails closed. Adds the gtest suite and the self-heal guide.
@tkhquang tkhquang self-assigned this Jun 8, 2026
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tkhquang, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 32 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f5e54a7a-c52c-4c66-a7ff-3ac894e1833c

📥 Commits

Reviewing files that changed from the base of the PR and between 20f9a54 and dbbdd0f.

📒 Files selected for processing (4)
  • docs/misc/rtti-self-heal.md
  • include/DetourModKit/rtti.hpp
  • src/rtti.cpp
  • tests/test_rtti_dissect.cpp
📝 Walkthrough

Walkthrough

This PR introduces a new RTTI dissection and self-healing module. A shared RTTI ABI infrastructure is extracted into an internal header, existing RTTI code is refactored to use shared helpers, and a comprehensive four-layer API is declared and implemented to identify types, scan blocks for labeled slots, heal field offsets via landmark templates, and solve uniform drift via fingerprinting. Tests and documentation complete the addition.

Changes

RTTI Dissection and Offset Healing

Layer / File(s) Summary
Internal RTTI ABI infrastructure and helpers
src/rtti_internal.hpp
New internal header with MSVC x64 RTTI constants, ColHead and ColSite POD structs, and resolve_col_site / read_name_seh functions extracted for shared use by both existing and new RTTI code paths.
Refactoring existing RTTI code to use shared helpers
src/rtti.cpp
rtti.cpp updated to call Rtti::detail::resolve_col_site and Rtti::detail::read_name_seh instead of local implementations; find_in_pointer_table now uses detail::MIN_VALID_PTR for pointer filtering.
Public RTTI dissection API declarations
include/DetourModKit/rtti_dissect.hpp
New public header declaring data structures (PointeeType, LabeledSlot, Landmark, HealHit, FingerprintHit), enums (Indirection, HealError), and API functions for reverse RTTI identification, block scanning, landmark healing, and fingerprint-based drift solving, all documented as fail-closed and exception-safe.
RTTI dissection and healing implementation
src/rtti_dissect.cpp
Implementation of identify_pointee_type for type resolution, reverse_scan_block for labeling slots, heal_landmark with bounded ring-scan and equidistant ambiguity detection, heal_offset wrapper, heal_error_to_string mapping, and solve_fingerprint with uniform drift validation and optional landmark scoring.
Comprehensive test suite with synthetic fixtures
tests/test_rtti_dissect.cpp
Global allocator overrides for allocation-free contract validation, synthetic RTTI fixture for in-process layout construction, and extensive tests covering type identification, block scanning with stride/byte-length variants, healing across drift/ambiguity/boundaries, and fingerprint solving with required/optional landmarks.
Module integration and user-facing documentation
include/DetourModKit.hpp, AGENTS.md, README.md, docs/misc/rtti-self-heal.md
Add new header to umbrella include, document module in project guides, and introduce comprehensive user documentation describing the four-layer flow, landmark templates, error semantics, validation rules, and composability with existing RTTI walker.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tkhquang/DetourModKit#85: Both PRs modify the existing MSVC RTTI walker implementation in src/rtti.cpp (especially read_name_seh, COL-site resolution, and pointer-table validity checks), and this PR's new rtti_dissect APIs build directly on that shared RTTI-walking foundation via rtti_internal.hpp.
🚥 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 clearly and concisely summarizes the main change: adding reverse RTTI dissection and self-healing offset resolution functionality.
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
tests/test_rtti_dissect.cpp (1)

3-18: ⚡ Quick win

Add the standard headers this file uses directly.

This TU uses std::min, std::is_trivially_copyable_v, and std::span, but only gets them transitively today. That makes the test brittle against header cleanup in gtest or DetourModKit/rtti_dissect.hpp.

Suggested fix
+#include <algorithm>
 `#include` <array>
 `#include` <atomic>
 `#include` <cstddef>
 `#include` <cstdint>
 `#include` <cstdlib>
 `#include` <cstring>
 `#include` <new>
+#include <span>
 `#include` <string>
 `#include` <string_view>
+#include <type_traits>
 `#include` <vector>
🤖 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_dissect.cpp` around lines 3 - 18, Add direct includes for the
standard headers used by this translation unit: include <algorithm> for
std::min, <type_traits> for std::is_trivially_copyable_v, and <span> for
std::span at the top of tests/test_rtti_dissect.cpp so the TU does not rely on
transitive includes; update the include block near the existing headers (before
using any symbols) to ensure functions and type traits used in this file are
declared.
🤖 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 `@src/rtti_dissect.cpp`:
- Around line 99-102: The check in descriptor_ok(const Rtti::Landmark &lm)
incorrectly rejects names whose length equals Rtti::MAX_TYPE_NAME_LEN; change
the condition that currently uses ">= Rtti::MAX_TYPE_NAME_LEN" to only reject
lengths greater than MAX_TYPE_NAME_LEN (i.e., use ">" or allow equality) so
Landmark::expected_mangled of length MAX_TYPE_NAME_LEN is accepted; update
descriptor_ok accordingly to match the bounds supported by read_name_seh() and
PointeeType::name_buf.

In `@src/rtti.cpp`:
- Around line 70-86: The code currently only enforces the p_self checks when
head_opt->signature == COL_SIGNATURE_X64 and allows other signatures to fall
through; change the logic in the block handling head_opt->signature so that any
signature other than COL_SIGNATURE_X64 is treated as invalid (return false)
instead of continuing to use the loader/base path — specifically, in the
function that inspects head_opt (referencing head_opt, COL_SIGNATURE_X64,
p_self, p_type_descriptor, mod_range, Memory::contains and TD_NAME_OFFSET), add
an explicit reject branch for non-x64 signatures (return false) before computing
td_addr/name_addr so malformed or unknown signatures cannot be accepted and feed
into type_name_* or the reverse dissector.

In `@tests/test_rtti_dissect.cpp`:
- Around line 237-244: The test helper unmapped_addr currently returns 0 if
VirtualAlloc fails, causing Identify_RejectsUnreadableSlotAddress to exercise
the null-address guard instead of the unreadable-memory path; update
unmapped_addr to abort the test on allocation failure (e.g., use a test fatal
failure/assert like EXPECT_NE/ASSERT_NE or GTEST_FAIL) so it never returns 0 on
failure, and apply the same change to the other identical helper at the 366-370
location; reference the unmapped_addr function and the
Identify_RejectsUnreadableSlotAddress test when making the change.

---

Nitpick comments:
In `@tests/test_rtti_dissect.cpp`:
- Around line 3-18: Add direct includes for the standard headers used by this
translation unit: include <algorithm> for std::min, <type_traits> for
std::is_trivially_copyable_v, and <span> for std::span at the top of
tests/test_rtti_dissect.cpp so the TU does not rely on transitive includes;
update the include block near the existing headers (before using any symbols) to
ensure functions and type traits used in this file are declared.
🪄 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: a73025ea-fa40-4632-84dc-116163006d44

📥 Commits

Reviewing files that changed from the base of the PR and between a83994c and 20f9a54.

📒 Files selected for processing (9)
  • AGENTS.md
  • README.md
  • docs/misc/rtti-self-heal.md
  • include/DetourModKit.hpp
  • include/DetourModKit/rtti_dissect.hpp
  • src/rtti.cpp
  • src/rtti_dissect.cpp
  • src/rtti_internal.hpp
  • tests/test_rtti_dissect.cpp

Comment thread src/rtti_dissect.cpp
Comment thread src/rtti.cpp Outdated
Comment thread tests/test_rtti_dissect.cpp
Harden the shared prelude so an unknown or corrupt signature cannot skip the pSelf cross-check and fall through on the loader base. Add the direct includes the dissect test relies on, guard the unmapped-address precondition, and unwrap the self-heal guide prose.
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