Win64 驱动内核编程-3.内核里使用内存
內核里使用內存
內存使用,無非就是申請、復制、設置、釋放。在?C?語言里,它們對應的函數是:malloc、memcpy、memset、free;在內核編程里,他們分別對應?ExAllocatePool、RtlMoveMemory、
RtlFillMemory、ExFreePool。它們的原型分別是:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
需要注意的是,RtlFillMemory?和?memset?的原型不同、ExAllocatePool?和?malloc?的原型也不同。前者只是參數前后調換了一下位置,但是后者則多了一個參數:PoolType。這個PoolType?也是必須了解的。PoolType?在?在?MSDN??的介紹上有?N??種,?其實?常用有?的只有?2??種?:
PagedPool?和?和?NonPagedPool。PagedPool??是?分頁內存,簡單來說就是物理內存不夠時,會把這片內存移動?到硬盤上,而?NonPagedPool??是?無論物理內存如何緊缺,都絕對不把?這片內存的內容移動到硬盤上。在往下講之前,先補充一個知識,?就是我們操作的內存,都是虛擬內存,和物理內存是兩碼事。?但虛擬內存?的數據是?放在?物理內存上的,兩者存在映射關系?,?一般來說,一?片?物理內存可以映射為多片虛擬內存?,?但一片虛擬內存必定只對應一片物理內存。假設虛擬內存是?0Xfffff80001234567?在物理內存的地址是?0x123456,當物理內存不夠用時,物理內存?0x123456?的原始內容就挪到硬盤上,然后把另外一片急需要用的內容移到物理內存里。此時,當你再讀取?0Xfffff80001234567?的內容時,就會引發缺頁異常,系統就會把在硬盤上的內容再次放到物理內存中(如果這個過程失敗,一般就死機了)。以上說了這么多廢話,總結兩句:1.NonPagedPool??的?總量?是有限的(?具體?大小和你物理內存的大小相)?關)?,而?而?PagedPool??的?總量較多?。申請了?內存忘記釋放?都會造成?內存泄漏,但是很明顯忘記放?釋放?NonPagedPool??的?后果要嚴重?得多?;2.?一般來說?,PagedPool??用來放?數據?(比如?你用ZwQuerySystemInformation??枚舉片?內核模塊,可以申請一大片?PagedPool??存放)?返回的數據)?,而?而?NonPagedPool??用來放?代碼?(你?寫內核?shellcode??并?需要執行時,?必須?使用?NonPagedPool存放?shellcode)?)?。?以我?的經驗來說,訪問到切換出去的內存沒事,但是?執行到?切換出去的內存必然藍屏?(?這)?只是我的經驗,正確性待定)。3.?在?用戶態?,內存?是有?屬性的,?有的?內存?片只?能?讀??不?能??寫?不?能?執行?(?(?PAGE_READ)?)?,?有的?內?存??片??可?以??讀??可?以?寫?也?可?以?執?行(PAGE_READ_WRITE_EXECUTE)。在內核里,PagedPool?和?和?NonPagedPool??都?是?可讀可寫可執行的?,?而且沒有類似?VirtualProtect??之類的函數。示例代碼:
void Test() { PVOID ptr1 = ExAllocatePool(PagedPool ,0x100); PVOID ptr2 = ExAllocatePool(NonPagedPool ,0x200); RtlFillMemory(ptr2 ,0x200 ,0x90); RtlMoveMemory(ptr1 ,ptr2 ,0x50); ExFreePool(ptr1); ExFreePool(ptr2); DbgPrint("[KrnlHW64]tttttttttt\n");}到這里順便提醒下大家,驅動里面的SEH很多時候都是在自己騙自己,該藍還得藍。我隨便測試了下。
在內核里想要寫入“別人的”內存(一般指?NTOS?等系統模塊的內存空間),還有另外的規矩,這里又涉及到另外兩個概念:IRQL?和內存保護。IRQL?稱為中斷請求級別,從?0~31?共32?個級別;內存保護可以打開和關閉,如果在內存處于保護狀態時寫入,會導致藍屏。?一般來說?,要寫入“?別人?的”?內核內存?,?必須關閉內存寫保護,并把?IRQL??提升到?2??才行?(絕大多數候?時候?IRQL??都為?為?0?,當?當?IRQL=2??時,會阻斷大部分線程執行,?防止?執行出錯)?)。內存是否處于寫保護的狀態記錄在?CR0?寄存器上,因此直接修改?CR0?寄存器的值即可;而提升或降低IRQL?則使用?KeRaiseIrqlToDpcLevel?和?KeLowerIrql?實現(WIN64?的?IRQL?值記錄在?CR8?寄存器上,而?WIN32?的?IRQL?值記錄在?KPCR?上)。代碼如下:
KIRQL WPOFFx64() { KIRQL irql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0); _disable(); return irql; }void WPONx64(KIRQL irql) { UINT64 cr0 = __readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql); }void Test() { KIRQL irql = WPOFFx64(); PVOID HookCode = ExAllocatePool(NonPagedPool, 0x200); RtlFillMemory(HookCode, 0x200, 0x90); RtlMoveMemory(HookCode, NtOpenProcess, 0x3); RtlMoveMemory(NtOpenProcess ,HookCode , 0x3); WPONx64(irql); }注意:如果不調用WPOFFx64,WPONx64直接去寫內存會藍屏,直接往上面的那個位置寫0也會藍屏。
至于寫入“別人的”內存,還有一種微軟推薦的安全方式,就是?MDL?映射內存的方式。
這個比較麻煩,大概方法是?申請一個?MDL?(類似?句柄的?玩意)?)?,然后?嘗試?鎖定頁面?,如果?成功,則?讓?系統分配一個?“?安全?”?的虛擬地址再行?寫入,?,?寫入?完畢?后?解鎖頁面?并釋放掉?MDL。以下是某人寫的?SafeCopyMemory
BOOLEAN SafeCopyMemory(PVOID pDestination, PVOID pSourceAddress, SIZE_T SizeOfCopy) { PMDL pMdl = NULL; PVOID pSafeAddress = NULL; if (!MmIsAddressValid(pDestination) || !MmIsAddressValid(pSourceAddress)) return FALSE; pMdl = IoAllocateMdl(pDestination, (ULONG)SizeOfCopy, FALSE, FALSE, NULL); if (!pMdl) return FALSE; __try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(pMdl); return FALSE; } pSafeAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority); if (!pSafeAddress) return FALSE; __try { RtlMoveMemory(pSafeAddress, pSourceAddress, SizeOfCopy); } __except (EXCEPTION_EXECUTE_HANDLER) {;} MmUnlockPages(pMdl); IoFreeMdl(pMdl); return TRUE; }void Test() { PVOID HookCode = ExAllocatePool(NonPagedPool, 0x200); RtlFillMemory(HookCode, 0x200, 0x90); RtlMoveMemory(HookCode, NtOpenProcess, 0x3); SafeCopyMemory(NtOpenProcess, HookCode, 0x3); }聲明:上面的代碼都是來源于一個網上資料,作者是?胡文亮。感謝這位前輩。之后的內容也是,我在看著這本書學習整理東西,在這里說是要表達我們要最終他人勞動成果。
總結
以上是生活随笔為你收集整理的Win64 驱动内核编程-3.内核里使用内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 9.PHP文件处理
- 下一篇: Win64 驱动内核编程-4.内核里操作