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);
    }

    声明:上面的代码都是来源于一个网上资料,作者是 胡文亮。感谢这位前辈。之后的内容也是,我在看着这本书学习整理东西,在这里说是要表达我们要最终他人劳动成果。
  • 相关阅读:
    ExtJS小技巧
    Oracle 表的行数、表占用空间大小,列的非空行数、列占用空间大小 查询
    NPM 私服
    IDEA 不编译java以外的文件
    SQL 引号中的问号在PrepareStatement 中不被看作是占位符
    Chrome 浏览器自动填表呈现淡黄色解决
    批量删除Maven 仓库未下载成功.lastupdate 的文件
    Oracle 11g 监听很慢,由于监听日志文件太大引起的问题(Windows 下)
    Hibernate 自动更新表出错 建表或添加列,提示标识符无效
    Hibernate 自动更新表出错 More than one table found in namespace
  • 原文地址:https://www.cnblogs.com/csnd/p/12062045.html
Copyright © 2011-2022 走看看