《Windows內核安全與驅動編程》-第六章學習

64位和32位的內核開發差異

6.1 64位系統新增機制

6.1.1 WOW64 子系統

WOW64 子系統,是64位 Windows 系統爲了兼容32位的應用程序而新增的子系統。當一個32位應用程序發起系統調用時,WOW64 子系統就會先把這些指針的長度轉換成合適的長度,然後再把系統調用請求提交給內核。通常把這個“攔截-轉換” 的過程稱爲 “thunking”。

WOW64 子系統有兩個重要的模塊,分別爲文件系統重定向器模塊和註冊表模塊重定向器模塊。

​ Windows 64位系統存在兩個 System32 目錄,,分別爲 %windir%\Sysem32%windir%\SysWOW64 目錄; 前者目錄下主要包含64位系統二進制文件,後者目錄下主要包含32位系統二進制文件。當一個32位的程序在32位系統環境下,需要到 System32 目錄下加載所依賴的系統DLL,當把這個程序放在64位的系統環境下運行時,64位環境下的同樣目錄存放的是64位的DLL文件,這樣會導致32位程序無法運行。爲了解決這個問題, WOW64子系統的文件重定向器對 System32 目錄做了透明的重定向。在絕大多數情況下,32 位應用程序訪問 %windir%\System32 目錄會被重定向到**%windir%\SysWOW64** 目錄。

HANDLE hFile = CreateFile(_T("C:\\windows\\system32\\testfile.txt"),GENERIC_READ_0,NULL,CREATE_ALWAYS,0,NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
    CloseHandle(hFile);
    hFile = INVALID_HADLE_VALUE;
}

​ 上面這段代碼如果運行在64位的環境下,被編譯成32位和64位應用程序,testfile.txt最終保存的目錄會不同。如果32位程序想訪問真實的 System32 目錄,需要調用API關閉重定向功能。

BOOL WINAPI Wow64DisableWow64FsReadirection(_Out_ PVOID *OldValue);

​ 該API只有一個參數,用於保存原來文件重定向器的狀態,當需要恢復重定向器的狀態的時候,可以使用 Wow64RevertWow64FsReadirection 函數。

BOOL WINAPI Wow64RevertWow64FsReadirection(_In_ PVOID OldValue);
PVOID pOldValue = NULL;
BOOL bRet = Wow64DisableWow64FsReadirection(&pOldValue);
if(bRet == TRUE )
{
        HANDLE hFile = CreateFile(_T("C:\\windows\\system32\\testfile.txt"),GENERIC_READ_0,NULL,CREATE_ALWAYS,0,NULL);
    if(hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFile);
        hFile = INVALID_HADLE_VALUE;
    }
    Wow64RevertWow64FsReadirection(OldValue);
}

​ 上述代碼即可在生成文件前關閉重定向,在文件生成後開啓重定向。注意並非所有的 %windir\System32% 下的目錄和文件都重定向。還有一些特殊目錄是不重定向的,它們分別是:

  • %windir%\System32\catroot
  • %windir%\System32\catroot2
  • %windir%\System32\dirvers\etc
  • %windir%\System32\catroot\logfiles
  • %windir%\System32\catroot\spool

​ 註冊表重定向器和文件系統重定向器功能類似,但是註冊表重定向器提供的功能更爲複雜,它除了提供重定向功能外,還提供註冊表反射功能。反射功能與本章關係不大不做介紹。

​ 在64位系統下運行的32位程序,訪問 HKEY_LOCAL_MACHINE\SOFTWARE 會被重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node

​ 如果想明確在指定的位置下創建子項,32位應用程序可以這樣實現:

HKEY hKey = NULL;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,T("Software\\Hello"),0,NULL,0,KEY_READ|KEY_WOW64_64KEY,NULL,&hKey,NULL);
if(hKey == NULL)
{
    RegCloseKey(hKey);
    hKey = NULL;
}

​ 32 位程序只要使用 KEY_WOW64_64KEY就可以訪問當前的64位註冊表,同理64位程序只要使用 KEY_WOW64_32KEY 就可以訪問32位註冊表。

6.1.2 PatchGuard 技術

​ 對於32位系統來說,驅動程序模塊可以對內核的數據結構或者關鍵的函數進行掛鉤和修改,但是這種技術不再適用於 Window64 位系統,原因是 64位系統中加入了 PatchGuard 機制。

​ 簡單來說,該機制就是系統會定時檢查系統文件的位置,如SSDT(系統服務描述表)、GDT (全局描述表)、IDT(中斷描述表)、系統模塊等。一旦發現這些關鍵位置的代碼被篡改,系統就會藍屏。

6.1.3 64位驅動的編譯、安裝與運行。

​ 這裏因爲我是使用的64位Win7,在第一部分環境配置中就解決了該問題。

6.2 編程差異

6.2.1 彙編嵌入變化

這裏也在環境配置時候提到了,64位程序不再允許內嵌彙編。

int 3; 替換爲 DbgBreakPoint(); 

6.2.2 預處理與條件編譯

​ 在驅動開發中,有時候代碼需要區分不同的平臺編譯不同的代碼分支。比如需要開發一個註冊表監控驅動,按照以往 32 位系統的常規做法,通過掛鉤系統服務描述表(SSDT)中註冊表相關API而得以實現的,但是64位系統下由於 PatchGuard 機制的存在,需要用其他的方法實現。所以在編譯的時候需要進行分支

#ifdef _WIN64
	// 64位環境
#else
	// 32位環境
#endif

6.2.3 數據結構調整

​ 本章開頭介紹了 thunking 技術,但是當一個32位應用程序通過 IOCTL 控制碼的方式與64位驅動程序通信時,如果通信的數據結構裏包含指針,此時 thunking 是不會發生的。那麼此時會導致 64位期待的數據結構大小大於 32位驅動發送的大小。爲了解決這個問題,在定義數據結構的時候,需要使用固定長度的類型來定義結構體成員。比如使用:

VOID * POINTER_32 Event 來替代 HANDLE * Event
UNICODE_STRING32 ObjectName 來替代 UNICODE_STRING ObjectName

​ 使用固定長度的類型來定義成員變量之後,在不同平臺大小不再有歧義。

明日計劃

繼續學習驅動編程開發篇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章