色婷婷狠狠18禁久久YY,CHINESE性内射高清国产,国产女人18毛片水真多1,国产AV在线观看

Windows是如何將64位Ntdll映射到32位進程的

阮建安2年前14瀏覽0評論

Windows是如何將64位Ntdll映射到32位進程的?

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(Virtual Address Descriptor,虛擬地址描述符)是什么和它將如何幫助我們理解加載64位dll到32位進程的機制的。

什么是虛擬地址描述符?

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模擬層的添加,對地址空間可用性進行了一些額外的考慮,并且這個過程反映了一些這些考慮和及其實現。