Snoozy

1.Sleep-inducing; tedious.

WOW64でのSystemcallをトレースしてみる

WOW64でのSystemcallをトレースしてみる

この記事はIPFactory Advent Calendar 2020の4日目の記事です.

qiita.com

IPFactoryというサークルについてはこちらをご覧ください.

ipfactory.github.io

前日12/3はn01e0による「userfaultfdについてのメモ」でした.

www.feneshi.co

Heaven's Gateと呼ばれる手法を聞いたことがあるだろうか. ユーザーモードのフックをバイパスする手法として,WOW64上の32bitアプリケーションから64bitOSカーネルへのシステムコール遷移の一部を自前で実装してしまうというものだ.本稿では,Heaven's Gateを実装する準備としてWOW64のシステムコールをトレースし,その内部動作を大まかに掴んでみようと思う.

WOW64

MSDNのドキュメントによると,WOW64は32bitのアプリケーションを64bitのWindows上でシームレスに実行するためのx86エミュレータであるとのことだ.WOW64は現在のWindowsにおいてデフォルトで有効であり,ユーザーはbit数の異なる新旧のアプリケーションの違いを意識することなく実行することができる.

docs.microsoft.com

さて,以下の画像を見てほしい.WOW64の構成は図のようになっている.

f:id:snoozekvn:20201204224531p:plain

引用: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はここからダウンロードすることができる.

docs.microsoft.com

なおWinDbg Previewを使用した場合,64bitコードと32bitコードを切り替えてのデバッグをうまく処理できなかったため,レガシー版のWinDbgを使用する.

また,検証に使ったOSのバージョンは以下.

OS Version:                10.0.19041 N/A Build 19041

64bitアプリケーションの場合

さて,WOW64のシステムコールをトレースする前に,64bitアプリケーションのシステムコールの遷移を改めて確認しておく.トレースするAPIは,特段の理由はないが,NtTerminateProcessを選択した.NtTerminateProcessを呼び出す適当なプログラムを用意し,WinDbgシステムコールをトレースしてみる.

最初にWinDbgでプログラムにアタッチ後,ブレークポイントを設置する.

f:id:snoozekvn:20201204224535p:plain

次に,gコマンドでブレークするまで処理を進ませる.以下の画像のように,eaxにシステムコール番号らしき値が格納され,syscall命令が実行されるようだ.

f:id:snoozekvn:20201204224539p:plain

32bitアプリケーションの場合

次に32bitアプリケーションのシステムコールをトレースしてみる.最初に,プログラム開始直後にロードされているモジュールを表示してみる.

f:id:snoozekvn:20201204224544p:plain

前説通り,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コマンドで確認することができる.

f:id:snoozekvn:20201204224501p:plain

本題のWOW64上でのシステムコールをトレースしよう.最初にbp ntdll_77d0000!NtTerminateProcessで32bit版ntdll.dll内のNtTerminateProcessにブレークポイントを設置する.

f:id:snoozekvn:20201204224535p:plain

gコマンドで処理を進めると,先ほど設置したブレークポイントでブレークする. ここから64bitOSカーネルのNtTerminateProcessまでをトーレスする.

f:id:snoozekvn:20201204224505p:plain

F11キーでステップイン実行すると,Wow64SystemServiceCallはwow64cpu!KiFastSystemCallへジャンプするようだ.

f:id:snoozekvn:20201204224548p:plain

KiFastSystemCallではセグメントセレクタを0x33に変更して,64bitモードへジャンプするようだ.なお,bit modeごとのセグメントレジスタの値は以下.

  • 64-bit (native) = 0x33
  • 32-bit (WOW64) = 0x23
  • 32-bit (native) = 0x1B

セグメントセレクタ自体の解説は以下を参照のこと.

www.malwaretech.com

f:id:snoozekvn:20201204224552p:plain

ここで初めて以下の画像のように64bitコードが登場する.

f:id:snoozekvn:20201204224556p:plain

最初に現れる処理はwow64cpu!CPUPReturnFromSimulatedCodeだ.この部分は32bitのコンテキストの保存と64bitのコンテキストへの移行を担う処理のようだ.

f:id:snoozekvn:20201204224521p:plain

その後,いくつかの関数を経て以下の処理へ遷移する. 具体的には次の関数を経由していた

  • wow64cpu!TurboDispatchJumpAddressStart
  • wow64cpu!Thunk3ArgSpNSpSp
  • wow64cpu!Thunk0Arg

NtTerminateProcessをコールするsyscall命令はwow64cpu!CpupSyscallStubで呼び出されていた.

f:id:snoozekvn:20201204224527p:plain

syscall実行後,32bitコンテキストを復元し,32bitから64bitへ移行したのと同じ要領で,64bitから32bitモードへ戻る. セグメントセレクタを0x23に変えてfar jumpしている点に注意.

f:id:snoozekvn:20201204224513p:plain

WOW64上の32bitアプリケーションから64bitのOSカーネルへのシステムコール遷移を追ってみた.次回の記事ではWOW64フックコードの作成,並びにHeaven's Gateの実装に挑戦したい.

明日12/5はfutabatoによる「医用画像処理のためのMRI入門」です.ご期待ください.

以上.

参考

www.fireeye.com

wbenny.github.io

www.apriorit.com

rce.co