Windows内核实验004 API调用
文章目錄
- 完善代碼
- 內(nèi)核API調(diào)用
- 修復(fù)一個(gè)潛在問題
- 復(fù)現(xiàn)問題
- 完整代碼
前面幾次實(shí)驗(yàn)我們已經(jīng)完成了一個(gè)三環(huán)的程序調(diào)用零環(huán)API的必要條件。
- 提升到零環(huán)權(quán)限
- 使fs指向KPCR
完善代碼
這次我們?nèi)サ糁暗乃姥h(huán)代碼,并且將函數(shù)地址寫入到IDT表項(xiàng),在虛擬機(jī)中運(yùn)行一下程序,看看會有什么結(jié)果。
這里他拋出了一個(gè)內(nèi)存訪問異常。原因在于我們修改了fs寄存器之后,在iretd指令返回三環(huán)的時(shí)候,系統(tǒng)不會自動幫我們將FS寄存器還原。
所以我們在返回三環(huán)之前還需要將fs還原回去。代碼如下:
__asm{push 0x30;pop fs;sti;push 0x3B;pop fs;iretd;}再次運(yùn)行我們的程序,
此時(shí)程序完全正常運(yùn)行。
內(nèi)核API調(diào)用
接下來我們在自己的代碼中調(diào)用一個(gè)內(nèi)核的API函數(shù)ExAllocatePool來分配一塊內(nèi)存。
首先在PC Hunter中將ntkrnlpa.exe拷貝出來,然后用IDA分析,接著設(shè)置基址和驅(qū)動模塊的基地址一致。
在IDA中找到ExAllocatePool這個(gè)函數(shù),并且記錄下函數(shù)地址。
然后按Y鍵可以復(fù)制出函數(shù)原型,然后定義函數(shù)指針,并且將函數(shù)地址賦值給函數(shù)指針變量
typedef DWORD (__stdcall *EX_ALLOCATE)(DWORD PoolType, DWORD NumberOfBytes); EX_ALLOCATE ExAllocatePool= (EX_ALLOCATE)0x83E51976;接著編寫調(diào)用代碼如下:
void __declspec(naked) IdtEntry() {__asm{push 0x30;pop fs;sti;}g_pool=ExAllocatePool(0, 4096);__asm{push 0x3B;pop fs;iretd;} }運(yùn)行程序
看到這里將我們申請的內(nèi)存首地址打印出來了,而且是一個(gè)內(nèi)核的地址,符合我們的預(yù)期
然后我們可以嘗試調(diào)用一下DbgPrint,同樣的方法找到函數(shù)地址和原型進(jìn)行調(diào)用
typedef DWORD ( __cdecl *DBGPRINT)(char* Format, ...); DBGPRINT MyDbgPrint=(DBGPRINT)0x83E5541F; char str[] = "Hello GuiShou!";void __declspec(naked) IdtEntry() {__asm{push 0x30;pop fs;sti;}//g_pool=ExAllocatePool(0, 4096);MyDbgPrint(str);__asm{push 0x3B;pop fs;iretd;} }運(yùn)行程序以后
在windbg窗口打印出了我們設(shè)置好的字符串,說明API調(diào)用成功
修復(fù)一個(gè)潛在問題
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-bD6wu6Cl-1573908064431)(assets/1573905826474.png)]
仔細(xì)觀察我們寫的這幾句還原FS段選擇子返回三環(huán)的代碼,實(shí)際上是有問題的。
在pop fs這句代碼執(zhí)行完成之后,iretd執(zhí)行之前;依然是開啟中斷的狀態(tài),那么意味著這兩行匯編指令之間CPU有可能收到時(shí)鐘中斷,造成線程切換。
而線程切換需要用的FS寄存器的值,然而FS這個(gè)時(shí)候是一個(gè)三環(huán)的段選擇子,而我們現(xiàn)在卻處在一個(gè)零環(huán)的環(huán)境下,FS需要指向KPCR,但是卻沒有指向那個(gè)位置。
就是說如果在指向這兩句代碼之間發(fā)生了線程切換,就會發(fā)生藍(lán)屏。盡管產(chǎn)生這個(gè)問題的幾率很小,但是依然不能忽視。
復(fù)現(xiàn)問題
接下來我們稍微修改一下代碼,就能復(fù)現(xiàn)這個(gè)問題:
void go() {while(1)__asm int 0x20; }我們在int 0x20指令加上一條死循環(huán)。當(dāng)CPU產(chǎn)生int 20異常時(shí),會進(jìn)入到我們設(shè)置好的IdtEntry函數(shù)提權(quán)到零環(huán),然后IdtEntry函數(shù)會重新返回到三環(huán)。接著由于int 0x20這條指令是在死循環(huán)里面。所以這個(gè)程序就會一直在三環(huán)和零環(huán)之前往返。這就會大大增加在push 0x2B;pop fs這兩句代碼之間發(fā)生線程切換的幾率。
運(yùn)行程序,在不連接調(diào)試器的情況下會直接藍(lán)屏,如果連接上調(diào)試器則虛擬機(jī)會出現(xiàn)卡死現(xiàn)象。
解決的方法很簡單,就是讓CPU在執(zhí)行這兩句代碼時(shí)不接收硬件中斷
在恢復(fù)FS寄存器之前關(guān)閉中斷,即可解決問題,事實(shí)上KiFastCallEntry也是這么做的
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-CHN5izUD-1573908064445)(assets/1573907354519.png)]
再次運(yùn)行程序,此時(shí)依然是死循環(huán),但是因?yàn)橐呀?jīng)關(guān)閉了中斷,所以循環(huán)往返三環(huán)和零環(huán)都是安全的,不會發(fā)生藍(lán)屏和卡死的現(xiàn)象。
完整代碼
最后附上完整的實(shí)驗(yàn)代碼
#include "pch.h" #include <iostream> #include <stdio.h> #include <stdlib.h> #include <windows.h>typedef DWORD (__stdcall *EX_ALLOCATE)(DWORD PoolType, DWORD NumberOfBytes); EX_ALLOCATE ExAllocatePool= (EX_ALLOCATE)0x83E51976; DWORD g_pool;typedef DWORD ( __cdecl *DBGPRINT)(char* Format, ...); DBGPRINT MyDbgPrint=(DBGPRINT)0x83E5541F; char str[] = "Hello GuiShou!";void __declspec(naked) IdtEntry() {__asm{push 0x30;pop fs;sti;}//g_pool=ExAllocatePool(0, 4096);//MyDbgPrint(str);__asm cli;__asm{push 0x3B;pop fs;iretd;} } void go() {while(1)__asm int 0x20; }//eq 80b95500 0040ee00`00081040 int main() {if ((DWORD)IdtEntry != 0x401040){printf("wrong addr:%p", IdtEntry);exit(-1);}go();//printf("%p\n", g_pool);system("pause"); } 《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Windows内核实验004 API调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows内核实验003 再次回到中
- 下一篇: Windows内核实验005 Inlin