Windows內核開發-9-32位和64位的區別


Windows內核開發-9-32位和64位的區別

32位的應用程序可以完美再64位的電腦上運行,而32位的內核驅動無法再64位的電腦上運行,或者64位的驅動無法在32位的應用程序上運行。這是為什么呢。

原因是在x64的Windows操作系統上,模擬了x86操作系統的操作,並且引入了一個WOW64子系統,將x86和x64完美進行兼容。

WOW64子系統

x86能在x64上運行全靠這個東西。全名叫做Windows On Windows,英文名感覺是在套娃,其實它的意思就是在Windows64上運行Windows32。

這個系統由Wow64.dll,Wow64Win.dll,Wow64Cpu.dll三個dll實現,具體怎么實現的不用考慮。

Wow64子系統可以完美實現x86和x64之間的轉換。

轉換流程: 當一個32位Application發起系統調用時,WOW64會攔截下來,將其轉換為64位的類型(包括指針范圍,數據類型范圍等等),然后再把系統調用請求提交給內核。這整個攔截-轉換的流程被稱為"thunking"。

WOW64有兩個重要的模塊,一個是系統文件重定向(File System Redirector),一個是注冊表重定向(Registry Redirector)。

系統文件重定向(File System Redirector)

Windows64位OS包含了兩個System32文件,一個是System32另一個是SysWow64。默認情況下的安裝路徑%Windows%\System32和%Windows%\SysWow64。

System32這個文件里面保存了系統需要的一些二進制文件,System32里面存放的是x64的系統二進制文件,SysWow64里面存放的是x86里的文件。不要被這個什么system32迷惑成了它就是32位的系統文件了。

 

一般情況下32位的只能加載32位的系統dll,64只能加載64的。因為是64位的操作系統,所以肯定默認是加載64的dll,但是32位怎么辦,為了解決這個問題WOW64就構成了文件系統重定向模塊,把32的系統dll放到了SysWow64里面,然后把System32這個文件夾給他重定向指到了SysWow64文件夾里了。

這里我們寫一個代碼來驗證一下:

void TestFileRedirector()
{
    HANDLE hFile = CreateFileA("C:\\Windows\\System32\\test.txt", GENERIC_READ | GENERIC_WRITE, 0x00000000, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        cout << "創建文件失敗" << endl;
    }
    else
    {
        cout << "創建文件成功,文件名為test.txt" << endl;
    }
    CloseHandle(hFile);
}

 

假如說這個test文件是在SysWow64文件夾下面創建的,那么說明我們前面的講述沒問題,確實是重定向到了SysWow64里面。這里我們要用管理員啟動Visual Studio才行,因為這個文件夾是系統文件夾,需要管理員權限。

下面是我的驗證結果:

 

 

在x86和x64運行后分別是在System32和SysWow64新建了文件,足以說明結論了。

關閉系統文件重定向

文件重定向固然不錯,但是肯定有時候我們會不得不關閉它。

這里就會用到兩個API:

BOOL Wow64DisableWow64FsRedirection(
  [out] PVOID *OldValue
);//關閉重定向,將原來的值保存到輸入參數oldvalue里面
BOOL Wow64RevertWow64FsRedirection(
  PVOID OlValue
);//通過參數olvalue來恢復重定向

 

這里我們在修改一下我們的代碼,讓他x86的程序不要重定向到x64文件里面:

void TestFileRedirector()
{
    PVOID OldValue;
    auto ret = Wow64DisableWow64FsRedirection(&OldValue);
    if (ret == 0)
    {
        cout << "調用關閉重定向的函數失敗,請檢查" << endl;
        return;
    }
    else
    {
        cout << "調用重定向的函數成功,已經取消文件系統重定向" << endl;
    }
​
    HANDLE hFile = CreateFileA("C:\\Windows\\System32\\test.txt", GENERIC_READ | GENERIC_WRITE, 0x00000000, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        cout << "創建文件失敗" << endl;
        return;
    }
    else
    {
        cout << "創建文件成功,文件名為test.txt" << endl;
    }
    CloseHandle(hFile);
    Wow64RevertWow64FsRedirection(OldValue);
}

 

這樣再創建就是到System32系統文件夾里面了。

 

 

有一部分文件是不會被重定向的:

