WOW64サービステーブルをWinDbgでみる

WOW64サービステーブルというのは、wow64.dllのなかに実装された関数テーブル。WOW64プロセスがシステムコールを発行したときに、x64システムコールへの変換を行うため透過的に呼び出される。前回までの調査でこいつのシンボル名はwow64!ServiceTablesであることがわかっているので、これを元にWinDbgでテーブルの構造をダンプしてみることにする。


結論としては、SERVICE_DESCRIPTOR_TABLE構造体とほぼ同じ型で、それが4個の配列(sdwhnt32, sdwhwin32, sdwhcon, sdwhbase)になっている。


まず調査するアドレスの構造がよくわからない場合、dcコマンドで16進数とASCII文字列にしてみる(いくつかのWinDbgコマンドは、アドレスを2つ渡すことで、始点と終点を指定できる)。このコマンドはASCIIが出るので、文字列や、視覚的な情報から規則性を見つけやすい。

; なんか規則性があるよね
0:000> dc wow64!ServiceTables wow64!ServiceTables+f0
00000000`7546aa00  754692a0 00000000 00000000 00000000  ..Fu............
00000000`7546aa10  000003e8 00000000 75469fc0 00000000  ..........Fu....
00000000`7546aa20  00000000 00000000 00000000 00000000  ................; ____ここまでで1エントリー?
00000000`7546aa30  7541fae0 00000000 00000000 00000000  ..Au............
00000000`7546aa40  000003e8 00000000 754214b0 00000000  ..........Bu....
00000000`7546aa50  00000000 00000000 7541e110 00000000  ..........Au....; ____ここまでで1エントリー?
00000000`7546aa60  75421b40 00000000 00000000 00000000  @.Bu............
00000000`7546aa70  000003e8 00000000 75421e60 00000000  ........`.Bu....
00000000`7546aa80  00000000 00000000 75421820 00000000  ........ .Bu....; ____ここまでで1エントリー?
00000000`7546aa90  754691e0 00000000 00000000 00000000  ..Fu............
00000000`7546aaa0  000003e8 00000000 75469258 00000000  ........X.Fu....
00000000`7546aab0  00000000 00000000 75469160 00000000  ........`.Fu....; ____ここまでで1エントリー?
00000000`7546aac0  00000045 00000000 00000045 00000000  E.......E.......
00000000`7546aad0  0210003e 00000000 7546aae0 00000000  >.........Fu....
00000000`7546aae0  0044005c 00760065 00630069 005c0065  \.D.e.v.i.c.e.\.; ここからは関係なさそうだ
00000000`7546aaf0  00610048                             H.a.


あるいは、d*sコマンドでシンボル名解決を図るのも有効。今回は、x64環境なので dqs(dump qword symbol_name) か dps(dump pointer_size symbol_name)を使うのが妥当。

; ポインタサイズで解釈してシンボル解決を試してみる。
; なんか出た。
0:000> ds wow64!ServiceTables wow64!ServiceTables+f0
00000000`7546aa00  00000000`754692a0 wow64!sdwhnt32JumpTable
00000000`7546aa08  00000000`00000000
00000000`7546aa10  00000000`000003e8
00000000`7546aa18  00000000`75469fc0 wow64!sdwhnt32Number
00000000`7546aa20  00000000`00000000
00000000`7546aa28  00000000`00000000 
; たぶん、0x30(48)バイト1エントリーだね。区切ってあらわしてるよ。
00000000`7546aa30  00000000`7541fae0 wow64win!sdwhwin32JumpTable
00000000`7546aa38  00000000`00000000
00000000`7546aa40  00000000`000003e8
00000000`7546aa48  00000000`754214b0 wow64win!sdwhwin32Number
00000000`7546aa50  00000000`00000000
00000000`7546aa58  00000000`7541e110 wow64win!sdwhwin32ErrorCase 

00000000`7546aa60  00000000`75421b40 wow64win!sdwhconJumpTable
00000000`7546aa68  00000000`00000000
00000000`7546aa70  00000000`000003e8
00000000`7546aa78  00000000`75421e60 wow64win!sdwhconNumber
00000000`7546aa80  00000000`00000000
00000000`7546aa88  00000000`75421820 wow64win!sdwhconErrorCase

00000000`7546aa90  00000000`754691e0 wow64!sdwhbaseJumpTable
00000000`7546aa98  00000000`00000000
00000000`7546aaa0  00000000`000003e8
00000000`7546aaa8  00000000`75469258 wow64!sdwhbaseNumber
00000000`7546aab0  00000000`00000000
00000000`7546aab8  00000000`75469160 wow64!sdwhbaseErrorCase

