ホーム > プログラミングメモ > カーネルモードへの移行
 

カーネルモードへの移行

アプリケーション・プログラムがシステムコールをすると、(処理内容にもよりますが)OS内部では CPUをカーネルモードに切り替えて処理を行います。 デバッガを使って、この流れを見てみましょう。

ネイティブAPI

本題に入る前に、ネイティブAPIの説明をします。 Win32アプリケーションプログラムはUSER32, KERNEL32, GDI32のDLLで公開されているAPIをコールすることで システムを呼び出します。これらのDLLがシステム内部をコールしてくれるわけです。 ただしKERNEL32は直接Windowsのカーネルを呼び出すことをしません。 なぜなら、WindowsNT/2000/XPはWin32専用に作られているわけではないからです。 Win32以外のサブシステム、POSIXサブシステム、OS/2サブシステム、INTERIXサブシステムなどの元ではWin32以外のアプリケーションプログラムも動作させることが可能です。 (WindowsXPではPOSIXサブシステム、OS/2サブシステムが廃止されましたが。) KERNEL32は(Win32専用ではない汎用的な)APIをコールし、このAPI内部でカーネルを呼び出しています。 この汎用的なAPIをネイティブAPIと呼びます。このネイティブAPIを実装・公開しているのはNTDLLというDLLです。 なおUSER32, GDI32は提供する機能がWin32に特化したものであるため、直接カーネル(NTOSKRNL.EXE, WIN32K.SYSなど)を呼び出します。

NTDLL.DLLの提供するネイティブAPIについて知らなくてもプログラミングする際に困ることはありませんが、 概要がわかるとWindowsの内部動作をより深く理解でき、 プログラミングするのが楽しくなります。

プログラム

ネイティブAPI/カーネルモード移行箇所の調査に使うプログラムのソースコードです。(nativeapi.c menu.h menu.rc) Visual Studioを用いてビルドします。 ビルド時の注意事項です。(1)DEBUGビルドすること。(2)コンソールアプリではなく、GUIアプリとしてビルド。(リンカの設定は/SUBSYSTEM:WINDOWS)(3)リンカでデバッグ情報の生成(/DEBUG, /PDB)を有効にしておくこと。 (4)シンボル「_DEBUG」を定義しておくこと。

【nativeapi.c】
// ネイティブAPI/カーネルモード移行箇所の調査をするためのプログラム(Win32/Win64共用) // (コンソールアプリではなく)GUIアプリとしてビルドすること。 // (RELEASEビルドではなく)DEBUGビルドし、デバッガ内から実行すること。 // 単独で実行するとデバッガが起動するか「不正な処理をしました」になる。 #include <windows.h> #include <stdio.h> #include <string.h> #include <tchar.h> #include "menu.h" LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM); TCHAR title[]=TEXT("ネイティブAPI/カーネルモード移行箇所の調査"); TCHAR szWinName[]=TEXT("NativeAPI"); int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode) { HWND hwnd; MSG msg; WNDCLASSEX wcl; wcl.cbSize=sizeof(WNDCLASSEX); wcl.hInstance=hThisInst; wcl.lpszClassName=szWinName; wcl.lpfnWndProc=WindowFunc; wcl.style=0; wcl.hIcon=LoadIcon(NULL, IDI_APPLICATION); wcl.hIconSm=NULL; wcl.hCursor=LoadCursor(NULL, IDC_ARROW); wcl.lpszMenuName=TEXT("MyMenu"); wcl.cbClsExtra=0; wcl.cbWndExtra=0; wcl.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH); if(!RegisterClassEx(&wcl)) return 0; hwnd=CreateWindow( szWinName, title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInst, NULL ); ShowWindow(hwnd, nWinMode); UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // END of while() return (int)msg.wParam; } // END of WinMain() LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { TCHAR messtr[256]; LPBYTE baseadr=0x0; MEMORY_BASIC_INFORMATION mbi; HDC hdc; switch(message){ case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_KERNEL32: _tcscpy(messtr, TEXT("Click OK to break before VirtualQuery() function.(KERNEL32)")); MessageBox(hwnd, messtr, title, MB_ICONEXCLAMATION|MB_OK); #ifdef _DEBUG DebugBreak(); #endif VirtualQuery(baseadr, &mbi, sizeof(mbi)); _tcscpy(messtr, TEXT("VirtualQuery() was executed.(KERNEL32)")); MessageBox(hwnd, messtr, title, MB_ICONINFORMATION|MB_OK); break; case IDM_GDI32: _tcscpy(messtr, TEXT("Click OK to break before Ellipse() function.(GDI32)")); MessageBox(hwnd, messtr, title, MB_ICONEXCLAMATION|MB_OK); hdc=GetDC(hwnd); SelectObject(hdc, (HBRUSH)GetStockObject(LTGRAY_BRUSH)); SelectObject(hdc, (HPEN)GetStockObject(BLACK_PEN)); #ifdef _DEBUG DebugBreak(); #endif Ellipse(hdc, 10, 10, 60, 60); InvalidateRect(hwnd, NULL, 1); ReleaseDC(hwnd, hdc); _tcscpy(messtr, TEXT("Ellipse() was executed.(GDI32)")); MessageBox(hwnd, messtr, title, MB_ICONINFORMATION|MB_OK); break; case IDM_USER32: _tcscpy(messtr, TEXT("Click OK to break before GetDC() function.(USER32)")); MessageBox(hwnd, messtr, title, MB_ICONEXCLAMATION|MB_OK); #ifdef _DEBUG DebugBreak(); #endif hdc=GetDC(hwnd); SelectObject(hdc, (HBRUSH)GetStockObject(LTGRAY_BRUSH)); SelectObject(hdc, (HPEN)GetStockObject(BLACK_PEN)); Ellipse(hdc, 10, 10, 60, 60); InvalidateRect(hwnd, NULL, 1); ReleaseDC(hwnd, hdc); _tcscpy(messtr, TEXT("GetDC() was executed.(USER32)")); MessageBox(hwnd, messtr, title, MB_ICONINFORMATION|MB_OK); break; case IDM_EXIT: PostQuitMessage(0); break; } // END of switch() break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } // END of switch() return 0; } // END of WindowFunc()

【menu.h】
#define IDM_KERNEL32 101 #define IDM_GDI32 102 #define IDM_USER32 103 #define IDM_EXIT 104

【menu.rc】
#include "menu.h" MyMenu MENU { POPUP "INT3(&I)" { MENUITEM "KERNEL32 function(&K)", IDM_KERNEL32 MENUITEM "GDI32 function(&G)", IDM_GDI32 MENUITEM "USER32 function(&U)", IDM_USER32 MENUITEM SEPARATOR MENUITEM "Exit(&X)", IDM_EXIT } }

