轉自天極
一、引言
80x86系列CPU具有四級保護機制。在Windows 9X作業系統只使用0級和3級,以便於移植到精簡指令集的電腦上,如RS4000等,這些處理器一般只有兩個特權級,即系統級和使用者級。在Windows 9X系統內容,應用程式運行在Ring3(3級),如果要運行特權指令就必須進入Ring0(0級)。在同一任務內,實現特權級從外層到內層變換的普通途徑是使用段間調用指令CALL,通過調用門進行轉移;實現特權級從內層向外層變換的普通途徑是使用段間返回指令RET。注意,不能用JMP指令實現任務內不同特權級的變換。調用門描述符轉移的進入點包含目標地址的段及位移量的48位全指標。在執行通過任務門的段間轉移指令JMP或段間調用指令CALL時,指令所含指標內的選擇子用於確定調用門,而位移被丟棄;把調用門內的48位全指標作為目標地址指標進行轉移。
二、基本思路
取得通用描述元表,搜尋該表找到一個暫時為空白的描述符,安裝調用門,進行遠程調用即可實現特權指令操作。
三、所用到的資料結構
①全域描述符GDT的格式:
在VC中定義全域描述符如下:
struct GDT_DESCRIPTOR{ WORD LimitL;//段界限低16位 WORD BaseL;//基地址低16位 BYTE BaseM;//基地址中間8位 BYTE AttriB;//段屬性 BYTE LimitH;//段界限的高4位(包括段屬性的高4位) BYTE BaseH;//基地址的高8位 } |
②門描述符的一般格式:
當TYPE的低4位值為0xC時,這是一個386調用門(CallGate)。在VC中定義"門"如下:struct GATE{ //門結構類型定義
WORD OffsetL; // 32位位移的低16位 WORD Selector; //選擇子 BYTE Dcount; //雙字計數欄位 BYTE Gtype; //當低4位值為0xC是,這是一個調用門 WORD LimitH; //32位位移的高16位 } |
③通用描述元表寄存器GDTR
GDTR長48位,其中高32位為基地址,低16位為界限,GDTR中的段界限以位元組為單位。在VC中定義如下:
struct GDTR{ WORD wGDTLimit; DWORD dwGDTBase; }; |
四、具體實現
使用MFC AppWizard建立一個基於對話方塊的應用程式,工程名為MyRing0。在對話方塊中添加一個按鈕,修改ID為ID_GETCR0,修改Caption為取CR0的值,同時添加該按鈕的訊息處理函數void CMyRing0Dlg::OnGetcr0()。建立一個標頭檔Ring0.h並添加到工程中,添加下面的代碼到Ring0.h中。
#pragma pack(1) struct GDT_DESCRIPTOR{ WORD LimitL; //段界限低16位 WORD BaseL; //基地址低16位 BYTE BaseM; //基地址中間8位 BYTE AttriB; //段屬性 BYTE LimitH; //段界限的高4位(包括段屬性的高4位) BYTE BaseH; //基地址的高8位 }; struct GATE_DESCRIPTOR{ WORD OffsetL; // 32位位移的低16位 WORD Selector; //選擇子 BYTE Dcount; //雙字計數欄位 BYTE Gtype; //當低4位值為0xC是,這是一個調用門 WORD LimitH; //32位位移的高16位 };struct GDTR{ WORD wGDTLimit; DWORD dwGDTBase; }; #pragma pack() |
在MyRing0Dlg.cpp中最後一個include語句後添加#include "Ring0.h",在按鈕的訊息處理函數void CMyRing0Dlg::OnGetcr0()前面添加下面兩個函數:
__declspec(naked) void GetCR0_Ring0() { _asm{ mov ebx,cr0 //特權指令 mov [EAX],ebx retf } }bool CallRing0(PVOID pvRing0FuncAddr,PWORD pVal) { struct GDT_DESCRIPTOR *pGDTDescriptor; struct GDTR gdtr; WORD CallgateAddr[3]; WORD wGDTIndex = 1; //取得通用描述元表GDT _asm Sgdt [gdtr] // 空選擇子有特殊用途,跳過它,從第二個選擇子開始搜尋 pGDTDescriptor = (struct GDT_DESCRIPTOR *)(gdtr.dwGDTBase + 8); for (wGDTIndex = 1; wGDTIndex < (gdtr.wGDTLimit / 8); wGDTIndex++) { if (pGDTDescriptor->AttriB == 0) { struct GATE_DESCRIPTOR *pGate; pGate = (struct GATE_DESCRIPTOR *) pGDTDescriptor; pGate->OffsetL = LOWORD(pvRing0FuncAddr); //選擇子0x28總是指向Ring0的程式碼片段 pGate->Selector = 0x28; pGate->Dcount = 0; pGate->Gtype = 0xEC; pGate->OffsetH = HIWORD(pvRing0FuncAddr); //準備遠程調用參數 CallgateAddr[0] = 0x0; CallgateAddr[1] = 0x0; CallgateAddr[2] = (wGDTIndex << 3) | 3; //進入Ring0 _asm Mov EAX,[pVal] _asm call FWORD PTR [CallgateAddr] //歸還剛才使用的全域描述符 memset(pGDTDescriptor, 0, 8); return true; } //下一個全域描述符 pGDTDescriptor++; } //沒有閒置全域描述符 return false; } |
為按鈕的訊息處理函數void CMyRing0Dlg::OnGetcr0()添加代碼如下:
void CMyRing0Dlg::OnGetcr0() { // TODO: Add your control notification handler code here WORD Val=0; char str[16]; if(!CallRing0((PVOID)GetCR0_Ring0,&Val)){ AfxMessageBox("系統資源不夠!"); return; }; sprintf(str,"CR0=%3d",Val); AfxMessageBox(str); } |
程式在VC6下編譯通過,作業系統為Windows98。