From 397d46f1745a2ab0043e5a4da5cb933a74d048a8 Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 4 Aug 2026 13:48:14 -0700 Subject: [PATCH] fifa17-recon: the -4 rule is literally the ASCII prefix "RS4:" The "4-byte header" that precedes every response class name in .rdata, which cost six failed class-to-deserializer resolutions before anyone noticed the offset, is not a length prefix or a refcount. It is the string RS4:. The full literal is RS4:FutXServerResponse, and searching for the bare class name lands four bytes in. Verified directly on three classes: FutDestroyMatchServerResponse name@0x18021d694 header = b'RS4:' FutGetDraftCurrentStateServerResponse name@0x180224204 header = b'RS4:' FutStickerBookStats2ServerResponse name@0x1802220cc header = b'RS4:' Found by a verification agent that had been instructed to distrust the rule. It did, and came back with the reason rather than the offset. A magic constant you have to remember is a rule you will eventually get wrong; a prefix you can read is not. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW --- fifa17-recon/tools/ghidra_env.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/fifa17-recon/tools/ghidra_env.py b/fifa17-recon/tools/ghidra_env.py index f7946b6..8ea99bb 100644 --- a/fifa17-recon/tools/ghidra_env.py +++ b/fifa17-recon/tools/ghidra_env.py @@ -165,12 +165,21 @@ def vtable(a, n=64): def class_deser(cls): """FutXServerResponse class name -> [(deserializer, vtable, factory), ...]. - THE -4 RULE. A response class's name literal is preceded by a 4-BYTE HEADER, - and the factory's `lea r8,[rip+...]` points at THAT header, not at the text. - So the reference to look up is `name_addr - 4`. Six attempts at class->deser - resolution failed before this was noticed -- four of them returned zero - candidates and were nearly written up as "the class has no deserializer". - Ghidra does create the reference, so no manual instruction decoding is needed. + THE -4 RULE, AND WHAT IT ACTUALLY IS. A response class's name literal is + preceded by a 4-byte header, and the factory's `lea r8,[rip+...]` points at + THAT header, not at the text, so the reference to look up is `name_addr - 4`. + Six attempts at class->deser resolution failed before this was noticed; four of + them returned zero candidates and were nearly written up as "the class has no + deserializer". Ghidra does create the reference, so no manual instruction + decoding is needed. + + The "4-byte header" is not a length prefix or a refcount. It is literally the + ASCII string `RS4:`. The full literal is `RS4:FutXServerResponse`, and searching + for the bare class name lands four bytes into it. Knowing that, the rule stops + being a magic constant to remember and becomes obvious, and it also means you + can search for `RS4:` + the class name directly and skip the arithmetic. + (Established 2026-08-04 by a verification agent that had been told to distrust + the rule; it did, and found the reason instead of the offset.) From the factory, the object's vtable is the .rdata address it references whose first two qwords are functions; the deserializer is vtable slot +0x08.