ビルドしたら、そのEXEファイルをデバッガ(windbg)内から実行します。Visual Studioのデバッガではなくwindbgを使うのは、 32-bitアプリだけでなく64-bitアプリについても調べるためです。(本稿執筆時のVisual Studio 2005 betaは64ビットプロセスのデバッグができない。(=64ビットプロセスにアタッチできない。)) プルダウンメニュー「INT3」を開くと、「KERNEL32 function」「GDI32 function」「USER32 function」が並んでおり、 例えば「KERNEL32 function」を選択すると、メッセージボックスが現れ「Click OK to break before VirtualQuery() function.(KERNEL32)」と表示されます。 OKボタンを押すとデバッガに制御が移るので、そこからステップ実行することで、VirtualQuery()がどのような手順で実行されるのかを眺めることができるわけです。 「GDI32 function」「USER32 function」についても同様です。

Win32(ネイティブ)の場合

まずWin32(ネイティブ)について見てみましょう。以下、WindowsXP Professional SP2+Pentium4(Northwood)の環境で実行します。 windbgの「File -> Open Executable...」でEXEファイルを開くとCommandウィンドウが開くので、「0:000>」プロンプトの所に「g」と入力してENTERキーを押します。 するとnativeapi.exeのウィンドウが表示されるので、プルダウンメニューから「KERNEL32 function」「GDI32 function」「USER32 function」のいずれかを選択します。 メッセージボックスが表示されたら、OKボタンを押します。 デバッガに制御が移り「0:000>」プロンプトが表示されたのを確認したら、ステップ実行していきます。(F11キーもしくはF8キーを押すか、「0:000>」プロンプトの所に「t」と入力してENTERキーを押す。) 最初はKERNEL32のfunctionです。

【KERNEL32 in Win32(x86 native)】
VirtualQuery()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.3.0017.0 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi\Debug\nativeapi.exe Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00400000 0047d000 nativeapi.exe ModLoad: 7c940000 7c9dd000 ntdll.dll ModLoad: 7c800000 7c931000 C:\WINDOWS\system32\kernel32.dll ModLoad: 77cf0000 77d7f000 C:\WINDOWS\system32\USER32.dll ModLoad: 77ed0000 77f16000 C:\WINDOWS\system32\GDI32.dll (658.e64): Break instruction exception - code 80000003 (first chance) eax=00251eb4 ebx=7ffda000 ecx=00000007 edx=00000080 esi=00251f48 edi=00251eb4 eip=7c941230 esp=0012fb20 ebp=0012fc94 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 ntdll!DbgBreakPoint: 7c941230 cc int 3 0:000> g ModLoad: 762e0000 762fd000 C:\WINDOWS\system32\IMM32.DLL ... 途中省略 ModLoad: 770d0000 7715c000 C:\WINDOWS\system32\oleaut32.dll (658.e64): Break instruction exception - code 80000003 (first chance) eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c941230 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint: 7c941230 cc int 3 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c941231 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint+0x1: 7c941231 c3 ret 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424480 esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 *** WARNING: Unable to verify checksum for nativeapi.exe nativeapi!WindowFunc+0xd0: 00424480 3bf4 cmp esi,esp 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00422bae esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!ILT+2985(__RTC_CheckEsp): 00422bae e9dd1d0000 jmp nativeapi!_RTC_CheckEsp (00424990) 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424990 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!_RTC_CheckEsp: 00424990 7501 jnz nativeapi!_RTC_CheckEsp+0x3 (00424993) [br=0] 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424992 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!_RTC_CheckEsp+0x2: 00424992 c3 ret 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424487 esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!WindowFunc+0xd7: 00424487 8bf4 mov esi,esp 0:000> t eax=0012fba0 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c80b859 esp=0012fab0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 kernel32!VirtualQuery: 7c80b859 8bff 0:000> t eax=0012fba0 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c80b85b esp=0012fab0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 kernel32!VirtualQuery+0x2: 7c80b85b 55 ... 途中省略 0:000> t eax=0012fba0 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c80b869 esp=0012fa9c ebp=0012faac iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 kernel32!VirtualQuery+0x10: 7c80b869 e8baffffff 0:000> t eax=0012fba0 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c80b828 esp=0012fa98 ebp=0012faac iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 kernel32!VirtualQueryEx: 7c80b828 8bff ... 途中省略 0:000> t eax=0012faa8 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c80b83f esp=0012fa7c ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 kernel32!VirtualQueryEx+0x17: 7c80b83f ff15bc12807c 0:000> t eax=0012faa8 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c94e213 esp=0012fa78 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!ZwQueryVirtualMemory: 7c94e213 b8b2000000 0:000> t eax=000000b2 ebx=00000000 ecx=00000000 edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c94e218 esp=0012fa78 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!ZwQueryVirtualMemory+0x5: 7c94e218 ba0003fe7f 0:000> t eax=000000b2 ebx=00000000 ecx=00000000 edx=7ffe0300 esi=0012fac0 edi=0012fcd8 eip=7c94e21d esp=0012fa78 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!ZwQueryVirtualMemory+0xa: 7c94e21d ff12 0:000> t eax=000000b2 ebx=00000000 ecx=00000000 edx=7ffe0300 esi=0012fac0 edi=0012fcd8 eip=7c94eb8b esp=0012fa74 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCall: 7c94eb8b 8bd4 0:000> t eax=000000b2 ebx=00000000 ecx=00000000 edx=0012fa74 esi=0012fac0 edi=0012fcd8 eip=7c94eb8d esp=0012fa74 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCall+0x2: 7c94eb8d 0f34 sysenter 0:000> t eax=00000000 ebx=00000000 ecx=00000001 edx=ffffffff esi=0012fac0 edi=0012fcd8 eip=7c94e21f esp=0012fa78 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!ZwQueryVirtualMemory+0xc: 7c94e21f c21800 0:000> t eax=00000000 ebx=00000000 ecx=00000001 edx=ffffffff esi=0012fac0 edi=0012fcd8 eip=7c80b845 esp=0012fa94 ebp=0012fa94 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 kernel32!VirtualQueryEx+0x1d: 7c80b845 85c0

KERNEL32のVirtualQuery()を呼ぶとVirtualQueryEx()が呼び出され、さらにNTDLLのZwQueryVirtualMemory()が呼び出されてから、NTDLLのKiFastSystemCall()内の sysenter命令を用いてカーネルモードに移行しているのがわかります。次はGDI32のfunctionです。

