為了安全起見,Windows XP及其以后的系統將一些重要的內存頁設置為只讀屬性,這樣就算有權力訪問該表也不能隨意對其修改,例如SSDT、IDT等。但這種方法很容易被繞過,我們只要將這些部分修改為可寫屬性就可以了,不過當我們的事情做完后記得把它們恢復為只讀屬性,不然會造成一些很難預料到的后果。
cr0是系統內的控制寄存器之一。控制寄存器是一些特殊的寄存器,它們可以控制CPU的一些重要特性。
控制寄存器最初出現於低級的286處理器中,以前稱之為機器狀態字(machine status word),在386以后它們被重命名為控制寄存器(control register)。
cr0寄存器直到486的處理器版本才被加入了“寫保護”(Write Protect,WP)位,WP位控制是否允許處理器向標記為只讀屬性的內存頁寫入數據。
WP位0:禁用寫保護的功能
WP位1:開啟寫保護的功能
cr0的第16位是WP位,只要將這一位置0就可以禁用寫保護,置1則可將其恢復。
禁用寫保護的操作步驟:
1 shl 16(1左移16位)//結果:10000000000000000
對結果取反 not (1 shl 16)//結果:FFFEFFFF=01111111111111111
對cr0的值進行“邏輯與”運算:and cr0, 01111111111111111 //即將第17位置0,其余位不變
啟用寫保護的操作步驟:
直接對CR0的值進行“邏輯或”運算:or cr0,10000000000000000//即將第17位置1,其余位不變
禁用和啟用寫保護的內聯匯編代碼如下所示:
// 關閉寫保護
__asm
{
cli ;//將處理器標志寄存器的中斷標志位清0,不允許中斷
mov eax, cr0
and eax, ~0x10000
mov cr0, eax
}
// 恢復寫保護
__asm
{
mov eax, cr0
or eax, 0x10000
mov cr0, eax
sti ;//將處理器標志寄存器的中斷標志置1,允許中斷
}
注意:cli和sti都是特權指令,必須在ring0才能使用的。
核心代碼如下:
PJMPCODE pCurAddr;//指向SSDT表中"當前地址"的指針 JMPCODE oleCode;//用來保存前5字節,以便恢復 //驅動程序的入口函數 #pragma INITCODE//將DriverEntry設在分頁內存中,當驅動加載成功,此函數在內存中移除。 extern "C" NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath) { ULONG curAddr,oldAddr; JMPCODE jmpCode; // __asm int 3;//斷點 DbgPrint("驅動加載成功……\n"); curAddr = Get_NTCurAddr(); oldAddr = Get_NTOldAddr(); if (curAddr!=oldAddr) { //保存前5字節 pCurAddr=(PJMPCODE)curAddr;//初始化指針 oleCode.jmpStyle=pCurAddr->jmpStyle;//跳轉方式的機器碼(1字節) oleCode.jmpAddr=pCurAddr->jmpAddr;//跳轉的目的地址機器碼(4字節) jmpCode.jmpStyle = 0xE9;//近跳轉 jmpCode.jmpAddr = oldAddr-curAddr-5; DbgPrint("要寫入的地址:%X",jmpCode.jmpAddr); //寫入JMP指令 //關閉寫保護 _asm { cli ;//將處理器標志寄存器的中斷標志位清0,不允許中斷 mov eax, cr0 and eax, ~0x10000 mov cr0, eax } pCurAddr->jmpStyle=0xE9;//近跳轉 pCurAddr->jmpAddr=jmpCode.jmpAddr;//要跳轉到的地址 // 恢復寫保護 _asm { mov eax, cr0 or eax, 0x10000 mov cr0, eax sti ;//將處理器標志寄存器的中斷標志置1,允許中斷 } DbgPrint("NtOpenProcess被Hook了"); } CreateMyDevice(pDriverObject);//創建設備 pDriverObject->DriverUnload = DDK_UnLoad; return STATUS_SUCCESS; }
//卸載例程 void DDK_UnLoad(IN PDRIVER_OBJECT pDriverObject) { //關閉寫保護 _asm { cli ;//將處理器標志寄存器的中斷標志位清0,不允許中斷 mov eax, cr0 and eax, ~0x10000 mov cr0, eax } pCurAddr->jmpStyle=oleCode.jmpStyle;//近跳轉 pCurAddr->jmpAddr=oleCode.jmpAddr;//要跳轉到的地址 // 恢復寫保護 _asm { mov eax, cr0 or eax,0x10000 mov cr0, eax sti ;//將處理器標志寄存器的中斷標志置1,允許中斷 } DbgPrint("驅動卸載成功……\n"); }