00000000`7546aac0  00000000`00000045 ; ここから下は関係なさそうだね
00000000`7546aac8  00000000`00000045
00000000`7546aad0  00000000`0210003e
00000000`7546aad8  00000000`7546aae0 wow64!WindirFullObjectNameBuffer
00000000`7546aae0  00760065`0044005c
00000000`7546aae8  005c0065`00630069
00000000`7546aaf0  00640072`00610048


この辺で、構造がわかってきた。面白そうなのでJumpTableフィールドの中身を調べる。

0:000> dps wow64!sdwhbaseJumpTable wow64!sdwhbaseJumpTable+100
00000000`754691e0  00000000`7545abc0 wow64!whNtWow64CsrBasepSoundSentryNotification
00000000`754691e8  00000000`7545ac18 wow64!whNtWow64CsrBasepRefreshIniFileMapping
00000000`754691f0  00000000`7545ad00 wow64!whNtWow64CsrBasepDefineDosDevice
00000000`754691f8  00000000`7545ae8c wow64!whNtWow64CsrBasepCreateProcess
00000000`75469200  00000000`7545b2c8 wow64!whNtWow64CsrBasepExitProcess
00000000`75469208  00000000`7545b320 wow64!whNtWow64CsrBasepSetProcessShutdownParam
00000000`75469210  00000000`7545b380 wow64!whNtWow64CsrBasepGetProcessShutdownParam
00000000`75469218  00000000`7545b3f4 wow64!whNtWow64CsrBasepSetTermsrvAppInstallMode
00000000`75469220  00000000`7545b44c wow64!whNtWow64CsrBasepSetClientTimeZoneInformation
00000000`75469228  00000000`7545b530 wow64!whNtWow64CsrBasepCreateThread
00000000`75469230  00000000`7545b5b0 wow64!whNtWow64CsrBasepCreateActCtx
00000000`75469238  00000000`7545b93c wow64!whNtWow64CsrBaseCheckRunApp
00000000`75469240  00000000`7545bb7c wow64!whNtWow64CsrBaseQueryModuleData
00000000`75469248  00000000`7545bc78 wow64!whNtWow64CsrBasepNlsUpdateCacheCount
00000000`75469250  00000000`7545bcd0 wow64!whNtWow64CsrBasepNlsGetUserInfo
00000000`75469258  04080804`040c0404 ; <- wow64!sdwhbaseNumber
00000000`75469260  0008001c`34040c0c
00000000`75469268  00000000`754691e0 wow64!sdwhbaseJumpTable
00000000`75469270  00000000`00000000
00000000`75469278  00000000`000003e8
00000000`75469280  00000000`75469258 wow64!sdwhbaseNumber
00000000`75469288  00000000`00000000
00000000`75469290  00000000`75469160 wow64!sdwhbaseErrorCase
; ...

どうみても関数テーブルです。すこし通り過ぎたあたりに、再びサービステーブルの構造が現れている(コメントの部分。0x75469268)。

0:000:x86> dt 73829268
sdwhbase

名前は wow64!sdwhbase という。また、関数テーブルの後には wow64!sdwhbaseNumber の中身がある。「これは1バイトの配列だな(キリッ 」というのは勘で。1バイトとして個数を数えると関数テーブルの個数と一致する。


この構造はほかのテーブルに関しても同様なので定義としてはこんな感じになる(ダンプ結果)。

struct WOW64_SERVICE_TABLE
{
  void*  JumpTable;  // 関数テーブル
  void*  Reserved1;
  ULONG  Size;       // テーブルエントリーの最大数
  UCHAR* Number;     // 関数の引数サイズの配列
  void*  Reserved2;
  void*  ErrorCase;  // (調べたが不明)
} wow64!ServiceTables[4];

// こいつらのフィールドは wow64!ServiceTables と同じものを指している。
WOW64_SERVICE_TABLE wow64!sdwhnt32;
WOW64_SERVICE_TABLE wow64win!sdwhwin32;
WOW64_SERVICE_TABLE wow64win!sdwhcon;
WOW64_SERVICE_TABLE wow64!sdwhbase;

それぞれのテーブルは ntoskrl.exe系、win32k.sys系、コンソール系、csrss.exe系 みたい。


こういう遊びをするのならこの本を買ってみるとよいかもです。

Windowsデバッグの極意 ツールを使いこなして、バグハント!

Windowsデバッグの極意 ツールを使いこなして、バグハント!

ISBN記法とか使うといい感じに書籍紹介できるっぽい!)