Windows NT/2000 為了確保系統可靠性,嚴格劃分了核心模式與使用者模式,在 x86 架構上分別對應 CPU 的 Ring 0 與 Ring 3 權限等級。Ring 0 允許執行特權指令、存取任意 I/O 裝置等。若要從使用者態(Ring 3)進入核心態(Ring 0),必須透過 CPU 的門機制,如中斷門、呼叫門等。Windows NT/2000 提供的系統服務(int 2eh 中斷)僅允許呼叫系統預定義的服務,若想執行自訂的 Ring 0 代碼,傳統做法是編寫裝置驅動程式。本文介紹一種在使用者態下、不依賴任何驅動程式即可執行 Ring 0 代碼的技術。
Windows NT/2000 將裝置驅動程式載入核心位址空間(通常位於 0x80000000 以上),由 DPL 為 0 的 GDT 項目 8(CS=8)實現 Ring 0 權限。本文的思路是在系統中建立一個指向我們代碼的呼叫門,藉此實現 Ring 0 代碼執行。關鍵在於構造屬於自己的 Call Gate。Call Gate 由全域描述符表(GDT)定義,GDT 基址可透過 sgdt 指令取得(非特權指令,Ring 3 可執行)。GDT 位於核心區域,一般使用者程式無法直接存取該記憶體區域。
幸運的是,Windows NT/2000 提供了一個名為 PhysicalMemory 的區段核心物件,位於 \Device 路徑下。透過這個物件可以操作實體記憶體。使用 objdir.exe 分析其 DACL 可見,預設僅 SYSTEM 帳號擁有讀寫權限,Administrators 僅有讀取權限,一般使用者則無權限。但若擁有 Administrator 權限,即可透過 GetSecurityInfo、SetEntriesInAcl、SetSecurityInfo 等 API 修改該物件的 ACE,賦予當前使用者寫入權限。這就是示例代碼需要 Administrator 權限的原因。
以下函數示範如何為 PhysicalMemory 區段物件新增可寫入的 ACE:
VOID SetPhyscialMemorySectionCanBeWrited(HANDLE hSection)
{
PACL pDacl = NULL;
PACL pNewDacl = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD dwRes;
EXPLICIT_ACCESS ea;
if ((dwRes = GetSecurityInfo(hSection, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, &pDacl, NULL, &pSD)) != ERROR_SUCCESS)
{
printf("GetSecurityInfo Error %u\n", dwRes);
goto CleanUp;
}
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = SECTION_MAP_WRITE;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = "CURRENT_USER";
if ((dwRes = SetEntriesInAcl(1, &ea, pDacl, &pNewDacl)) != ERROR_SUCCESS)
{
printf("SetEntriesInAcl %u\n", dwRes);
goto CleanUp;
}
if ((dwRes = SetSecurityInfo(hSection, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, pNewDacl, NULL)) != ERROR_SUCCESS)
{
printf("SetSecurityInfo %u\n", dwRes);
goto CleanUp;
}
CleanUp:
if (pSD) LocalFree(pSD);
if (pNewDacl) LocalFree(pNewDacl);
}
執行後,PhysicalMemory 物件將新增一條 ACE,授予當前使用者 SECTION_MAP_WRITE 權限,從而允許修改 GDT、植入呼叫門,最終實現無驅動執行 Ring 0 代碼。
此技術雖展示了核心機制的可控性,但修改核心記憶體結構極度危險,稍有不慎即導致系統崩潰。實際開發中應優先考慮正規驅動開發模型,僅在極特殊的研究或除錯場景下參考此法。現代 Windows 版本(含 Patch Guard、HVCI 等防護機制)已大幅限制此類操作,本文僅供歷史技術參考。