内核中使用内存
c语言小知识:
https://zhuanlan.zhihu.com/p/87975119
https://zhuanlan.zhihu.com/p/86707793
https://zhuanlan.zhihu.com/p/55768830
https://zhuanlan.zhihu.com/p/55768545
https://zhuanlan.zhihu.com/p/38935280
程序的本质,就是内存里的一串串的数字,它们被 CPU 当作指令解析,才能够有意义
内存使用,无非就是申请、复制、设置、释放。在 C 语言里,它们对应的函数是:malloc、 memcpy、memset、free,在内核编程里,他们分别对应 ExAllocatePool、RtlMoveMemory、 RtlFillMemory、ExFreePool
PVOID ExAllocatePool(POOL_TYPE PoolType, SIZE_T NumberOfBytes); |
VOID RtlMoveMemory(PVOID Destination, PVOID Source, SIZE_T Length); |
VOID RtlFillMemory(PVOID Destination, SIZE_T Length, UCHAR Fill); |
VOID ExFreePool(PVOID P); |
RtlFillMemory 和 memset : 函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度 的原型不同、ExAllocatePool 和 malloc 的原型 : void *malloc(unsigned int size)也不同
ExAllocationPool多了一个参数,PoolType常用的只有 2 种:
PagedPool 和 NonPagedPool PagedPool 是分页内存,简单来说就是物理内存不够时,会把 这片内存移动到硬盘上,而 NonPagedPool 是无论物理内存如何紧缺,都绝对不把这片内存的内容移动到硬盘上
操作的内存,都是虚拟内存,和物理内存是两码事。但虚拟内存的数据是放在物理内存上的,两者存在映射关系,一 般来说,一片物理内存可以映射为多片虚拟内存,但一片虚拟内存必定只对应一片物理内存
假设虚拟内存是 0Xfffff80001234567 在物理内存的地址是 0x123456,当物理内存不够用 时,物理内存 0x123456 的原始内容就挪到硬盘上,然后把另外一片急需要用的内容移到物 理内存里。此时,当你再读取 0Xfffff80001234567 的内容时,就会引发缺页异常,系统就会 把在硬盘上的内容再次放到物理内存中(如果这个过程失败,一般就死机了)
一般来说,PagedPool 用来放数据(比如你用 ZwQuerySystemInformation 枚举内核模块,可以申请一大片 PagedPool 存放返回的数据), 而 NonPagedPool 用来放代码(你写内核 shellcode 并需要执行时,必须使用 NonPagedPool 存放 shellcode)。以我的经验来说,访问到切换出去的内存没事,但是执行到切换出去的内 存必然蓝屏
在用户态,内存是有属性的,有的内存片 只能读不能写 不 能 执行 ( PAGE_READ), 有的 内 存 片 可以读 可以写也可以执行 (PAGE_READ_WRITE_EXECUTE)。在内核里, PagedPool 和 NonPagedPool 都是可读可写可执 行的,而且没有类似 VirtualProtect 之类的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #include <ntddk.h> #include <intrin.h >
void test() { PVOID ptr1 = ExAllocatePool(PagedPool, 0x100); //申请内存 相当于malloc PVOID ptr2 = ExAllocatePool(NonPagedPool, 0x200); RtlFillMemory(ptr2, 0x200, 0x90); //设置内存,相当于memset RtlMoveMemory(ptr1, ptr2, 0x50); ExFreePool(ptr1); //释放内存,相当于free ExFreePool(ptr2); } //以下两个函数,在HOOK修改内存时使用 KIRQL WPOFFx64() { KIRQL irql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0);//将值Data写入 CR0 寄存器 _disable();//禁用中断 return irql; }
void WPONx64(KIRQL irql) { UINT64 cr0 = __readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql);//恢复当前的处理器到其原始值的IRQL }
//此函数有点吹毛求疵,不推荐使用,因为效率太低了。 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 DriverUnload(IN PDRIVER_OBJECT DriverObject) { DbgPrint("[Memory]Unload...\n"); return; }
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { DriverObject->DriverUnload = DriverUnload; DbgPrint("[Memory]Load...\n"); test(); return STATUS_SUCCESS; }
|
测试结果: