Unsafe Relocation Fixups in Asset Loaders
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Why asset relocations matter
Many legacy game engines (Granny 3D, Gamebryo, etc.) load complex assets by:
- Parsing a header and section table.
- Allocating one heap buffer per section.
- Building a
SectionArraythat stores the base pointer of every section. - Applying relocation tables so that pointers embedded inside the section data get patched to the right target section + offset.
When the relocation handler blindly trusts attacker-controlled metadata, every relocation becomes a potential arbitrary read/write primitive. In Anno 1404: Venice, granny2.dll ships the following helper:
`GrannyGRNFixUp_0` (trimmed)
int *__cdecl GrannyGRNFixUp_0(DWORD RelocationCount,
Relocation *PointerFixupArray,
int *SectionArray,
char *destination)
{
while (RelocationCount--) {
int target_base = SectionArray[PointerFixupArray->SectionNumber]; // unchecked index
int *patch_site = (int *)(destination + PointerFixupArray->SectionOffset); // unchecked offset
*patch_site = target_base ;
if (target_base)
*patch_site = target_base + PointerFixupArray->Offset;
++PointerFixupArray;
}
return SectionArray;
}
SectionNumber is never range-checked and SectionOffset is never validated against the current section size. Crafting relocation entries with negative offsets or oversized indices lets you walk outside the section you control and stomp allocator metadata such as the section pointer array itself.
Stage 1 – Writing backwards into loader metadata
The goal is to make the relocation table of section 0 overwrite entries of SectionContentArray (which mirrors SectionArray and is stored right before the first section buffer). Because Granny’s custom allocator prepends 0x1F bytes and the NT heap adds its own 0x10-byte header plus alignment, an attacker can precalculate the distance between the start of the first section (destination) and the section-pointer array.
In the tested build, forcing the loader to allocate a GrannyFile structure that is exactly 0x4000 bytes makes the section-pointer arrays land right before the first section buffer. Solving
0x20 (header) + 0x20 (section descriptors)
+ n * 1 (section types) + n * 1 (flags)
+ n * 4 (pointer table) = 0x4000
gives n = 2720 sections. A relocation entry with SectionOffset = -0x3FF0 ( 0x4000 - 0x20 - 0x20 + 0x30 ) now resolves to SectionContentArray[1] even though the destination section thinks it is patching internal pointers.
Stage 2 – Deterministic heap layout on Windows 10
Windows 10 NT Heap routes allocations ≤ RtlpLargestLfhBlock (0x4000) to the randomized LFH and larger ones to the deterministic backend allocator. By keeping the GrannyFile metadata slightly above that threshold (using the 2720 sections trick) and preloading several malicious .gr2 assets, you can make:
- Allocation #1 (metadata + section pointer arrays) land in a >0x4000 backend chunk.
- Allocation #2 (section 0 contents) land immediately after allocation #1.
- Allocation #3 (section 1 contents) land right after allocation #2, giving you a predictable target for subsequent relocations.
Process Monitor confirmed that assets are streamed on demand, so repeatedly requesting crafted units/buildings is enough to “prime” the heap layout without touching the executable image.
Stage 3 – Converting the primitive into RCE
- Corrupt
SectionContentArray[1]. Section 0’s relocation table overwrites it by using the-0x3FF0offset. Point it at any writable region you control (e.g., later section data). - Recycle the corrupted pointer. Section 1’s relocation table now treats
SectionNumber = 1as whatever pointer you injected. The handler writesSectionArray[1] + Offsettodestination + SectionOffset, giving you an arbitrary 4-byte write for every relocation entry. - Hit reliable dispatchers. In Anno 1404 the target of choice was the
granny2.dllallocator callbacks (no ASLR, DEP disabled). Overwriting the function pointer thatgranny2.dlluses for the nextMalloc/Freecall immediately diverts execution to attacker-controlled code loaded from the trojanized asset.
Because both granny2.dll and the injected .gr2 buffers reside at stable addresses when ASLR/DEP are disabled, the attack reduces to building a small ROP chain or raw shellcode and pointing the callback at it.
Practical checklist
- Look for asset loaders that maintain
SectionArray/ relocation tables. - Diff relocation handlers for missing bounds on indices/offsets.
- Measure the allocator headers added by both the game’s allocator wrapper and the underlying OS heap to compute backwards offsets precisely.
- Force deterministic placement by:
- inflating metadata (many empty sections) until allocation size >
RtlpLargestLfhBlock; - repeatedly loading the malicious asset to fill backend holes.
- inflating metadata (many empty sections) until allocation size >
- Use a two-stage relocation table (first to retarget
SectionArray, second to spray writes) and overwrite function pointers that will fire during normal rendering (allocator callbacks, virtual tables, animation dispatchers, etc.).
Once you gain an arbitrary file write (e.g., via the path traversal in the multiplayer save transfer), repackaging RDA archives with the crafted .gr2 gives you a clean delivery vector that is automatically decompressed by remote clients.
References
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks

