歡迎轉載,轉載請注明出處:http://www.cnblogs.com/lanrenxinxin/p/4977488.html
本文是《深入理解Windows操作系統 (第六版) 》關於64位Windows操心系統有關Wow64部分的讀書筆記,因為之前很少有接觸到關於Wow64的部分,記錄一下。
Wow64是允許在64位Windows上執行32位x86應用程序的軟件。它的實現方式是一組用戶模式dll,外加一些來自內核的支持,此內核支持是為了創建32位版本的數據結構,例如進程環境塊(PEB)和線程環境塊(TEB),這些數據結構正常情況下只有64位版本,通過Get/SetThreadContext 來改變Wow64環境下也是由內核實現的,下面是負責Wow64的用戶模式DLL:
- Wow64.dll:管理進程和線程的創建,勾住異常分發和Ntoskrnl.exe導出的基本系統調用。它也實現了文件重定向以及注冊表重定向。
- Wow64Cpu.dll:位每個正在Wow64內部運行的線程,管理他們的32位的CPU環境,針對從32位到64位或者從64位到32位的CPU模式切換,提供了與處理器體系結構相關的支持。
- Wow64Win.dll:截取了Win32k.sys導出的GUI系統調用
- IA64系統上的IA32Exec.bin和Wow32ia32x.dll:包含了IA-32軟件仿真器和它的接口庫,因為Itanium處理器不能以原生方式高效地執行x86的32位指令,所以通過這兩個額外的組件來實現軟件仿真(通過二進制翻譯)。
各個組件的依賴關系如圖:
Wow64進程地址空間布局
Wow64進程可以在2GB虛擬空間中運行,也可以在4GB虛擬空間中運行。如果映像文件的頭部設置了大地址空間感知標志,則內存管理將4GB邊界之上至用戶模式邊界末尾之間保留為用戶模式地址空間。如果映像文件沒有被標記為大地址空間感知的,則內存管理器將保留2GB之上的用戶模式地址空間。
系統調用
Wow64勾住了所有從32位代碼轉變至原生64位系統的代碼路徑,也勾住了64位原生系統需要調用至32位用戶模式代碼的所有路徑。在進程創建的過程中,進程管理器(process manager)將原生的64位Ntdll.dll和針對Wow64進程的32位Ntdll.dll映射到進程地址空間中。當加載器的初始化過程被調用時,它調用Wow64.dll內部的Wow64初始化代碼。然后Wow64建立起32位Ntdll所要求的啟動環境,將CPU模式切換至32位下,並開始執行32位加載器。從這個點開始,執行過程繼續執行,就如同該進程運行在原生的32位系統之上。
Ntdll.dll,User32.dll和Gdi32.dll的特殊32位版本位於\Windows\Syswow64文件夾下。它們調用到Wow64.dll中,而不是發出原生的32位系統調用指令。Wow64轉變到原生的64位模式下,並捕獲到與系統調用有關的參數(將32位指針轉化為64位指針),並發出對應的原生64位系統調用。當原生的系統調用返回時,Wow64把任何輸出參數,如果有必要的話,在返回至32位模式之前從64位轉換成32位格式。
異常分發
Wow64通過Ntdll.dll的KiUserExceptionDispatcher勾住了異常分發過程。無論何時當64位內核將要給一個Wow64進程分發一個異常時,Wow64會捕獲住原生的異常以及用戶模式下的環境記錄(context record),然后准備一個32位異常和環境記錄,並且按照原生32位內核所做的那樣將他分發出去。
用戶APC分發
Wow64通過Ntdll的KiUserApcDispatcher也勾住了用戶模式APC的遞交過程。無論何時當64位內核將要給一個Wow64進程分發一個用戶模式APC時,Wow64把32位APC地址映射到一個更高的64位地址空間范圍中。然后64位Ntdll捕獲住原生的APC以及用戶模式下的環境記錄,將它映射到一個32位地址。然后為它准備一個32位用戶模式APC和環境記錄,並且按照原生32位內核所做的那樣將它分發出去。
控制台支持
因為控制台是由Csrss.exe在用戶模式下實現的,它只是單個原生二進制可執行文件,所以,32應用程序在64位Windows上執行不能執行控制台I/O。類似於專門有一個特殊的rpcrt4.dll用來將32位RPC適配成64位RPC,Wow64的32位kernel32.dll中有專門的代碼來調用到Wow中,以便在與Csrss與Conhost.exe交互過程中對參數進行適配。
用戶回調
Wow64截取了所有從內核到用戶模式的回調。Wow64將這樣的回調也按照系統調用來對待;然而,根據轉換規則是按相反的順序來完成的:輸入參數從64位轉換為32位,而輸出參數則是在該次回調返回時從32位轉換至64位。
文件系統重定向
為了維護應用程序的兼容性,已經降低從Win32到64位Windows的應用程序移植代價,系統目錄名稱仍然保持不變。因此,\Windows\System32文件夾包含了原生的64位映像文件。因為Wow64勾住了所有的系統調用,所以它會解釋所有與路徑相關的API,將\Windows\System32文件夾的名稱替換為\Windows\Syswow64。Wow64也將\Windows\LastGood重定向到\Windows\LastGodd\Syswow64,將\Windows\Regedit.exe重定向到\Windows\syswow64\Regedit.exe。通過使用系統環境變量,%PROGRAMFILE%環境變量對於32位程序被設置為\Program File (x86),而對於64位應用程序被設置為\Program File文件夾,CommonProgramFiles和ComonProgramFiles(x86)也存在,它們總是指向32位的位置,而ProgramW6432和CommonProgramWP6432則無條件指向64位位置。
注:因為有些特定的32位程序可能真的需要知曉或者能夠處理64位映像文件,所以有一個虛擬的目錄,\Windows\Sysnative,使得任何從32位程序發出的針對此目錄的I/O,都免於被文件重定向。這個目錄實際上並不存在,它只是一個允許訪問到真正的System32目錄的虛擬路徑而已,即使運行在Wow64下的程序也不例外。
Windows System32中有一些目錄,由於兼容性的原因,這些子目錄沒有被重定向,所以32位程序訪問這些目錄的時候實際上訪問的是真正的目錄。這些目錄包括:
- %windir%\system32\drivers\etc
- %windir%\system32\spool
- %windir%\system32\catroot和%windir%system32\catroor2
- %windir%\system32\logfiles
- %windir%\system32\driverstore
注冊表的重定向
應用程序和組件程序將它們的配置數據保留在注冊表中。組件程序在安裝的過程中,當它們被注冊的時候,通常將配置數據寫到注冊表中。如果同樣的組件即安裝注冊了一個32位二進制文件,又安裝了一個64位二進制文件,那么,最后被注冊的組件將會覆蓋掉以前組件的注冊,因為他們填寫在相同的位置上。
為了以透明的方式解決這個問題,並且無須對32位組件進行任何代碼修飾,注冊表被分成了兩個部分:原生的和Wow64的。在默認情況下,32位組件訪問32位視圖,64位組件訪問64位視圖,這為32位和64位組件提供了一個安全的環境,並且將32位應用程序的狀態與64位應用程序的狀態隔開來。
為了實現這一點,Wow64截取了所有要打開注冊表的系統調用,並且重新解釋這些注冊表鍵的路徑,將它們指向注冊表的64位視圖。Wow64在以下這些點上分裂注冊:
- HKLM\SOFTWARE
- HKEY_CLASSES_ROOT
但是許多子鍵實際上在32位和64位之間是共享的——也就是說,並不是並非整個鍵巢都被分裂了。在以上的每個鍵下面,Wow64創建了一個稱為Wow6432Node的鍵。在該鍵下面保存的是32位的配置信息。注冊表的所有其他部分對於32位程序和64位程序都是共享的(比如:HKLM\SYSTEM)。
還有一個額外的幫助,如果一個32位應用程序向注冊表中寫入一個以數據“%ProgramFiles%”或者“%commonprogramfiles%”為開頭的REG_SZ或者REG_EXPAND_SZ值,那么Wow64將實際的值修改為”%ProgramFiles(x86)”或“commonprogramfiles(x86)”以便符合前面介紹過的文件重定向和布局結構。32位程序必須正確的寫這些字符串——其他的數據都被忽略,按普通的方式寫入。最后,任何包含”system32”的鍵被替換為“syswowo64”(針對所有的大小寫),也不管大小寫標志是否敏感,除非使用了KEY_WOW64_64KEY,以及該鍵位於“反射鍵”列表中。
如果應用程序需要顯示的指定一個注冊表鍵位於某個特定的視圖中,那么,在RegOpenKeyEx,RegCreateKeyEx,RegOPenKeyTransacted,RegCreateKeyTransacted和RegDeleteKeyEx函數中使用以下標志可以做到這一點:
- KEY_WOW64_64KEY —— 從一個32位或者64位程序顯示的打開一個64位鍵,並禁止REG_SZ或者REG_EXPAND_SZ截取轉換處理。
- KEY_WOW64_32KEY —— 從一個32位或者64位程序顯示的打開一個32位鍵。
控制請求
除了普通的讀和寫操作外,應用程序可以利用Windows 的DeviceIoControl API與某些設備驅動程序通過設備I/O控制函數進行通信。應用程序可能會在調用時指定一個輸入或輸出緩沖區。如果該緩沖區包含了與指針相關的數據,並且發送該控制請求的進程是一個Wow64進程,那么輸入或輸出結構的視圖在32位應用程序和64位驅動程序之前是不相同的。驅動程序可以調用IoIs32bitProcess()函數來檢測一個I/O請求是否從一個Wow64進程發出。
可以參考MSDN上的《Supporting 32-Bit I/O in Your 64-BitDriver》。
限制
Wow64不支持16位應用程序的執行(32位Windows支持),也不支持加載32位內核模式的設備驅動程序。Wow64進程只能加載32位dll,不能加載原生的64位dll。類似的,原生的64位進程不能加載32位的DLL。唯一列外的是,在跨越體系結構差異時,能夠加載僅包含資源或數據的Dll,這是允許的,因為這些Dll只包含數據,不包含代碼。