zoukankan      html  css  js  c++  java
  • Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存

    内存使用,无非就是申请、复制、设置、释放。在 语言里,它们对应的函数是:mallocmemcpymemsetfree;在内核编程里,他们分别对应 ExAllocatePoolRtlMoveMemory

    RtlFillMemoryExFreePool。它们的原型分别是:

                                  

    需要注意的是,RtlFillMemory 和 memset 的原型不同、ExAllocatePool 和 malloc 的原型也不同。前者只是参数前后调换了一下位置,但是后者则多了一个参数:PoolType。这个PoolType 也是必须了解的。PoolType 在 在 MSDN  的介绍上有 N  种, 其实 常用有 的只有 2  种 :

    PagedPool 和 和 NonPagedPoolPagedPool  是 分页内存,简单来说就是物理内存不够时,会把这片内存移动 到硬盘上,而 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
    ");}

    到这里顺便提醒下大家,驱动里面的SEH很多时候都是在自己骗自己,该蓝还得蓝。我随便测试了下。



    在内核里想要写入“别人的”内存(一般指 NTOS 等系统模块的内存空间),还有另外的规矩,这里又涉及到另外两个概念:IRQL 和内存保护。IRQL 称为中断请求级别,从 031 32 个级别;内存保护可以打开和关闭,如果在内存处于保护状态时写入,会导致蓝屏。 一般来说 ,要写入“ 别人 的” 内核内存 , 必须关闭内存写保护,并把 IRQL  提升到 2  才行 (绝大多数候 时候 IRQL  都为 为 ,当 当 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);
    }

    声明:上面的代码都是来源于一个网上资料,作者是 胡文亮。感谢这位前辈。之后的内容也是,我在看着这本书学习整理东西,在这里说是要表达我们要最终他人劳动成果。
  • 相关阅读:
    AKKA学习(二) 未完
    AKKA学习(一)
    seata项目结构
    seata demo
    FESCAR
    GTS原理、架构
    Fescar使用(资料)
    高性能异步分布式事务TCC框架(资料汇总)
    TIDB学习资料
    自旋锁
  • 原文地址:https://www.cnblogs.com/csnd/p/12062044.html
Copyright © 2011-2022 走看看