%Windows%System32\catrrot
%Windows%System32\catrrot2
%Windows%System32\drivers\etc
%Windows%System32\logfiles
%Windows%System32\spool

 

注冊表重定向(Registry Redirector)

和系統文件重定向(File System Redirecotr)比較類似,但是功能更為復雜。除了重定向還有注冊表反射功能(Registry Reflection 該功能暫時用不到)。

和File System Redirector比較類似的是,win32訪問HKEY_LOCAL_MACHINE\SOFTWARE會被重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下面。同樣寫一下代碼測試一下:

void TestRegRedirector()
{
    HKEY hKey = NULL;
    RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Test"),
        0, NULL, 0, KEY_READ, NULL, &hKey,NULL);
    if (hKey != NULL)
    {
        cout << "創建注冊表成功" <<endl;
        RegCloseKey(hKey);
    }
    else
    {
        cout << "創建注冊表失敗" << endl;
        return;
    }
}

 

用32位來運行,看他添加到哪里:

 

 

當然也肯定有關閉的辦法。

DIY注冊表重定向

在創建注冊表的API上加一個宏定義就可完美解決這個問題了:

    RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Test"),0, NULL, 0, KEY_READ | KEY_WOW64_32KEY, NULL, &hKey,NULL);

 


這里的KEY_WOW64_32KEY就是32位,KEY_WOW64_64KEY就可以鎖定為64位了。
void TestRegRedirector()
{
    HKEY hKey = NULL;
    RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Test"),
        0, NULL, 0, KEY_READ | KEY_WOW64_32KEY, NULL, &hKey,NULL);
    if (hKey != NULL)
    {
        cout << "創建注冊表成功" <<endl;
        RegCloseKey(hKey);
    }
    else
    {
        cout << "創建注冊表失敗" << endl;
        return;
    }
}

 

64位系統的升級技術

64對比32提供了很多新技術,比如之前的32位被很多程序很多公司,進行掛鈎啊各種功能導致很不安全很麻煩。

PatchGuard

所以升級了一個PatchGuard技術,這個機制就是系統會定期檢查內部的關鍵位置是否被篡改,一旦被篡改就會藍屏。

比如一些論壇常見的SSDT(系統描述表),GDT(全局描述表),IDT(中斷描述表)等等。但是其實也是可以繞過的。正所謂道高一尺魔高一丈就是這個意思,沒有絕對的安全。

x64的編譯、安裝、運行

編譯很簡單,vs換成x64就行了。

安裝的話著需要考慮32位exe安裝驅動的時候不會把他放到64位驅動system32這個文件夾下就行了,這個用關閉File System redirecotr就行。

運行:x64的驅動必須得有簽名才行,變相提高了安全吧,不過我們自己測試就把測試機變成測試模式就好了。

編程差異

x86和x64編程還是有少許區別的。

加入匯編

32位和64比較麻煩的就是不能直接內聯匯編了,就比如說下面這段代碼:

#include<ntddk.h>
​
​
extern "C"
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    __asm
    {
        int 3
    }
​
    return STATUS_SUCCESS;
}

 

一個很簡單的內核驅動代碼,用x64編譯就不行,而x86沒問題:

 

 

也就是說不運行直接內聯匯編了,只能用匯編asm寫好了,然后作為函數的形式放進去了。

條件編譯

WDK設置了宏來幫助條件編譯,比如針對操作系統平台有: _M_AMD64, _M_IX64等等,對於位數也有 _WIN64和 _WIN32 ,比如說:

#ifdef _WIN32   //32位情況
#else //不是32位情況#endif

 

調整數據結構

當一個32位的exe通過DeviceIoControl的方式和64位驅動進行交互的時候,如果結構體里有指針是不會進行thunking技術調整的,所以這里就會涉及到一些問題了,比如指針位數的不兼容,以及比如int這種位數也是不兼容的,還有對齊的一些很多問題。所以最好是采用限制好了的數據結構體。

比如說結構體定義成這樣

struct DRIVER_DATA
{
    void* POINTER_32    test;
    UNICODE_STRING32    testUnicode_string;
};

 

直接把長度一次到位限制了。

小結:

x64和x86的區別還是蠻多的,如果要從頭到尾設計一個驅動的話是必須得思考這個問題的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM