作者:陳曦
日期:2012-6-19 12:28:30
環境:[win7旗艦版 SP1 ; Intel i3 x86, 支援64位 ;vs2010 ; wrk-v1.2 ; Virtual PC 2007 windows 2003 server sp1 standard edition鏡像 ;WinDbg 6.11.0001.404(x86) ]
轉載請註明出處
Q: 在windows下,調用CreateProcess這個API來建立進程,它內部究竟做了什嗎?
A: 對於作業系統,一般肯定是分層的。核心將處理最終的建立進程操作,但是它的上層可能有一些模組,進行一些參數合法性判斷或者為了可移植考慮的判斷。windows同樣不例外。看看windows下面核心上面的模組:
不妨先寫一個CreateProcess的程式,通過逆向工程得到內部調用的東西。
Q: 如下代碼:
#include <windows.h>#include <stdio.h>int main(){ PROCESS_INFORMATION processInfo; STARTUPINFOAstartupInfo; ZeroMemory(&processInfo, sizeof(processInfo)); ZeroMemory(&startupInfo, sizeof(startupInfo)); startupInfo.cb = sizeof(startupInfo); BOOL ret = CreateProcessA(NULL, "c:\\windows\\system32\\cmd.exe", NULL, NULL, false, 0, NULL, NULL, &startupInfo, &processInfo); if(ret)printf("create process ok...\n"); else {printf("create process failed...\n");printf("error is %d", GetLastError()); } return 0;}
編譯成CreateProcessDemo.exe, 運行:
可以看出,它正確地建立了進程。
A: 下面我們將找出哪個模組包含CreateProcessA函數。進入VS的命令列工具,
使用如下命令dumpbin.exe /all CreateProcessDemo.exe > d:\dumpbin_createprocessdemo.txt得到所有dump的資訊,找到如下資訊:
KERNEL32.dll 41819C Import Address Table 41803C Import Name Table 0 time date stamp 0 Index of first forwarder reference A4 CreateProcessA 1C0 GetCurrentProcess 4C0 TerminateProcess 162 FreeLibrary 4F1 VirtualQuery 214 GetModuleFileNameW 24A GetProcessHeap 2CB HeapAlloc 2CF HeapFree 279 GetSystemTimeAsFileTime 1C1 GetCurrentProcessId 1C5 GetCurrentThreadId 293 GetTickCount 3A7 QueryPerformanceCounter CA DecodePointer 4A5 SetUnhandledExceptionFilter
可以看出,CreateProcessA是在KERNEL32.DLL中被引用的。
Q: 現在我們可以在kernel32.dll中查看CreateProcessA的調用關係了?
A: 是的。使用ida,開啟系統目錄下面的kernel32.dll, 並尋找CreateProcessA函數的位置:
可以在靠近最後的時候發現調用CreateProcessInternalA常式。
Q: 繼續尋找CreateProcessInternalA常式的內部實現,它最終會調用CreateProcessInternalW來實現。繼續尋找CreateProcessInternalW的實現,發現它最終會調用NtCreateUserProcess常式(在xp或者server 2003下,會調用NtCreateProcessEx).此常式不在kernel32.dll中,它在哪裡?
A: 正如上面的圖示描述,它以nt開頭,它在ntdll.dll中。同樣適用ida開啟ntdll.dll, 找到NtCreateUserProcess的實現:
Q: ZwCreateUserProcess是什麼,好像和NtCreateUserProcess是一樣的?
A: 僅僅在ntdll.dll中來說,依照上面的,它們是一致的;可是在核心中,它們不完全一致。Nt開頭的常式會進行存取權限和參數合法性判斷,而Zw不會,Zw它可以由核心模式代碼直接使用;同時,調用Zw開頭的常式會將先前的模式改變為核心模式,而使用Nt開頭的常式不能。
這裡,可以看下xp或2003 server下的nt核心對應的代碼:
////NtCreateProcess函數是調用它 的。//此函數調用PspCreateProcess函數。//NTSTATUS // typedef ULONG NTSTATUS;NtCreateProcessEx( __out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ParentProcess, __in ULONG Flags, //建立標誌 __in_opt HANDLE SectionHandle, __in_opt HANDLE DebugPort, __in_opt HANDLE ExceptionPort, __in ULONG JobMemberLevel )/*++ Routine Description: //常式描述 This routine creates a process object. //建立一個進程對象Arguments: //參數 ProcessHandle - Returns the handle for the new process. //返回新進程的控制代碼指標 DesiredAccess - Supplies the desired access modes to the new process. //提供新進程的存取權限 ObjectAttributes - Supplies the object attributes of the new process. //提供新進程的對象屬性 . . .--*/{ NTSTATUS Status;//// 在偵錯模式下才有用;否則,被定義為空白語句// PAGED_CODE(); //如果線程之前執行的模式不是核心模式 if (KeGetPreviousMode() != KernelMode) { // // Probe all arguments //檢查所有的參數 // try { ProbeForWriteHandle (ProcessHandle); //ProbeForWriteHandle宏在ex.h檔案中定義 } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode (); } } if (ARGUMENT_PRESENT (ParentProcess)) { //如果參數--父進程控制代碼存在 Status = PspCreateProcess (ProcessHandle, //調用PspCreateProcess函數 DesiredAccess, ObjectAttributes, ParentProcess, Flags, SectionHandle, DebugPort, ExceptionPort, JobMemberLevel); } else { //否則,返回參數不合法的錯誤 Status = STATUS_INVALID_PARAMETER; } return Status;}
Q: 剛剛所說的使用者模式是如何進入核心模式的?
A: 上面的ntdll.dll中執行依然是使用者模式,NtCreateUserProcess通過向eax傳入中斷號, edx傳入7FFE0300H為參數地址來進行系統調用,進入核心模式。它的內部實現為:
NTSTATUSNtCreateProcess( //建立進程 __out PHANDLE ProcessHandle, //進程控制代碼的指標 __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ParentProcess, //父進程控制代碼 __in BOOLEAN InheritObjectTable, //是否繼承控制代碼表 __in_opt HANDLE SectionHandle, __in_opt HANDLE DebugPort, //調試連接埠 __in_opt HANDLE ExceptionPort //異常連接埠 ){ ULONG Flags = 0; if ((ULONG_PTR)SectionHandle & 1) { Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY; } if ((ULONG_PTR) DebugPort & 1) { Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT; } if (InheritObjectTable) { //是否繼承控制代碼表 Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES; }//調用NtCreateProcessEx函數 return NtCreateProcessEx (ProcessHandle, DesiredAccess, ObjectAttributes OPTIONAL, //OPTIONAL只是表示選擇性參數的意思,並不會對編譯造成影響 ParentProcess, Flags, //上面計算得到的Flags SectionHandle, DebugPort, ExceptionPort, 0);}
因為未找到win7的WRK原始碼資訊,上面為nt核心的實現。注意,如上程式執行是在win7系統下的執行。
Q: 為了更清楚地得到內部調用細節,是否可以使用虛擬機器來測試一下?
A: 可以,使用虛擬機器進行核心模式調試,來驗證上面的整個過程;當然,因為使用的虛擬機器系統是2003 server sp1, 它的核心和win7核心是不同的,所以會有不一致的地方。
Q: 當虛擬機器和調試器啟動後(此具體過程略,關於wrk的配置可以在網上搜尋),然後做什嗎?
A: 先在win7下面編寫一個可以在虛擬機器系統2003 server sp1下可以跑的程式.可以是任意的了。
Q: 如下代碼:
#include <stdio.h>int main(){ printf("hello\n"); return 0;}
為了保證在低版本下可運行,編譯時間將使用的庫設定成使用靜態庫。
編譯成hello.exe.放到虛擬機器系統案頭上。此時讓hello.exe運行嗎?
A: 不要著急,我們先在NtCreateProcessEx開始處加斷點。如下:
如紅色地方。接著讓虛擬機器繼續運行,在虛擬機器的案頭雙擊hello.exe運行起來。
Q: 雙擊後,調試器遇到斷點停頓下來:
如上,紫色位置為windbg跑到斷點時的。此時,是否就可以看看呼叫堆疊了?
A: 是的。使用在kd提示符後輸入k命令得到堆棧資訊:
可以看出從kernel32中的CreateProcessW,到CreateProcessInternalW, 又到ntdll中的NtCreateProcessEx,接著調入陷入核心常式KiFastSystemCallRet進入nt模組(即為核心模組)。在核心中,它調用NtCreateProcessEx函數來完成具體的工作。
Q: ntdll中的NtCreateProcessEx與nt中的NtCreateProcessEx名稱一樣,會有衝突嗎?
A: 它們不是簡單的上層和下層的模組,中間又有了一些模組,所以名稱一樣不會直接造成衝突;並且ntdll調用核心常式也肯定不是把函數名稱傳進去的; 編譯器是可以正確地找到了地址,運行時也不會混淆。
Q: 在核心中,NtCreateProcessEx和ZwCreateProcessEx有什麼區別?
A: 使用x nt!ZwCreateProcessEx先看看核心中是否存在ZwCreateProcessEx:
由上可以看出確實存在。
接著反組譯碼:
可以看出,它的實現很直接,調用系統調用具體處理。它和之前看到的NtCreateProcessEx顯然不一樣,NtCreateProcessEx先進行了一些參數和模式的判斷。所以說,ZwCreateProcessEx會將啟動並執行模式轉變成核心模式,因為它通過系統調用被迫陷入核心模式。不過,NtCreateProcessEx內部的處理依然是它實際的實現。
Q: 那麼,調用結束時如何返回呢?
A: 如下,
在base\ntos\ke\i386\trap.asm中包含了返回時的處理:
kss61:;; Upon return, (eax)= status code. This code may also be entered from a failed; KiCallbackReturn call.; mov esp, ebp ; deallocate stack space for arguments;; Restore old trap frame address from the current trap frame.;kss70: mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address mov edx, [ebp].TsEdx ; restore previous trap frame address mov [ecx].ThTrapFrame, edx ;;; System service's private version of KiExceptionExit; (Also used by KiDebugService);; Check for pending APC interrupts, if found, dispatch to them; (saving eax in frame first).; public _KiServiceExit_KiServiceExit: cli ; disable interrupts DISPATCH_USER_APC ebp, ReturnCurrentEax;; Exit from SystemService; EXIT_ALL NoRestoreSegs, NoRestoreVolatile
主要就是恢複調用時的資訊,繼續執行。
作者:陳曦
日期:2012-6-19 12:28:30
環境:[win7旗艦版 SP1 ; Intel i3 x86, 支援64位 ;vs2010 ; wrk-v1.2 ; Virtual PC 2007 windows 2003 server sp1 standard edition鏡像 ;WinDbg 6.11.0001.404(x86) ]
轉載請註明出處