回覆列表
  • 1 # 傑哥文娛

    WoW64是什麼?

    來自MSDN:

    WOW64是允許32位Windows應用程式無縫執行在64位Windows的模擬器。

    換句話說,隨著64位版本Windows的引進,Microsoft需要拿出一種允許在32位時代的Windows程式與64位Windows新的底層元件無縫互動的解決方案。特別是64位記憶體定址和與核心直接交流的元件。

    兩個NT層,一個核心

    在32位的Windows系統中,要呼叫Windows API的應用程式需要經過一系列的動態連結庫(DLL)。然而,所有的系統呼叫最終會定向到ntdll.dll,它是在使用者模式下將使用者模式API傳遞給核心的最高層。以呼叫CreateFileW為例,這個API呼叫源於使用者模式下的kernel32.dl,隨後它以NtCreateFile傳遞給ntdll,隨後NtCreateFile透過系統排程程式將控制權傳遞給核心。

    在32位Windows下這是非常簡單的,然而,在WoW64下需要額外的步驟。32位的ntdll不可以直接將控制權交給核心,因為核心是64位的,只接受遵循64位ABI的型別(譯者注:ABI,Application Binary Interface,應用二進位制介面)。正因為如此,一個翻譯層以幾個標準的命名為wow64.dll,wow64cpu.dll和wow64win.dll的DLL的形式被新增到64位Windows。這幾個DLL負責將32位呼叫轉換成64位呼叫。那些呼叫最終被定向到對映到每個32位程序中的64位ntdll。許多關於這種從32位系統呼叫到64位系統呼叫(1)的神奇轉換的資訊是可獲得的,所以我們不會從這裡進入。我們最關注的是核心何時和怎樣將64位版本的ntdll對映到一個32位程序。看起來像這樣:

    我們特別關注倒數第二項。我們能發現ntdll被對映到地址是64位地址範圍(7FFFFED40000-7FFFFEF1FFFF),而且它的位置在Windows 64位系統檔案所在的System32\路徑下。然而,我們知道32位程序不可以訪問或者執行在64位記憶體空間。

    什麼是虛擬地址描述符?

    VAD是Windows作業系統跟蹤系統中可用物理記憶體的許多方法之一。VAD專門跟蹤每個程序使用者模式範圍的保留的和提交的地址。任何時候一個程序請求一些記憶體,一個新的VAD實力被建立用來跟蹤記憶體。

    VAD被構造成一個自平衡樹,每個節點描述了一段記憶體範圍。每個節點至多包含兩個子節點,左邊是低地址,右邊是高地址。每個程序被分配一個VadRoot,之後透過遍歷VadRoot來分辨額外用來描述保留或提交的虛擬地址範圍的額外節點。我們需要關注WindDBG中的!vad命令的輸出,因為這是我們將大量使用來跟蹤64位Windows中32位程序的對映的輸出。對於這個練習,不是所有的域對我們來說都是特別有趣的。我們考慮測試程式HelloWorld.exe的輸出。透過!process ProcessObject 命令的輸出來分辨我們程序的VadRoot。

    一旦我們確定了VadRoot,我將地址輸入到 !vad 命令。(輸出為了容易分析已被截斷)

    我們看到五列: "VAD", "Level", "Start", "End", 和"Commit".!vad命令 接受VAD例項的地址;在我們的例子中,我們已經為它提供了在此程序中透過使用!process命令獲得的VadRoot。

    VAD地址是當前VAD結構體或例項的地址:

    等級(Level)描述了這個VAD例項(節點)在所在樹中的級別。Level 0是從上面!process輸出中獲得的VadRoot。開始(Starting)和結束(Ending)地址值用VPN(Virtual Page Numbers,虛擬頁數量)表示。這些地址可以透過乘以頁面大小(4kb)或者左移3位轉化為虛擬地址。結束VPN會新增一個額外的0xFFF來擴充套件到頁面末尾。如我們上面例子中的D20->D20000,DD20->DD2FFF。提交(Commit)是被此VAD例項描述的範圍內提交頁面的數量。分配型別(type of allocation)告訴我們改特定範圍是否已經被對映或是程序私有的。訪問型別(Type of access)描述改範圍內的允許訪問。最後是被對映到當前區域對應的名稱。一個AVD例項可以以多種方式建立。如透過使用對映API(CreateFileMapping/MapViewOfFile)或者記憶體分配API如VirutalAlloc函式。記憶體可以是保留或者提交的(或free的),或保留和部分提交的。無論哪一種,一個VAD項被對映到程序的Vad樹來讓記憶體管理器知道此程序中當前已提交的記憶體。我們對VAD 的觀察將揭示WoW64下執行的32位程序的初始設定。 對映NT子系統DLL

    程序初始化的早期,在主可執行檔案被對映和初始化之前,Windows為特殊區域確定和保留一些地址範圍。其中包含初始程序地址空間,共享系統空間(_KUSER_SHARED_DATA),控制流守護點陣圖區域,和NT本地子系統(ntdll)。由於程序初始化整體的複雜性,我們只關注最後一塊,它包含32位ntdll和64位ntdll載入到32位程序地址空間的邏輯。我們關注一系列的API呼叫和在每個點的記憶體區域的虛擬地址描述符(VAD)。為了讓核心區分怎樣對映一個新程序,它需要知道是否這是一個WoW64程序。當程序物件最初被建立,核心透過讀取名為_EPROCESS.Wow64Process的未文件化結構體_EPROCESS結構體的值來實現此操作。

    PspAllocateProcess是我們探索開始的地方,但是更具體的說,我們開始在MmInitializeProcessAddressSpace()。MmInitializeProcessAddressSpace()負責與一個新程序地址空間有關的初始化。它呼叫MiMapProcessExecutable,該函式建立了定義初始程序可定址記憶體空間的VAD項,隨後將新建立的程序對映到它的基虛擬地址。

    一個特別有趣的函式是PspMapSystemDlls。我們關注在呼叫PspMapSystemDlls之前的程序地址空間的樣子。在WinDBG中確保我們當前處於我們測試應用程式的上下文中(.process),並尋找當前VadRoot(!vad output)。

    到目前為止我們可以觀察到,我們的程序在32問地址空間中被對映和分配了一個基地址(1200),核心共享記憶體(0x7FFE0000-0x7FFE0FFF) 和64KB保留記憶體區域(0x7FFE1000-0x7FFEFFFF) 也已經被對映到他們各自的虛擬地址。

    PspMapSystemDlls透過一個包含多個平臺子系統模組的全域性指標迭代。對於x86和x64Windows,這些是分別位於C:\Windows\SysWow64 和C:\Windows\System目錄中的ntdll.dll。

    一旦PspMapSystemDlls發現要載入的DLL,它呼叫PspMapSystemDll 來對映他們(DLLs)到程序的地址空間。該函式非常簡短,下面展示了一個片段。為了正確對映本地子系統,需要滿足一些條件。

    PspMapSystemDll透過呼叫MmMapViewOfSection實現實際的本地DLL的對映,並儲存所佔的基地址。在這兩個DLL對映完成並且他們的VAD項初始化完成後,我們的32位程序地址空間看起來像這樣:

    所以現在,我們對映完我們的程序(0xc40000-0xcf2fff),核心共享記憶體空間(0x7ffe0000-0x7ffe0fff),32位地址空間的有效結束區域(0x7ffe1000-0x7ffeffff),和我們的兩個NT子系統DLL。

    鎖定地址空間

    為了完成32位程序的對映,還有最後一步要做。我們知道一個32位程序最多定址到2GB的虛擬記憶體,所以Windows需要遮蔽此程序剩餘的地址空間。對於32位程序,遮蔽在 0x7FFF0000 - 0x7FFFFFFF之後;然而,0x7FFeFFFF之後什麼也不可以對映。基於此事實,緊鄰64位NTDLL的記憶體區域需要保留或者遮蔽。要做到這一點,核心標記剩下的64位地址空間為私有。它透過遍歷當前程序的VAD樹和定位最後可用的虛擬地址來建立此VAD項,然後附加一個新的VAD項。

    完成此任務的API是MiInitializeUserNoAccess。該函式接受當前程序控制代碼和一個虛擬地址。傳遞的虛擬地址是0x7FFF0000,這是32為程序最後可定址範圍的起始。然後,它遍歷當前的VAD項並執行一個新範圍的插入,該範圍覆蓋了32位程序剩餘的地址空間。在此呼叫後,我們的程序地址空間看起來像這樣:

    我們現在可以發現,我們的32位程序已經對映,並且它的合規的記憶體地址範圍已經被核心保留。涵蓋0x7FFF0 - 0x7FFFFED3F和0x7FFFFEF20- 0x7FFFFFFEF 範圍的VAD例項已經被核心保留為私有。隨後任何檢索記憶體的呼叫僅僅會發生在允許的32為地址空間內。一旦程序完全載入,我們可以看到額外的已提交的記憶體出現在程序(0xC40000)附近的地址空間。

    結束演講

    我們觀察到64位Windows下的32位程序的初始對映以及64位ntdll如何被對映到64位區域,隨後64位地址空間被鎖定,防止使用者訪問,我們學到了什麼?

    1. 早期初始化邏輯決定我們是否準備對映一個WoW64程序。

    2. 分配最初的32位地址空間區域;這包括最高可訪問的32位地址範圍,和程序首選的基虛擬地址。

    3. NT子系統DLL被載入到他們各自的地址範圍,32位ntdll載入到32位空間,64位ntdll載入到64位地址空間。

    4. MmInitializeUserNoAccess 用來建立與64位ntdll範圍相鄰的範圍。這具有從32位程序鎖定64位可定址空間的效果。

    希望這篇文章提供了一些關於Windows如何允許講32位程序無縫整合到64位Windows作業系統的透明度。隨著WoW64模擬層的新增,對地址空間可用性進行了一些額外的考慮,並且這個過程反映了一些這些考慮和及其實現。

  • 中秋節和大豐收的關聯?
  • 美軍的海狼級核潛艇有多麼強?