【GDI32 in Win32(x86 native)】
Ellipse()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.3.0017.0 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi\Debug\nativeapi.exe Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00400000 0047d000 nativeapi.exe ModLoad: 7c940000 7c9dd000 ntdll.dll ModLoad: 7c800000 7c931000 C:\WINDOWS\system32\kernel32.dll ModLoad: 77cf0000 77d7f000 C:\WINDOWS\system32\USER32.dll ModLoad: 77ed0000 77f16000 C:\WINDOWS\system32\GDI32.dll (738.36c): Break instruction exception - code 80000003 (first chance) eax=00251eb4 ebx=7ffda000 ecx=00000007 edx=00000080 esi=00251f48 edi=00251eb4 eip=7c941230 esp=0012fb20 ebp=0012fc94 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 ntdll!DbgBreakPoint: 7c941230 cc int 3 0:000> g ModLoad: 762e0000 762fd000 C:\WINDOWS\system32\IMM32.DLL ... 途中省略 ModLoad: 770d0000 7715c000 C:\WINDOWS\system32\oleaut32.dll (738.36c): Break instruction exception - code 80000003 (first chance) eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=7c941230 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint: 7c941230 cc int 3 0:000> t eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=7c941231 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint+0x1: 7c941231 c3 ret 0:000> t eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=00424586 esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 *** WARNING: Unable to verify checksum for nativeapi.exe nativeapi!WindowFunc+0x1d6: 00424586 3bf4 cmp esi,esp 0:000> t eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=00422bae esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!ILT+2985(__RTC_CheckEsp): 00422bae e9dd1d0000 jmp nativeapi!_RTC_CheckEsp (00424990) 0:000> t eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=00424990 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!_RTC_CheckEsp: 00424990 7501 jnz nativeapi!_RTC_CheckEsp+0x3 (00424993) [br=0] 0:000> t eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=00424992 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!_RTC_CheckEsp+0x2: 00424992 c3 ret 0:000> t eax=01b00017 ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=0042458d esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!WindowFunc+0x1dd: 0042458d 8bf4 mov esi,esp 0:000> t eax=7601068d ebx=00000000 ecx=00550000 edx=7601068d esi=0012fac0 edi=0012fcd8 eip=77edc83b esp=0012faa8 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!Ellipse: 77edc83b 8bff ... 途中省略 0:000> t eax=7ffdf000 ebx=7601068d ecx=00550000 edx=7601068d esi=00010000 edi=0012fcd8 eip=77edc8b6 esp=0012fa84 ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!Ellipse+0xa4: 77edc8b6 e80c000000 0:000> t eax=7ffdf000 ebx=7601068d ecx=00550000 edx=7601068d esi=00010000 edi=0012fcd8 eip=77edc8c7 esp=0012fa80 ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!NtGdiEllipse: 77edc8c7 b880100000 0:000> t eax=00001080 ebx=7601068d ecx=00550000 edx=7601068d esi=00010000 edi=0012fcd8 eip=77edc8cc esp=0012fa80 ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!NtGdiEllipse+0x5: 77edc8cc ba0003fe7f 0:000> t eax=00001080 ebx=7601068d ecx=00550000 edx=7ffe0300 esi=00010000 edi=0012fcd8 eip=77edc8d1 esp=0012fa80 ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!NtGdiEllipse+0xa: 77edc8d1 ff12 0:000> t eax=00001080 ebx=7601068d ecx=00550000 edx=7ffe0300 esi=00010000 edi=0012fcd8 eip=7c94eb8b esp=0012fa7c ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCall: 7c94eb8b 8bd4 0:000> t eax=00001080 ebx=7601068d ecx=00550000 edx=0012fa7c esi=00010000 edi=0012fcd8 eip=7c94eb8d esp=0012fa7c ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCall+0x2: 7c94eb8d 0f34 sysenter 0:000> t eax=00000001 ebx=7601068d ecx=00000001 edx=ffffffff esi=00010000 edi=0012fcd8 eip=77edc8d3 esp=0012fa80 ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!NtGdiEllipse+0xc: 77edc8d3 c21400 0:000> t eax=00000001 ebx=7601068d ecx=00000001 edx=ffffffff esi=00010000 edi=0012fcd8 eip=77edc8bb esp=0012fa98 ebp=0012faa4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 GDI32!Ellipse+0xa9: 77edc8bb 5f

GDI32のEllipse()を呼ぶとGDI32内のNtGdiEllipse()が呼び出され、NTDLLのKiFastSystemCall()内の sysenter命令を用いてカーネルモードに移行しているのがわかります。 (NTDLLのその他のルーチンがコールされていないことに注意。) 最後にUSER32のfunctionです。

【USER32 in Win32(x86 native)】
GetDC()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.3.0017.0 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi\Debug\nativeapi.exe Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00400000 0047d000 nativeapi.exe ModLoad: 7c940000 7c9dd000 ntdll.dll ModLoad: 7c800000 7c931000 C:\WINDOWS\system32\kernel32.dll ModLoad: 77cf0000 77d7f000 C:\WINDOWS\system32\USER32.dll ModLoad: 77ed0000 77f16000 C:\WINDOWS\system32\GDI32.dll (9d0.970): Break instruction exception - code 80000003 (first chance) eax=00251eb4 ebx=7ffdd000 ecx=00000007 edx=00000080 esi=00251f48 edi=00251eb4 eip=7c941230 esp=0012fb20 ebp=0012fc94 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 ntdll!DbgBreakPoint: 7c941230 cc int 3 0:000> g ModLoad: 762e0000 762fd000 C:\WINDOWS\system32\IMM32.DLL ... 途中省略 ModLoad: 770d0000 7715c000 C:\WINDOWS\system32\oleaut32.dll (9d0.970): Break instruction exception - code 80000003 (first chance) eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c941230 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint: 7c941230 cc int 3 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=7c941231 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint+0x1: 7c941231 c3 ret 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424653 esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 *** WARNING: Unable to verify checksum for nativeapi.exe nativeapi!WindowFunc+0x2a3: 00424653 3bf4 cmp esi,esp 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00422bae esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!ILT+2985(__RTC_CheckEsp): 00422bae e9dd1d0000 jmp nativeapi!_RTC_CheckEsp (00424990) 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424990 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!_RTC_CheckEsp: 00424990 7501 jnz nativeapi!_RTC_CheckEsp+0x3 (00424993) [br=0] 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=00424992 esp=0012fabc ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!_RTC_CheckEsp+0x2: 00424992 c3 ret 0:000> t eax=00000001 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=0042465a esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!WindowFunc+0x2aa: 0042465a 8bf4 mov esi,esp 0:000> t eax=0075053e ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=77cf8697 esp=0012fab8 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 USER32!NtUserGetDC: 77cf8697 b891110000 0:000> t eax=00001191 ebx=00000000 ecx=7c95056d edx=00150608 esi=0012fac0 edi=0012fcd8 eip=77cf869c esp=0012fab8 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 USER32!NtUserGetDC+0x5: 77cf869c ba0003fe7f 0:000> t eax=00001191 ebx=00000000 ecx=7c95056d edx=7ffe0300 esi=0012fac0 edi=0012fcd8 eip=77cf86a1 esp=0012fab8 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 USER32!NtUserGetDC+0xa: 77cf86a1 ff12 0:000> t eax=00001191 ebx=00000000 ecx=7c95056d edx=7ffe0300 esi=0012fac0 edi=0012fcd8 eip=7c94eb8b esp=0012fab4 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCall: 7c94eb8b 8bd4 0:000> t eax=00001191 ebx=00000000 ecx=7c95056d edx=0012fab4 esi=0012fac0 edi=0012fcd8 eip=7c94eb8d esp=0012fab4 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCall+0x2: 7c94eb8d 0f34 sysenter 0:000> t eax=b0010cba ebx=00000000 ecx=00000001 edx=ffffffff esi=0012fac0 edi=0012fcd8 eip=77cf86a3 esp=0012fab8 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 USER32!NtUserGetDC+0xc: 77cf86a3 c20400 0:000> t eax=b0010cba ebx=00000000 ecx=00000001 edx=ffffffff esi=0012fac0 edi=0012fcd8 eip=00424666 esp=0012fac0 ebp=0012fcd8 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 nativeapi!WindowFunc+0x2b6: 00424666 3bf4

