| 595 | } |
| 596 | |
| 597 | void* VtableHook::FindGlobalWithVtable(void* vtablePtr, |
| 598 | uintptr_t steamBase, size_t steamSize) |
| 599 | { |
| 600 | uintptr_t target = reinterpret_cast<uintptr_t>(vtablePtr); |
| 601 | |
| 602 | // Default instances live in writable .data/.bss. Scan writable ranges. |
| 603 | for (int r = 0; r < g_writableCount; r++) |
| 604 | { |
| 605 | if (g_writableRanges[r].end <= steamBase || |
| 606 | g_writableRanges[r].start >= steamBase + steamSize) |
| 607 | continue; |
| 608 | |
| 609 | const uintptr_t* scanStart = reinterpret_cast<const uintptr_t*>( |
| 610 | (g_writableRanges[r].start + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); |
| 611 | const uintptr_t* scanEnd = reinterpret_cast<const uintptr_t*>( |
| 612 | g_writableRanges[r].end & ~(sizeof(uintptr_t) - 1)); |
| 613 | |
| 614 | for (const uintptr_t* p = scanStart; p + 2 < scanEnd; p++) |
| 615 | { |
| 616 | // First word == vtable, next two words == 0 (arena / has_bits). |
| 617 | if (*p == target && *(p + 1) == 0 && *(p + 2) == 0) |
| 618 | { |
| 619 | void* inst = reinterpret_cast<void*>(const_cast<uintptr_t*>(p)); |
| 620 | Log::Info("default instance for vtable %p at %p (offset 0x%zx)", |
| 621 | vtablePtr, inst, reinterpret_cast<uintptr_t>(inst) - steamBase); |
| 622 | return inst; |
| 623 | } |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | Log::Error("default instance for vtable %p not found", vtablePtr); |
| 628 | return nullptr; |
| 629 | } |
| 630 | |
| 631 | bool VtableHook::InstallCloudEnabledHook(void** vtable, CloudEnabledHookInfo& info) |
| 632 | { |
nothing calls this directly
no outgoing calls
no test coverage detected