PTTRDLL: 実ゲーム上の DX11 オーバーレイ + IL2CPP フック
PTTR という Unity IL2CPP ゲーム向けに書いたオーバーレイ DLL だ。大義はない。それぞれ少し知っている二つを一本につなぎたかっただけだ。グラフィックス層の Hook と、IL2CPP のリバース。手元に本物のゲームがあったので注入し、画面に ImGui メニューを重ね、プレイヤー状態(体力・バフ・アップグレード・座標)を読み書きした。
見間違えやすい点:GitHub の主言語が C# になっているのは、Data/ に巨大な IL2CPP の dump.cs があり統計が偏るからで、実際にビルドできる本体は C++ だ。
画面側から切る
ゲーム画面に描くいちばんきれいな道は、Present 経路を握ることだ。MinHook で IDXGISwapChain::Present(vtable 8)と ResizeBuffers(13)、さらに D3D11CreateDeviceAndSwapChain を掛ける。
詰まるのは、注入時点ではゲームの swap chain がまだ無く、vtable が取れないことだ。一時的な dummy swap chain を自前で作り、その偽オブジェクトの vtable から関数アドレスを解き、本物にフックする。ここが通れば DX Hook の残りは流れ作業だ。
オーバーレイは Dear ImGui の Win32 + DX11 バックエンド、暗色の ClickGUI。リサイズ時は RTV を作り直さないと画面が死ぬ。表示切替は INSERT、WndProc フック経由。ImGui と MinHook は vendored で一緒にビルドし、外部パッケージマネージャは使わない。
ゲーム論理へ
画面ができたら次は IL2CPP と正面から向き合う。GameAssembly.dll 内 PTTRPlayer の固定 RVA に Update / Awake / OnEnable / OnDestroy / Die / set_health をフックする。Awake / OnEnable でローカルプレイヤーを掴み、Update で常駐 R/W、Die と set_health で起こしたくない状態変化を止める。
座標は一段遠回りする。il2cpp_resolve_icall で get_transform と get_position_Injected を解き、XYZ をメニューに出す。
いわゆる無敵は核がロック体力で、血量 / ブースト / アップグレード値をスライダーで触り、現在値をリアルタイム表示する。config.h にコンパイル時デフォルト(god ON、血 10000、boost 9999、upgrade 99)、実行時はオーバーレイ側で上書きできる。
踏んだ穴
いちばん高い授業料は野ポインタだ。死んだゲームオブジェクトに読み書きするとホストプロセスが即死する。ゲームメモリへのアクセスはすべて __try/__except で包み、壊れたポインタはログして終わり、ゲームを道連れにしない。追記専用のファイルログで注入初期化と描画各段を残す。ログ無しのこの手の作業は目隠し運転だ。
もう一つの先天的な脆さ:RVA_* は特定ビルドの dump から抜いた値で、ゲームが更新されれば全部死ぬ。だからこれは汎用ツールではなく、個人の玩具のままになる。
いま
個人プロジェクト、ずっと WIP、MIT。あとで忘れたときの DX11 Hook と IL2CPP の手順書として残している。完成度は高くないが、「画面からゲーム論理まで一段ずつ潜る」感触は今でも好きだ。