?? 漫談兼容內(nèi)核之十:windows的進程創(chuàng)建和映像裝入.txt
字號:
漫談兼容內(nèi)核之十:Windows的進程創(chuàng)建和映像裝入
[align=center]毛德操[/align]
關(guān)于Windows的進程創(chuàng)建和映像裝入的過程,“Microsoft Windows Internals 4e”一書的第六章中有頗為詳細的說明。本文就以此為依據(jù),夾譯、夾敘、夾議地作一介紹。書中說,創(chuàng)建進程的過程分成六個階段,發(fā)生于操作系統(tǒng)的三個部分中,那就是:Windows客戶端即某個應(yīng)用進程的包括Kernel32.dll在內(nèi)的動態(tài)連接庫,Windows的“執(zhí)行體”、即內(nèi)核(確切地說是內(nèi)核的上層),以及Windows子系統(tǒng)的服務(wù)進程Csrss中。這六個階段是:
1. 打開目標映像文件。
2. 創(chuàng)建Windows的“執(zhí)行體進程對象”,也就是內(nèi)核中的“進程控制塊”數(shù)據(jù)結(jié)構(gòu)。
3. 創(chuàng)建該進程的初始(第一個)線程,包括其堆棧、上下文、以及“執(zhí)行體線程對象”,即內(nèi)核中的“線程控制塊”數(shù)據(jù)結(jié)構(gòu)。
4. 將新建進程通知Windows子系統(tǒng)。
5. 啟動初始線程地運行(除非因為參數(shù)中的CREATE_SUSPENDED標志位為1而一創(chuàng)建便被掛起)。
6. 在新進程和線程的上下文中完成用戶空間的初始化,包括裝入所需的DLL,然后開始目標程序的運行。
下面分階段敘述。
第一階段:打開目標映像文件
在Win32位API中,創(chuàng)建進程是由CreateProcess()完成的。這實際上是個宏定義,根據(jù)不同的情況定義成CreateProcessA()或CreateProcessW()之一,這兩個函數(shù)都在kernel32.dll中(可以用工具depends觀察)。兩個函數(shù)的區(qū)別僅在于字符串的表達,前者采用ASCII字符,而后者采用“寬字符”、即Unicode。實際上Windows的內(nèi)部都采用寬字符,所以前者只是把字符串轉(zhuǎn)換成寬字符格式,然后調(diào)用后者。
可以在Windows上運行的可執(zhí)行軟件有好幾類,處理的方法自然就不一樣:
● Windows的32位.exe映像,直接運行。
● Windows的16位.exe映像,啟動ntvdm.exe,以原有命令行作為參數(shù)。
● DOS的.exe、.com、或.pif映像,啟動ntvdm.exe,以原有命令行作為參數(shù)。
● DOS的.bat或.cmd批命令文件(腳本),啟動cmd.exe,以原有命令行作為參數(shù)。
● POSIX可執(zhí)行映像,啟動posix.exe,以原有命令行作為參數(shù)。
● OS/2可執(zhí)行映像,啟動os2.exe,以原有命令行作為參數(shù)。
這里面最重要的當然是32位的.exe映像,而最后兩類現(xiàn)在已經(jīng)很少見了。從對于除32位.exe以外的各種映像的處理,讀者不妨對比一下Wine對.exe映像的處理,看看這里有著什么樣的相似性。
當然,我們在這里只關(guān)心32位.exe映像。對于這一類映像,CreateProcess()首先打開映像文件,再為其(分配)創(chuàng)建一個“Section”、即內(nèi)存區(qū)間。創(chuàng)建內(nèi)存區(qū)間的目的當然是要把映像文件影射到這個區(qū)間,不過此時還不忙著映射,還要看看。看什么呢?首先是看已經(jīng)打開的目標文件是否一個合格的.exe映像(萬一是DLL映像?)。還要看的事就有點出乎讀者意外了,看的是在“注冊表”中的這個路徑:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
用depends可以看到,ntdll.dll中有個函數(shù)LdrQueryImageFileExecutionOption s(),就是專門干這個事的。
如果上述路徑下有以目標映像文件的文件名和擴展名為“鍵”的表項,例如“image.exe”,而表項中又有名為“Debugger”的值,那么這個值(一個字符串)就替換了原來的目標文件名,變成新的目標映像名,并重新執(zhí)行上述的第一階段操作。這樣做的目的當然是為調(diào)試程序提供方便,但是我們不妨設(shè)想:如果黑客或某個木馬程序設(shè)法在注冊表中加上了一個表項?這時候用戶以為是啟動了程序A,而實際啟動的卻是B!。
第二階段:創(chuàng)建內(nèi)核中的進程對象
我們知道,Linux上的每個進程(線程)都有一個“進程控制塊”、即task_struct數(shù)據(jù)結(jié)構(gòu),與具體進程/線程有關(guān)的絕大部分信息都集中存儲在這個數(shù)據(jù)結(jié)構(gòu)中。而Windows則有所不同。首先,Windows的進程和線程各有不同的“對象”、即數(shù)據(jù)結(jié)構(gòu),從概念上把線程和進程分離開來。線程是具體的(執(zhí)行)上下文,是CPU調(diào)度的單位和目標,而進程只是若干共享地址空間和特性(如調(diào)度優(yōu)先級)的線程的集合。于是,進程有進程的數(shù)據(jù)結(jié)構(gòu),線程有線程的數(shù)據(jù)結(jié)構(gòu)。這就好像是對一組task_struct數(shù)據(jù)結(jié)構(gòu)“提取公因子”所形成的結(jié)果,這個舉措是很好理解的。進一步,Windows又把本可集中存儲的的進程數(shù)據(jù)結(jié)構(gòu)也拆分成好幾個對象,有的在內(nèi)核中,有的則在用戶空間。
內(nèi)核中與進程有關(guān)的對象有:
● EPROCESS。即struct _EPROCESS,在“Internals”書中也稱為“Process Block”。它代表著Windows的一個進程,‘E’表示“Executive”,微軟把Windows內(nèi)核中的上層稱為“Executive”、以區(qū)別于下層的設(shè)備驅(qū)動和內(nèi)存管理等成分、一般翻譯成“執(zhí)行體”?!癊xecutive”也有“管理”、“運行”的意思(所以CEO就是“總裁”)。
● KPROCESS。這是EPROCESS內(nèi)部的一個成分,其名稱就叫“Pcb”。
● W32PROCESS。下面將要講到,在用戶空間有個“Windows子系統(tǒng)”的服務(wù)進程csrss。這個服務(wù)進程為系統(tǒng)中的每個Windows應(yīng)用進程都維持著一個數(shù)據(jù)結(jié)構(gòu),其中包含了一些與窗口和圖形界面有關(guān)的信息。而對于窗口和圖形界面的操作原來也是由csrss在“客戶”進程的請求下實現(xiàn)的。但是,為了提高效率,后來把這部分功能移到了內(nèi)核中。與此相應(yīng),有關(guān)數(shù)據(jù)結(jié)構(gòu)的一部分也需要移到內(nèi)核中,就成了W32PROCESS。
既然KPROCESS是EPROCESS一部分,實際上內(nèi)核中與進程有關(guān)的對象實際上只有兩種,就是EPROCESS和W32PROCESS。不過這里沒有包括“打開對象表”,那也是每個進程都有的(Linux內(nèi)核中的“打開文件表”也在進程控制塊的外面)。
用戶空間與進程有關(guān)的對象有:
● 如上所述,把W32PROCESS數(shù)據(jù)結(jié)構(gòu)移入內(nèi)核以后,csrss仍需要為每個Windows進程保持一些別的信息,所以csrss內(nèi)部仍有按進程的相應(yīng)數(shù)據(jù)結(jié)構(gòu)。
● PEB(Process Environment Block)、即“進程環(huán)境塊”。PEB中記錄著進程的運行參數(shù)、映像裝入地址等等信息。PEB在用戶空間中的位置是固定的,總是在0x7ffdf000。在Windows中,用戶空間和系統(tǒng)空間的分界線是2GB、即0x80000000,所以PEB在靠近用戶空間頂端的地方。
“Internals”書中并未給出有關(guān)數(shù)據(jù)結(jié)構(gòu)的定義,但是通過Debug手段給出了EPROCESS的內(nèi)部結(jié)構(gòu):
[code] +0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32Void
+0x0c0 ExceptionPort : Ptr32Void
+0x0c4 ObjectTable : Ptr32_HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32_ETHREAD
+0x118 HardwareTrigger : Uint4B[/code]
可見,EPROCESS的第一個成分是Pcb,其類型是_KPROCESS、即KPROCESS,這是一個大小為0x6c的數(shù)據(jù)結(jié)構(gòu)。書中也給出了它的內(nèi)部結(jié)構(gòu)。
“Undocumented Windows 2000 Secrets”一書也以Debug手段給出了這個數(shù)據(jù)結(jié)構(gòu)的內(nèi)部結(jié)構(gòu),但是列出的結(jié)構(gòu)與此有很大的不同,也許是因為版本的關(guān)系。從所列的內(nèi)容看,似乎“Secrets”一書倒是正確的,因為那里所列的EPROCESS結(jié)構(gòu)中有關(guān)于虛存的成分Vm,是一個大小為0x50的數(shù)據(jù)結(jié)構(gòu),而這里沒有,但是虛存(地址空間)顯然是進程的主要資源,所以EPROCESS數(shù)據(jù)結(jié)構(gòu)中理應(yīng)有它的位置。由此看來,“Secrets”一書所述更接近于桌面和服務(wù)器系統(tǒng)的現(xiàn)實,而“Internals”書中所列可能更接近于不帶MMU的嵌入式系統(tǒng)。而且,“Secrets”一書還在附錄C中給出了通過逆向工程手段得到的EPROCESS和PEB等數(shù)據(jù)結(jié)構(gòu)的定義(代碼),這當然是很有價值的。
那么,如果確有不同版本的EPROCESS,這會有什么影響呢?首先,用戶空間的應(yīng)用程序不能直接訪問內(nèi)核中的EPROCESS數(shù)據(jù)結(jié)構(gòu),所以具體的EPROCESS數(shù)據(jù)結(jié)構(gòu)屬于內(nèi)核的內(nèi)部實現(xiàn),只要內(nèi)核中的各種成分、各個環(huán)節(jié)都配套成龍,“自圓其說”,就沒有什么問題,這跟Linux內(nèi)核中一些條件編譯和裁剪的效果是類似的。可是,另一方面,對于可以動態(tài)裝入的.sys模塊,如果在模塊中需要訪問這些數(shù)據(jù)結(jié)構(gòu),那就可能有問題了,因為.sys模塊都是以二進制映像的形式提供的,不像在Linux中那樣可以由源代碼重新編譯。怎么辦呢?我們可以到Windows的DDK中去找找答案。
在DDK的.h文件中,有函數(shù)IoGetCurrentProcess()的申明:
[code]NTKERNELAPI
PEPROCESS
IoGetCurrentProcess(
VOID
);[/code]
這個函數(shù)是內(nèi)核為.sys模塊提供的支撐函數(shù),相當于由Linux內(nèi)核導(dǎo)出的函數(shù)。其返回值類型是PEPROCESS,就是指向EPROCESS數(shù)據(jù)結(jié)構(gòu)的指針。顯然,這跟Linux內(nèi)核中的current相似,調(diào)用的目的是獲取當前進程的EPROCESS數(shù)據(jù)結(jié)構(gòu)(指針)。但是,DDK的.h文件中卻并未給出EPROCESS數(shù)據(jù)結(jié)構(gòu)的定義,所以調(diào)用這個函數(shù)所得到的僅僅是個指針,實際上與void*并無區(qū)別。這意味著在.sys模塊中是不允許直接訪問其內(nèi)部成分的。那么,.sys模塊如何使用這個指針呢?下面就是一個例子,還是在DDK中:
[code]NTKERNELAPI
VOID
MmProbeAndLockProcessPages (
IN OUT PMDL MemoryDescriptorList,
IN PEPROCESS Process,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -