Try FText::ToString by symbol first, and record why it does not save us - #89
Merged
Conversation
A symbol is deterministic: it is either there and correct, or absent. That is strictly better than a byte signature, so it now runs first, and the pattern scan stays as the fallback. ResolveFTextToStringBySymbol looks for ?ToString@FText@@QEBAAEBVFString@@xz across every loaded module via Toolhelp. It does not fix Father's Day, and it cannot: symbol resolution needs the engine in DLLs, and a monolithic Shipping build has no UE symbols at all. Measured rather than assumed — the exe carries 312 exports and every one is a driver hint (NvOptimusEnablement, ags*), the PDB is named in the header but not shipped, and the only DLL beside it is turbojpeg. At runtime the resolver interrogates 142 modules and hits nothing. That module count is logged on purpose. Without it, a zero cannot separate "looked everywhere, not there" from "the snapshot failed and I looked at nothing" — the same trap as the "connesso via IPC" line, which reads like success and is half the chain. The check that confirms it is comparing against (Get-Process ...).Modules.Count: 142 against 142. So the value here is for non-monolithic and editor builds, plus a failure that now says what it did. Commercial games still fall through to the pattern. The four ambiguous candidates are measured and written down: four distinct functions sharing a prologue, diverging at byte 13 — inside the 17 the signature covers. Lengthening the signature would mean retuning it against the four games where it is already unique, with no ground truth to say which candidate is even the right one. The entry names the promising direction instead: anchoring on a string reference rather than a prologue. Verified on the real game: no regression, fallback chain intact, game responsive, both architectures rebuilt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A symbol is deterministic: it is either there and correct, or absent. That is strictly better than a byte signature, so it now runs first, with the pattern scan as fallback.
It does not fix Father's Day, and it cannot. Symbol resolution needs the engine in DLLs, and a monolithic Shipping build — the commercial norm — has no UE symbols at all. That is measured, not assumed:
NvOptimusEnablement,ags*)turbojpeg.dllonlySo the value here is for non-monolithic and editor builds, plus a failure that now says what it did. Commercial games still fall through to the pattern.
The module count is not decoration
Without it, a zero cannot separate "looked everywhere, not there" from "the snapshot failed and I looked at nothing" — two diagnoses leading to two different investigations. It is the same trap as the
connesso via IPCline recorded in #88: reads like success, is half the chain.The check that confirms the enumeration actually works: compare against
(Get-Process ...).Modules.Count. 142 against 142.Why the ambiguity is not fixable by a longer signature
The four candidates are measured and written into the log — four distinct functions sharing a prologue, diverging at byte 13, inside the 17 the signature covers:
0x1CADD20call [rax+0x28], returnsrax+0x680x1D04720call [rax+0x48], returns[rbx+0x60](int32)0x1D047B0[rbx+0x64]— twin accessors0x3074940mov rcx,[rax+0x90], calls throughr8Lengthening the signature means retuning it against the four games where it is already unique — with no ground truth to say which candidate is even the right one, because there are no symbols here to compare against. Guessing is exactly what this source refuses to do, and refusing remains correct: a wrong hook in a shipping game is a crash or silent heap corruption.
The entry names the promising direction instead: anchoring on a string reference — find a known datum in
.rdataand walk back to the code that uses it — which does not depend on how the compiler laid out a prologue.Verification
Re-ran on Father's Day with real injection: symbol path exercised (142 modules, no hit), pattern still correctly refuses, GDI fallback activates, game responsive, no regression. Both architectures rebuilt.
🤖 Generated with Claude Code