- determinism oracle regenerated and byte-identical (bb4fd9ac / 978041ac) - the four phase-23/33 draw-bearing tail callees run EVERY turn; the three inner functions holding the draws run zero times -- the gate is inside each outer body - CreateRaidEncounter is called (2 on one turn) and draws nothing: candidate list empty - Zuul: 7 calls / 7 words per trade-raid Chance site, 14 not 16, as predicted - EncounterDetect_Run receives an EMPTY record vector, so ProcessTeamRecord and AssignContacts never run; the 2-word detection residual is in 0x007d5150's subtree - a MinHook detour on 0x00893290 changes the game's output; bisected over six runs. The un-instrumented game and lane Z's instrument agree, so lane Z's numbers stand - lane AI1 insert: P2 held across two fresh processes, Rung B stays as written
28 lines
2.6 KiB
JSON
28 lines
2.6 KiB
JSON
{
|
|
"entries": [
|
|
{
|
|
"name": "SpyManager_Slot13RngCallee",
|
|
"addr": "0x008408e0",
|
|
"convention": "thiscall",
|
|
"prototype": "int (void* this, int, int) // the ONE callee through which Game::ServerSpyManager vftable 0x00a3073c slot 13 (0x008877b0) reaches the strategic generator. Exactly ONE incoming reference in the image: an unconditional call at 0x00887af2 inside that slot. Real body 0x008408e0..0x00840a59, 370 bytes. Contains three of lane V2's eight draw sites -- Chance 0x00840929, NextInt 0x008409c7, Chance 0x00840a3c -- all loading the generator as [reg+0x16c]. Named for the verified relationship (which slot reaches it, and that it draws), NOT for any semantics: its body was not read. Lane H hooks it with a register-transparent entry counter so that 'the spy path never fired' can be split into 'slot 13 was not entered' and 'slot 13 was entered and gated above this call'",
|
|
"status": "mapped",
|
|
"source": "findings/control-flow/tail-probes.md"
|
|
},
|
|
{
|
|
"name": "TradeManager_Slot13RngCalleeA",
|
|
"addr": "0x00820ca0",
|
|
"convention": "thiscall",
|
|
"prototype": "uint (void* fleet, char) // first of the two callees through which Game::ServerTradeManagerImpl vftable 0x00a31b74 slot 13 (0x0088ef80) reaches the strategic generator. Exactly ONE incoming reference: an unconditional call at 0x0088f036. Real body 0x00820ca0..0x00820e53, 436 bytes, containing lane V2's NextFloat site 0x00820e18. Its own body loops the vector at arg+0xa4/+0xa8 testing per-element flags, and its boolean result is what gates the call to TradeManager_Slot13RngCalleeB one instruction later. THE CALLER'S LOOP IS OVER THE FLEETS VECTOR at GetServer()+0x64/+0x68, not over a trade-route list -- which is why 'the tail draws nothing because we have no trade routes' does not explain this slot",
|
|
"status": "mapped",
|
|
"source": "findings/control-flow/tail-probes.md"
|
|
},
|
|
{
|
|
"name": "TradeManager_Slot13RngCalleeB",
|
|
"addr": "0x0088b440",
|
|
"convention": "thiscall",
|
|
"prototype": "void (void* this, int) // second of the two callees through which Game::ServerTradeManagerImpl slot 13 (0x0088ef80) reaches the strategic generator, called at 0x0088f042 ONLY when TradeManager_Slot13RngCalleeA returned non-zero. Exactly ONE incoming reference. Real body 0x0088b440..0x0088b976, 1323 bytes, containing lane V2's NextInt site 0x0088b613 (`mov ecx,[ecx+0x16c]; add ecx,4`). The two form a two-stage gate: A draws first and decides whether B runs, so a run where A fires and B does not is a MEASUREMENT, not a gap",
|
|
"status": "mapped",
|
|
"source": "findings/control-flow/tail-probes.md"
|
|
}
|
|
]
|
|
}
|