天行健 发表于 6 天前

Win64驱动开发教程05-内核里使用内存



程序的本质,就是内存里的一串串的数字(它们被 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 的原型不同、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);
}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=WPOFF();
RtlMoveMemory(NtOpenProcess,HookCode,15);
WPON(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;
}

页: [1]
查看完整版本: Win64驱动开发教程05-内核里使用内存