【要約】12 バイトで .NET メソッドの行き先を変える [Qiita_Trend] | Summary by TechDistill
> Source: Qiita_Trend
Execute Primary Source
// Problem
開発者は、実行中のメソッドの挙動を動的に変更したい。既存コードを書き換えずに制御を奪う手法が必要だ。しかし、従来の手段では以下の課題に直面する。
- ・呼び出し側のコードを特定し、call命令を差し替える膨大なコスト。
- ・ILレベルの書き換えでは、JIT後のネイティブコードを直接制御できない。
- ・5バイトの相対ジャンプでは、ジャンプ先が2GBの範囲外だと機能しない。
// Approach
著者は、JIT後のネイティブコードの入り口を直接書き換える手法を採用した。実装手順は以下の通りだ。
- ・
RuntimeHelpers.PrepareMethodを使い、対象メソッドを事前にJITコンパイルする。 - ・
ResolveRealEntryを用いて、実際の実行アドレスを特定する。 - ・12バイトの命令
mov rax, imm64; jmp raxで、64ビット絶対アドレスへのジャンプを実現する。 - ・
VirtualProtect等で、書き込み不可のメモリ領域に一時的な権限を付与する。
// Result
実験の結果、呼び出し側のコードを一切変更せずに、メソッドの挙動を完全に制御することに成功した。
- ・
DateTime.Nowの戻り値を西暦9999年に固定するなどの改道を実現した。 - ・12バイトの絶対ジャンプにより、広範なアドレスへの遷移が可能となった。
- ・元の命令を保存する仕組みにより、Hookの解除と元の状態への復元も可能である。
Senior Engineer Insight
> 本手法は極めて強力だが、実戦投入には高いリスクが伴う。現場での評価は以下の通りだ。
- ・運用リスク: 書き換え中の命令実行によるプロセス停止の懸念がある。
- ・デバッグ性: 実行時の制御フローが隠蔽され、事後解析が困難になる。
- ・適用範囲: プロダクションでの常用は避け、高度なデバッグ等に限定すべきだ。