PTTRDLL: DX11 Overlay + IL2CPP Hooks on a Real Game
PTTRDLL is an overlay DLL I wrote for a Unity IL2CPP game called PTTR. No grand mission — I wanted to wire together two things I already half-knew: graphics-layer hooking, and IL2CPP reverse work. I had a real game as a practice field, so I injected, stacked an ImGui menu on the frame, and read/wrote player state — health, buffs, upgrades, position.
One easy misread: GitHub labels the repo primary language as C# because Data/ holds a huge IL2CPP dump.cs that skews the stats. What actually compiles is C++.
Cut in from the frame
To draw on the game’s picture, the clean path is to own the present path. I used MinHook on IDXGISwapChain::Present (vtable index 8) and ResizeBuffers (13), plus D3D11CreateDeviceAndSwapChain.
The sticky part: at inject time the game’s swap chain often does not exist yet, so you cannot grab its vtable. I spin up a temporary dummy swap chain, resolve Present/Resize addresses from that fake object’s vtable, then hook the real ones. Once that clicks, the rest of the DX hook is straightforward.
The overlay is Dear ImGui’s Win32 + DX11 backend with a dark ClickGUI. On resize you must rebuild the RTV or the frame dies. Toggle is INSERT via a hooked WndProc. ImGui and MinHook are vendored and built with the project — no external package manager, less mental load.
Then into game logic
Frame done, next is player state against IL2CPP. I hook fixed RVAs in GameAssembly.dll on PTTRPlayer methods: Update, Awake, OnEnable, OnDestroy, Die, set_health. Awake / OnEnable capture the local player instance; Update does standing R/W; Die / set_health pin states I do not want to flip.
Position needs an extra hop: il2cpp_resolve_icall for get_transform and get_position_Injected, then XYZ onto the menu.
God mode is basically locked health, plus sliders for health / boost / upgrade values with live readback. config.h holds compile-time defaults (god on, health 10000, boost 9999, upgrade 99); runtime sliders override them.
Pitfalls
Wild pointers are the expensive lesson. Read/write a dead game object and the host process is gone. Every game-memory touch sits in __try/__except — log the bad pointer, do not take the game with you. Append-only file logging covers inject init and render stages; without logs this class of work is driving blind.
Another baked-in fragility: those RVA_* values are from one game build’s dump. A game update nukes them; you re-dump. That is why this stays a personal toy, not a general tool.
Status
Personal WIP, MIT. Kept mainly as my own reference for DX11 hook and IL2CPP patterns when I forget them later. Incomplete, but the feel of cutting from pixels down into game logic is why I still like opening it.