USER32のNtUserGetDC()を呼び出すと()、そのままNTDLLのKiFastSystemCall()内の sysenter命令を用いてカーネルモードに移行しているのがわかります。 (NTDLLのその他のルーチンがコールされていないことに注意。)

上記例ではsysenter命令を用いてカーネルモードへ移行していますが、sysenterはPentiumIIで導入された命令です。 それ以前のCPU(Pentium, Pentium PROなど)で動作するWindowsNT 4.0/2000などではソフトウェア割り込み(int 2E)を使ってカーネルモードに移行します。 WindowsXPのNTDLL.DLLにもKiIntSystemCall()という名前でint 2Eを用いたカーネルモード移行ルーチンが残っています。 (Die Windows HistoryにWindowsXPをPentiumで動作させている例が載っているのを見ると、 おそらくこうした場合に使われているものと推測される。)

Win32(WOW64)の場合

Win32(WOW64)について調べます。 本題に入る前にWOW64について見ておきます。 WOW64とは64-bit版Windows上でWin32アプリを実行するためのエミュレーション層を指します。 主として3つのDLL(WOW64.DLL WOW64WIN.DLL WOW64CPU.DLL)から構成されます。 WOW64.DLLとWOW64WIN.DLLがWIN32アプリのシステムコールをintercept(横取り)して 64-bitのシステムコールに置き換えて実行します。 WOW64.DLLが横取りした分についてはNTDLL.DLL(64-bit native)経由でNTOSKRNL.EXE(64-bit native)がコールされ、 WOW64WIN.DLLが横取りした分については直接WIN32K.SYS(64-bit native)がコールされます。 WOW64CPU.DLLはCPUのモード切替を担当します。 (MSDNの資料には「WOW64CPU.DLLはx64では不要」と記述されているが、 x64プロセッサの32-bitから64-bitへの切り替えはWOW64CPU.DLLで行われており、必須であるものと思われる。)

以下、WindowsXP Professional x64 Edition(RC1)+Athlon64(NewCastle)の環境で実行します。 windbgはVersion 6.4.4.4(x64版-beta)を使います。 最初はKERNEL32のfunctionです。

