WOW64でのSystemcallをトレースしてみる
WOW64でのSystemcallをトレースしてみる
この記事はIPFactory Advent Calendar 2020の4日目の記事です.
IPFactoryというサークルについてはこちらをご覧ください.
前日12/3はn01e0による「userfaultfdについてのメモ」でした.
Heaven's Gateと呼ばれる手法を聞いたことがあるだろうか. ユーザーモードのフックをバイパスする手法として,WOW64上の32bitアプリケーションから64bitOSカーネルへのシステムコール遷移の一部を自前で実装してしまうというものだ.本稿では,Heaven's Gateを実装する準備としてWOW64のシステムコールをトレースし,その内部動作を大まかに掴んでみようと思う.
WOW64
MSDNのドキュメントによると,WOW64は32bitのアプリケーションを64bitのWindows上でシームレスに実行するためのx86エミュレータであるとのことだ.WOW64は現在のWindowsにおいてデフォルトで有効であり,ユーザーはbit数の異なる新旧のアプリケーションの違いを意識することなく実行することができる.
さて,以下の画像を見てほしい.WOW64の構成は図のようになっている.
引用:https://www.atmarkit.co.jp/ait/articles/1007/01/news131.html
WOW64のAPIコールの仕組みは同@ITの解説がわかりやすい.以下に引用する.
64bit Windows向けに作られたアプリケーションを実行する場合は、それぞれのアプリケーションごとに独立した1つの64bitプロセス空間が作成され、その中で実行される。 これに対してWin32アプリケーションを実行しようとすると、32bit版のWindows OSをエミュレーションするための環境(32bitプロセス空間)が作成され、その中でWin32アプリケーションが実行される。Win32アプリケーションが発行するAPIはエミュレーション用に用意された特別なDLLを経由して64bitのOSカーネルへ渡される。 引用: @IT 「第2回 Win32アプリケーションを実行するWOW64 (1/2)」
WOW64を構成する主要な64bitDLLは以下.
- wow64.dll
- wow64cpu.dll
- wow64win.dll
- ntdll.dll
WOW64上ではbit数の異なるDLLがロードされることになる.それぞれの格納元は次のとおり.
- 32-bits %windir%\SysWOW64\
- 64-bits %windir%\System32\
システムコールのトレースに使用するWinDbgはここからダウンロードすることができる.
なおWinDbg Previewを使用した場合,64bitコードと32bitコードを切り替えてのデバッグをうまく処理できなかったため,レガシー版のWinDbgを使用する.
また,検証に使ったOSのバージョンは以下.
OS Version: 10.0.19041 N/A Build 19041
64bitアプリケーションの場合
さて,WOW64のシステムコールをトレースする前に,64bitアプリケーションのシステムコールの遷移を改めて確認しておく.トレースするAPIは,特段の理由はないが,NtTerminateProcessを選択した.NtTerminateProcessを呼び出す適当なプログラムを用意し,WinDbgでシステムコールをトレースしてみる.
最初にWinDbgでプログラムにアタッチ後,ブレークポイントを設置する.
次に,gコマンドでブレークするまで処理を進ませる.以下の画像のように,eaxにシステムコール番号らしき値が格納され,syscall命令が実行されるようだ.
32bitアプリケーションの場合
次に32bitアプリケーションのシステムコールをトレースしてみる.最初に,プログラム開始直後にロードされているモジュールを表示してみる.
前説通り,wow64cpu,wow64,wow64win等のwow付きのモジュールや,ntdllが2つロードされていることが確認できる.また見切れてしまっているがntdll_77d0000のpdbファイル名はwntdll.pdb,ntdllはntdll.pdbであることもわかる.このファイルはIDAやGhidra等で逆アセンブルする際に有用だ.
さらにWOW64を使って32bitアプリケーションを動かした場合,プロセス内には32bitと64bit用に2つのPEBとTEBが作成される.これは!wow64exts.info
コマンドで確認することができる.
本題のWOW64上でのシステムコールをトレースしよう.最初にbp ntdll_77d0000!NtTerminateProcess
で32bit版ntdll.dll内のNtTerminateProcessにブレークポイントを設置する.
gコマンドで処理を進めると,先ほど設置したブレークポイントでブレークする. ここから64bitOSカーネルのNtTerminateProcessまでをトーレスする.
F11キーでステップイン実行すると,Wow64SystemServiceCallはwow64cpu!KiFastSystemCallへジャンプするようだ.
KiFastSystemCallではセグメントセレクタを0x33に変更して,64bitモードへジャンプするようだ.なお,bit modeごとのセグメントレジスタの値は以下.
- 64-bit (native) = 0x33
- 32-bit (WOW64) = 0x23
- 32-bit (native) = 0x1B
セグメントセレクタ自体の解説は以下を参照のこと.
ここで初めて以下の画像のように64bitコードが登場する.
最初に現れる処理はwow64cpu!CPUPReturnFromSimulatedCodeだ.この部分は32bitのコンテキストの保存と64bitのコンテキストへの移行を担う処理のようだ.
その後,いくつかの関数を経て以下の処理へ遷移する. 具体的には次の関数を経由していた
- wow64cpu!TurboDispatchJumpAddressStart
- wow64cpu!Thunk3ArgSpNSpSp
- wow64cpu!Thunk0Arg
NtTerminateProcessをコールするsyscall命令はwow64cpu!CpupSyscallStubで呼び出されていた.
syscall実行後,32bitコンテキストを復元し,32bitから64bitへ移行したのと同じ要領で,64bitから32bitモードへ戻る. セグメントセレクタを0x23に変えてfar jumpしている点に注意.
WOW64上の32bitアプリケーションから64bitのOSカーネルへのシステムコール遷移を追ってみた.次回の記事ではWOW64フックコードの作成,並びにHeaven's Gateの実装に挑戦したい.
明日12/5はfutabatoによる「医用画像処理のためのMRI入門」です.ご期待ください.
以上.