【KERNEL32 in Win32(WOW64)】
VirtualQuery()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.4.0004.4 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi\Debug\nativeapi.exe Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00000000`00400000 00000000`0047d000 nativeapi.exe ModLoad: 00000000`77ea0000 00000000`77ff3000 ntdll.dll ModLoad: 00000000`77b70000 00000000`77bb6000 C:\WINDOWS\system32\wow64.dll ModLoad: 00000000`77b20000 00000000`77b69000 C:\WINDOWS\system32\wow64win.dll ModLoad: 00000000`77b10000 00000000`77b19000 C:\WINDOWS\system32\wow64cpu.dll (70.73c): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> g ModLoad: 00000000`77ce0000 00000000`77e92000 NOT_AN_IMAGE ModLoad: 00000000`7d4d0000 00000000`7d5d0000 NOT_AN_IMAGE ModLoad: 00000000`7d5e0000 00000000`7d6f0000 C:\WINDOWS\syswow64\ntdll32.dll ModLoad: 00000000`77ce0000 00000000`77e92000 NOT_AN_IMAGE ModLoad: 00000000`77bc0000 00000000`77ccf000 NOT_AN_IMAGE ModLoad: 00000000`7d4d0000 00000000`7d5d0000 C:\WINDOWS\syswow64\kernel32.dll ModLoad: 00000000`7d940000 00000000`7da10000 C:\WINDOWS\syswow64\USER32.dll ModLoad: 00000000`7d810000 00000000`7d8a0000 C:\WINDOWS\syswow64\GDI32.dll (70.73c): WOW64 breakpoint - code 4000001f (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ntdll32!DbgBreakPoint: 00000000`7d5f002d cc int 3 0:000:x86> g ModLoad: 00000000`7dee0000 00000000`7df40000 C:\WINDOWS\syswow64\IMM32.DLL ... 途中省略 ModLoad: 00000000`75810000 00000000`758d0000 C:\WINDOWS\syswow64\USERENV.dll (70.73c): WOW64 breakpoint - code 4000001f (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ntdll32!DbgBreakPoint: 00000000`7d5f002d cc int 3 0:000:x86> t ntdll32!DbgBreakPoint+0x1: 00000000`7d5f002e c3 ret 0:000:x86> t *** WARNING: Unable to verify checksum for nativeapi.exe *** ERROR: Module load completed but symbols could not be loaded for nativeapi.exe nativeapi+0x24480: 00000000`00424480 3bf4 cmp esi,esp 0:000:x86> t nativeapi+0x24482: 00000000`00424482 e827e7ffff call nativeapi+0x22bae (00422bae) 0:000:x86> t nativeapi+0x22bae: 00000000`00422bae e9dd1d0000 jmp nativeapi+0x24990 (00424990) 0:000:x86> t nativeapi+0x24990: 00000000`00424990 7501 jnz nativeapi+0x24993 (00424993) [br=0] 0:000:x86> t nativeapi+0x24992: 00000000`00424992 c3 ret 0:000:x86> t nativeapi+0x24487: 00000000`00424487 8bf4 mov esi,esp 0:000:x86> t nativeapi+0x24489: 00000000`00424489 6a1c push 0x1c 0:000:x86> t nativeapi+0x2448b: 00000000`0042448b 8d85c8feffff lea eax,[ebp-0x138] ss:002b:00000000`002cfb78=cccccccc 0:000:x86> t nativeapi+0x24491: 00000000`00424491 50 push eax 0:000:x86> t nativeapi+0x24492: 00000000`00424492 8b8decfeffff mov ecx,[ebp-0x114] ss:002b:00000000`002cfb9c=00000000 0:000:x86> t nativeapi+0x24498: 00000000`00424498 51 push ecx 0:000:x86> t nativeapi+0x24499: 00000000`00424499 ff1558b34700 call dword ptr [nativeapi+0x7b358 (0047b358)] {kernel32!VirtualQuery (7d4eac15)} ds:002b:00000000`0047b358=7d4eac15 0:000:x86> t kernel32!VirtualQuery: 00000000`7d4eac15 8bff 0:000:x86> t kernel32!VirtualQuery+0x2: 00000000`7d4eac17 55 0:000:x86> t kernel32!VirtualQuery+0x3: 00000000`7d4eac18 8bec ... 途中省略 0:000:x86> t kernel32!VirtualQuery+0x10: 00000000`7d4eac25 e8baffffff 0:000:x86> t kernel32!VirtualQueryEx: 00000000`7d4eabe4 8bff 0:000:x86> t kernel32!VirtualQueryEx+0x2: 00000000`7d4eabe6 55 0:000:x86> t kernel32!VirtualQueryEx+0x3: 00000000`7d4eabe7 8bec ... 途中省略 0:000:x86> t kernel32!VirtualQueryEx+0x17: 00000000`7d4eabfb ff15e0024e7d 0:000:x86> t ntdll32!NtQueryVirtualMemory: 00000000`7d5fcac6 b820000000 ... 途中省略 0:000:x86> t ntdll32!ZwQueryVirtualMemory+0xb: 00000000`7d5fcad1 64ff15c0000000 0:000:x86> t wow64cpu!X86SwitchTo64BitMode: 00000000`77b11870 ea2c38b1773300 0:000:x86> t wow64cpu!CpupReturnFromSimulatedCode: 00000000`77b1382c 67448b0424 ... 途中省略 0:000> t wow64cpu!CpupReturnFromSimulatedCode+0x22: 00000000`77b1384e 448bda 0:000> t wow64cpu!TurboDispatchJumpAddressStart: 00000000`77b13851 41ff24cf 0:000> t wow64cpu!ServiceNoTurbo: 00000000`77b13855 4189b5a4000000 ... 途中省略 0:000> t wow64cpu!ServiceNoTurbo+0x22: 00000000`77b13877 ff15f3d7ffff 0:000> t wow64!Wow64SystemServiceEx: 00000000`77b767b0 4c8bdc ... 途中省略 0:000> t wow64!Wow64SystemServiceEx+0xd3: 00000000`77b76883 41ffd4 0:000> t wow64!whNtQueryVirtualMemory: 00000000`77b820c0 488bc4 ... 途中省略 0:000> t wow64!whNtQueryVirtualMemory+0x3f: 00000000`77b820ff e8bc86ffff 0:000> t wow64!Wow64ShallowThunkSIZE_T32TO64: 00000000`77b7a7c0 4885d2 ... 途中省略 0:000> t wow64!Wow64ShallowThunkSIZE_T32TO64+0x15: 00000000`77b7a7d5 c20000 0:000> t wow64!whNtQueryVirtualMemory+0x44: 00000000`77b82104 85ff ... 途中省略 0:000> t wow64!whNtQueryVirtualMemory+0xfb: 00000000`77b821bb e870fdffff 0:000> t wow64!whNtQueryVirtualMemory_MemoryBasicInformation: 00000000`77b81f30 488bc4 ... 途中省略 0:000> t wow64!whNtQueryVirtualMemory_MemoryBasicInformation+0xbc: 00000000`77b81fec ff1546f2feff 0:000> t ntdll!ZwQueryVirtualMemory: 00000000`77ef2f90 4c8bd1 0:000> t ntdll!NtQueryVirtualMemory+0x3: 00000000`77ef2f93 b820000000 0:000> t ntdll!NtQueryVirtualMemory+0x8: 00000000`77ef2f98 0f05 syscall 0:000> t ntdll!NtQueryVirtualMemory+0xa: 00000000`77ef2f9a c3 0:000> t wow64!whNtQueryVirtualMemory_MemoryBasicInformation+0xc2: 00000000`77b81ff2 4c8ba42498000000

まず冒頭のModLoadの行にご注目ください。 仮想アドレス空間にロードされたDLLが列挙されています。 64ビットネイティブなDLLとして、NTDLL.DLL WOW64.DLL WOW64WIN.DLL WOW64CPU.DLLがロードされています。 さらにWIN32アプリがコールしてくるスタブルーチンとしてのKERNEL32.DLL USER32.DLL GDI32.DLLもロードされており、 KERNEL32.DLLがネイティブAPIコールのために使うNTDLL32.DLLもロードされています。

KERNEL32のVirtualQuery()をコールすると、VirtualQueryEx()がコールされ、NTDLL32のNtQueryVirtualMemory()、 ZwQueryVirtualMemory()がコールされます。 32-bit版Windowsであればここでカーネルモードへ移行するところでしょうが、そうはならずに セグメント間CALL命令(call dword ptr fs:[000000c0])でWOW64に制御を移します。 最初にWOW64CPU.DLLのX86SwitchTo64BitModeで32-bit mode(Compatibility mode)から64-bit modeに移行します。 その後はWOW64CPU内のいくつかのルーチン、WOW64のwhNtQueryVirtualMemory(), whNtQueryVirtualMemory_MemoryBasicInformation()などを実行した後に (64ビットネイティブな)NTDLLのNtQueryVirtualMemory()、ZwQueryVirtualMemory()がコールされ(エントリーポイントはどちらも同じ)、ここでようやくsyscall命令を用いてカーネルモードへ移行します。 (KiFastSystemCall()をコールするのではなく、直接syscall命令を実行している点に注意。)

GDI32のfunctionについても見てみます。

【GDI32 in Win32(WOW64)】
Ellipse()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.4.0004.4 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi\Debug\nativeapi.exe Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00000000`00400000 00000000`0047d000 nativeapi.exe ModLoad: 00000000`77ea0000 00000000`77ff3000 ntdll.dll ModLoad: 00000000`77b70000 00000000`77bb6000 C:\WINDOWS\system32\wow64.dll ModLoad: 00000000`77b20000 00000000`77b69000 C:\WINDOWS\system32\wow64win.dll ModLoad: 00000000`77b10000 00000000`77b19000 C:\WINDOWS\system32\wow64cpu.dll (51c.1d8): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> g ModLoad: 00000000`77ce0000 00000000`77e92000 NOT_AN_IMAGE ModLoad: 00000000`7d4d0000 00000000`7d5d0000 NOT_AN_IMAGE ModLoad: 00000000`7d5e0000 00000000`7d6f0000 C:\WINDOWS\syswow64\ntdll32.dll ModLoad: 00000000`77ce0000 00000000`77e92000 NOT_AN_IMAGE ModLoad: 00000000`77bc0000 00000000`77ccf000 NOT_AN_IMAGE ModLoad: 00000000`7d4d0000 00000000`7d5d0000 C:\WINDOWS\syswow64\kernel32.dll ModLoad: 00000000`7d940000 00000000`7da10000 C:\WINDOWS\syswow64\USER32.dll ModLoad: 00000000`7d810000 00000000`7d8a0000 C:\WINDOWS\syswow64\GDI32.dll (51c.1d8): WOW64 breakpoint - code 4000001f (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ntdll32!DbgBreakPoint: 00000000`7d5f002d cc int 3 0:000:x86> g ModLoad: 00000000`7dee0000 00000000`7df40000 C:\WINDOWS\syswow64\IMM32.DLL ... 途中省略 ModLoad: 00000000`75810000 00000000`758d0000 C:\WINDOWS\syswow64\USERENV.dll (51c.1d8): WOW64 breakpoint - code 4000001f (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ntdll32!DbgBreakPoint: 00000000`7d5f002d cc int 3 0:000:x86> t ntdll32!DbgBreakPoint+0x1: 00000000`7d5f002e c3 ret 0:000:x86> t *** WARNING: Unable to verify checksum for nativeapi.exe *** ERROR: Module load completed but symbols could not be loaded for nativeapi.exe nativeapi+0x24586: 00000000`00424586 3bf4 cmp esi,esp 0:000:x86> t nativeapi+0x24588: 00000000`00424588 e821e6ffff call nativeapi+0x22bae (00422bae) 0:000:x86> t nativeapi+0x22bae: 00000000`00422bae e9dd1d0000 jmp nativeapi+0x24990 (00424990) 0:000:x86> t nativeapi+0x24990: 00000000`00424990 7501 jnz nativeapi+0x24993 (00424993) [br=0] 0:000:x86> t nativeapi+0x24992: 00000000`00424992 c3 ret 0:000:x86> t nativeapi+0x2458d: 00000000`0042458d 8bf4 mov esi,esp 0:000:x86> t nativeapi+0x2458f: 00000000`0042458f 6a3c push 0x3c 0:000:x86> t nativeapi+0x24591: 00000000`00424591 6a3c push 0x3c 0:000:x86> t nativeapi+0x24593: 00000000`00424593 6a0a push 0xa 0:000:x86> t nativeapi+0x24595: 00000000`00424595 6a0a push 0xa 0:000:x86> t nativeapi+0x24597: 00000000`00424597 8b85bcfeffff mov eax,[ebp-0x144] ss:002b:00000000`002cfb6c=fc0104f3 0:000:x86> t nativeapi+0x2459d: 00000000`0042459d 50 push eax 0:000:x86> t nativeapi+0x2459e: 00000000`0042459e ff15dcb24700 call dword ptr [nativeapi+0x7b2dc (0047b2dc)] 0:000:x86> t GDI32!Ellipse: 00000000`7d82cc68 8bff ... 途中省略 0:000:x86> t GDI32!Ellipse+0xaa: 00000000`7d82cca7 e80c000000 0:000:x86> t GDI32!NtGdiEllipse: 00000000`7d82ccb8 b888110000 ... 途中省略 0:000:x86> t GDI32!NtGdiEllipse+0xe: 00000000`7d82ccc6 64ff15c0000000 0:000:x86> t wow64cpu!X86SwitchTo64BitMode: 00000000`77b11870 ea2c38b1773300 0:000:x86> t wow64cpu!CpupReturnFromSimulatedCode: 00000000`77b1382c 67448b0424 ... 途中省略 0:000> t wow64cpu!CpupReturnFromSimulatedCode+0x22: 00000000`77b1384e 448bda 0:000> t wow64cpu!TurboDispatchJumpAddressStart: 00000000`77b13851 41ff24cf 0:000> t wow64cpu!ServiceNoTurbo: 00000000`77b13855 4189b5a4000000 ... 途中省略 0:000> t wow64cpu!ServiceNoTurbo+0x22: 00000000`77b13877 ff15f3d7ffff 0:000> t wow64!Wow64SystemServiceEx: 00000000`77b767b0 4c8bdc ... 途中省略 0:000> t wow64!Wow64SystemServiceEx+0xd3: 00000000`77b76883 41ffd4 0:000> t wow64win!whNtGdiEllipse: 00000000`77b49f00 4883ec38 ... 途中省略 0:000> t wow64win!ZwGdiEllipse+0x3: 00000000`77b571f3 b888110000 0:000> t wow64win!ZwGdiEllipse+0x8: 00000000`77b571f8 0f05 syscall 0:000> t wow64win!ZwGdiEllipse+0xa: 00000000`77b571fa c3 0:000> t wow64win!whNtGdiEllipse+0x21: 00000000`77b49f21 4883c438 0:000> t wow64win!whNtGdiEllipse+0x25: 00000000`77b49f25 c3 0:000> t wow64!Wow64SystemServiceEx+0xd6: 00000000`77b76886 8bd8

GDI32のEllipse()をコールすると、NtGdiEllipse()がコールされます。 32-bit版Windowsであればここで直接カーネルモードへ移行しますが、そうはならずに セグメント間CALL命令(call dword ptr fs:[000000c0])でWOW64に制御を移します。 最初にWOW64CPU.DLLのX86SwitchTo64BitModeで32-bit mode(Compatibility mode)から64-bit modeに移行します。 その後はWOW64CPU内のいくつかのルーチン、WOW64WINのwhNtGdiEllipse(), ZwGdiEllipse()をコールし(どちらもエントリーポイントは同じ)、ここでようやくsyscall命令を用いてカーネルモードへ移行します。 (NTDLLのルーチンをコールしていない点に注意。)

最後にUSER32のfunctionについて見てみます。

【USER32 in Win32(WOW64)】
GetDC()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.4.0004.4 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi\Debug\nativeapi.exe Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00000000`00400000 00000000`0047d000 nativeapi.exe ModLoad: 00000000`77ea0000 00000000`77ff3000 ntdll.dll ModLoad: 00000000`77b70000 00000000`77bb6000 C:\WINDOWS\system32\wow64.dll ModLoad: 00000000`77b20000 00000000`77b69000 C:\WINDOWS\system32\wow64win.dll ModLoad: 00000000`77b10000 00000000`77b19000 C:\WINDOWS\system32\wow64cpu.dll (fc.72c): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> g ModLoad: 00000000`77ce0000 00000000`77e92000 NOT_AN_IMAGE ModLoad: 00000000`7d4d0000 00000000`7d5d0000 NOT_AN_IMAGE ModLoad: 00000000`7d5e0000 00000000`7d6f0000 C:\WINDOWS\syswow64\ntdll32.dll ModLoad: 00000000`77ce0000 00000000`77e92000 NOT_AN_IMAGE ModLoad: 00000000`77bc0000 00000000`77ccf000 NOT_AN_IMAGE ModLoad: 00000000`7d4d0000 00000000`7d5d0000 C:\WINDOWS\syswow64\kernel32.dll ModLoad: 00000000`7d940000 00000000`7da10000 C:\WINDOWS\syswow64\USER32.dll ModLoad: 00000000`7d810000 00000000`7d8a0000 C:\WINDOWS\syswow64\GDI32.dll (fc.72c): WOW64 breakpoint - code 4000001f (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ntdll32!DbgBreakPoint: 00000000`7d5f002d cc int 3 0:000:x86> g ModLoad: 00000000`7dee0000 00000000`7df40000 C:\WINDOWS\syswow64\IMM32.DLL ... 途中省略 ModLoad: 00000000`75810000 00000000`758d0000 C:\WINDOWS\syswow64\USERENV.dll (fc.72c): WOW64 breakpoint - code 4000001f (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ntdll32!DbgBreakPoint: 00000000`7d5f002d cc int 3 0:000:x86> t ntdll32!DbgBreakPoint+0x1: 00000000`7d5f002e c3 ret 0:000:x86> t *** WARNING: Unable to verify checksum for nativeapi.exe *** ERROR: Module load completed but symbols could not be loaded for nativeapi.exe nativeapi+0x24653: 00000000`00424653 3bf4 cmp esi,esp 0:000:x86> t nativeapi+0x24655: 00000000`00424655 e854e5ffff call nativeapi+0x22bae (00422bae) 0:000:x86> t nativeapi+0x22bae: 00000000`00422bae e9dd1d0000 jmp nativeapi+0x24990 (00424990) 0:000:x86> t nativeapi+0x24990: 00000000`00424990 7501 jnz nativeapi+0x24993 (00424993) [br=0] 0:000:x86> t nativeapi+0x24992: 00000000`00424992 c3 ret 0:000:x86> t nativeapi+0x2465a: 00000000`0042465a 8bf4 mov esi,esp 0:000:x86> t nativeapi+0x2465c: 00000000`0042465c 8b4508 mov eax,[ebp+0x8] ss:002b:00000000`002cfcb8=00190196 0:000:x86> t nativeapi+0x2465f: 00000000`0042465f 50 push eax 0:000:x86> t nativeapi+0x24660: 00000000`00424660 ff15f8b44700 call dword ptr [nativeapi+0x7b4f8 (0047b4f8)] {USER32!NtUserGetDC (7d958cc0)} ds:002b:00000000`0047b4f8=7d958cc0 0:000:x86> t USER32!NtUserGetDC: 00000000`7d958cc0 b80a100000 ... 途中省略 0:000:x86> t USER32!NtUserGetDC+0xe: 00000000`7d958cce 64ff15c0000000 0:000:x86> t wow64cpu!X86SwitchTo64BitMode: 00000000`77b11870 ea2c38b1773300 0:000:x86> t wow64cpu!CpupReturnFromSimulatedCode: 00000000`77b1382c 67448b0424 ... 途中省略 0:000> t wow64cpu!CpupReturnFromSimulatedCode+0x22: 00000000`77b1384e 448bda 0:000> t wow64cpu!TurboDispatchJumpAddressStart: 00000000`77b13851 41ff24cf 0:000> t wow64cpu!Thunk1ArgSp: 00000000`77b13cde 4d6313 0:000> t wow64cpu!Thunk1ArgSp+0x3: 00000000`77b13ce1 eb4b 0:000> t wow64cpu!Thunk0Arg: 00000000`77b13d2e e83d000000 0:000> t wow64cpu!CpupSyscallStub: 00000000`77b13d70 0f05 syscall 0:000> t wow64cpu!CpupSyscallStub+0x2: 00000000`77b13d72 c3 0:000> t wow64cpu!Thunk0Arg+0x5: 00000000`77b13d33 4d89b42480140000

USER32のNtUserGetDC()をコールすると、いくつかの処理をした後に セグメント間CALL命令(call dword ptr fs:[000000c0])でWOW64に制御を移します。 最初にWOW64CPU.DLLのX86SwitchTo64BitModeで32-bit mode(Compatibility mode)から64-bit modeに移行します。 WOW64CPU内のいくつかのルーチンを実行した後に WOW64CPU内のCpupSyscallStub()をコールし、ここで syscall命令を用いてカーネルモードへ移行します。 (NTDLLのルーチンをコールしていない点に注意。)

Win64の場合

最後にWin64について調べます。 システムコール時の流れはWin32(ネイティブ)の場合と大体同じです。

Win32(WOW64)の場合と同様にWindowsXP Professional x64 Edition(RC1)+Athlon64(NewCastle)の環境で実行します。 windbgはVersion 6.4.4.4(x64版-beta)を使います。 最初はKERNEL32のfunctionです。

【KERNEL32 in Win64】
VirtualQuery()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.4.0004.4 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi64\Debug\nativeapi.exe Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00000000`00400000 00000000`0040f000 nativeapi.exe ModLoad: 00000000`77ea0000 00000000`77ff3000 ntdll.dll ModLoad: 00000000`77ce0000 00000000`77e92000 C:\WINDOWS\system32\kernel32.dll ModLoad: 00000000`77bc0000 00000000`77ccf000 C:\WINDOWS\system32\USER32.dll ModLoad: 000007ff`7fc80000 000007ff`7fd17000 C:\WINDOWS\system32\GDI32.dll (19c.6e8): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> g ModLoad: 000007ff`7d3d0000 000007ff`7d409000 C:\WINDOWS\system32\IMM32.DLL ... 途中省略 ModLoad: 000007ff`7c550000 000007ff`7c654000 C:\WINDOWS\system32\USERENV.dll (19c.6e8): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> t ntdll!DbgBreakPoint+0x1: 00000000`77ef4051 c3 ret 0:000> t *** WARNING: Unable to verify checksum for nativeapi.exe *** ERROR: Module load completed but symbols could not be loaded for nativeapi.exe nativeapi+0x12a8: 00000000`004012a8 41b830000000 mov r8d,0x30 0:000> t nativeapi+0x12ae: 00000000`004012ae 488d942440010000 lea rdx,[rsp+0x140] ss:00000000`0012fc20=fffff80000000000 0:000> t nativeapi+0x12b6: 00000000`004012b6 488b4c2430 mov rcx,[rsp+0x30] ss:00000000`0012fb10=0000000000000000 0:000> t nativeapi+0x12bb: 00000000`004012bb ff15976d0000 call qword ptr [nativeapi+0x8058 (0000000000408058)] ds:00000000`00408058=0000000077cec3e0 0:000> t kernel32!VirtualQuery: 00000000`77cec3e0 4883ec38 ... 途中省略 0:000> t kernel32!VirtualQuery+0x20: 00000000`77cec400 ff15ba51ffff 0:000> t ntdll!ZwQueryVirtualMemory: 00000000`77ef2f90 4c8bd1 0:000> t ntdll!ZwQueryVirtualMemory+0x3: 00000000`77ef2f93 b820000000 0:000> t ntdll!ZwQueryVirtualMemory+0x8: 00000000`77ef2f98 0f05 syscall 0:000> t ntdll!ZwQueryVirtualMemory+0xa: 00000000`77ef2f9a c3 0:000> t kernel32!VirtualQuery+0x26: 00000000`77cec406 85c0

冒頭のModLoadの行を見ると、仮想アドレス空間にロードされているDLLのファイル名はWin32(ネイティブ)の場合と 同じであることがわかります。 (混乱しそうですが、64-bit版WindowsでのNTDLL.DLL system32ディレクトリ内のKERNEL32.DLL GDI32.DLL USER32.DLL はすべて64ビットネイティブなDLLです。) KERNEL32のVirtualQuery()をコールするとNTDLLのZwQueryVirtualMemory()がコールされ、 そこでsyscall命令を用いてカーネルモードへ移行しています。 次はGDI32です。

【GDI32 in Win64】
Ellipse()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.4.0004.4 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi64\Debug\nativeapi.exe Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00000000`00400000 00000000`0040f000 nativeapi.exe ModLoad: 00000000`77ea0000 00000000`77ff3000 ntdll.dll ModLoad: 00000000`77ce0000 00000000`77e92000 C:\WINDOWS\system32\kernel32.dll ModLoad: 00000000`77bc0000 00000000`77ccf000 C:\WINDOWS\system32\USER32.dll ModLoad: 000007ff`7fc80000 000007ff`7fd17000 C:\WINDOWS\system32\GDI32.dll (708.348): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> g ModLoad: 000007ff`7d3d0000 000007ff`7d409000 C:\WINDOWS\system32\IMM32.DLL ... 途中省略 ModLoad: 000007ff`7c550000 000007ff`7c654000 C:\WINDOWS\system32\USERENV.dll (708.348): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> t ntdll!DbgBreakPoint+0x1: 00000000`77ef4051 c3 ret 0:000> t *** WARNING: Unable to verify checksum for nativeapi.exe *** ERROR: Module load completed but symbols could not be loaded for nativeapi.exe ... 途中省略 0:000> t nativeapi+0x139d: 00000000`0040139d ff155d6c0000 0:000> t GDI32!Ellipse: 000007ff`7fcd13c0 4883ec68 ... 途中省略 0:000> t GDI32!Ellipse+0x13c: 000007ff`7fcd14fc e85fbdfdff 0:000> t GDI32!NtGdiEllipse: 000007ff`7fcad260 4c8bd1 0:000> t GDI32!ZwGdiEllipse+0x3: 000007ff`7fcad263 b888110000 0:000> t GDI32!ZwGdiEllipse+0x8: 000007ff`7fcad268 0f05 syscall 0:000> t GDI32!ZwGdiEllipse+0xa: 000007ff`7fcad26a c3 0:000> t GDI32!Ellipse+0x13f: 000007ff`7fcd1501 4c8b6c2438

GDI32のEllipse()をコールすると、GDI32内のNtGdiEllipse()、ZwGdiEllipse()がコールされ(どちらもエントリーポイントは同じ)、 ここで直接syscall命令を用いてカーネルモードへ移行します。(NTDLLのルーチンをコールしていない点に注意。) 最後にUSER32です。

【USER32 in Win64】
GetDC()呼び出し時の実行内容 Microsoft (R) Windows Debugger Version 6.4.0004.4 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: D:\nativeapi\nativeapi64\Debug\nativeapi.exe Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols Executable search path is: ModLoad: 00000000`00400000 00000000`0040f000 nativeapi.exe ModLoad: 00000000`77ea0000 00000000`77ff3000 ntdll.dll ModLoad: 00000000`77ce0000 00000000`77e92000 C:\WINDOWS\system32\kernel32.dll ModLoad: 00000000`77bc0000 00000000`77ccf000 C:\WINDOWS\system32\USER32.dll ModLoad: 000007ff`7fc80000 000007ff`7fd17000 C:\WINDOWS\system32\GDI32.dll (2d4.56c): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> g ModLoad: 000007ff`7d3d0000 000007ff`7d409000 C:\WINDOWS\system32\IMM32.DLL ... 途中省略 ModLoad: 000007ff`7c550000 000007ff`7c654000 C:\WINDOWS\system32\USERENV.dll (2d4.56c): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 00000000`77ef4050 cc int 3 0:000> t ntdll!DbgBreakPoint+0x1: 00000000`77ef4051 c3 ret 0:000> t *** WARNING: Unable to verify checksum for nativeapi.exe *** ERROR: Module load completed but symbols could not be loaded for nativeapi.exe nativeapi+0x143c: 00000000`0040143c 488b8c24a0010000 mov rcx,[rsp+0x1a0] ss:00000000`0012fc80=00000000002901da 0:000> t nativeapi+0x1444: 00000000`00401444 ff15ce6d0000 call qword ptr [nativeapi+0x8218 (0000000000408218)] ds:00000000`00408218=0000000077be1e60 0:000> t USER32!ZwUserGetDC: 00000000`77be1e60 4c8bd1 0:000> t USER32!ZwUserGetDC+0x3: 00000000`77be1e63 b80a100000 0:000> t USER32!ZwUserGetDC+0x8: 00000000`77be1e68 0f05 syscall 0:000> t USER32!ZwUserGetDC+0xa: 00000000`77be1e6a c3 0:000> t nativeapi+0x144a: 00000000`0040144a 4889842470010000

USER32のZwUserGetDC()をコールすると、いくつかの処理をした後に syscall命令を用いてカーネルモードへ移行します。(NTDLLのルーチンをコールしていない点に注意。)

関連リンク

Information for Windows NT and Windows 2000 - The Native API
How Do Windows NT System Calls REALLY Work
System Call Optimization with the SYSENTER Instruction
Windows Source Home Page

最終更新日:2004年1月2日(日)

Copyright(C) 毛流麦花
 
64ビットCPU(AMD64+EM64T)でアセンブラ