内核里使用内存

内存使用,无非就是申请、复制、设置、释放。在 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.内核里使用内存的更多相关文章

  1. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  2. Win64 驱动内核编程-7.内核里操作进程

    在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...

  3. Win64 驱动内核编程-5.内核里操作文件

    内核里操作文件 RING0 操作文件和 RING3 操作文件在流程上没什么大的区别,也是"获得文件句柄->读/写/删/改->关闭文件句柄"的模式.当然了,只能用内核 A ...

  4. Win64 驱动内核编程-6.内核里操作注册表

    内核里操作注册表 RING0 操作注册表和 RING3 的区别也不大,同样是"获得句柄->执行操作->关闭句柄"的模式,同样也只能使用内核 API 不能使用 WIN32 ...

  5. Win64 驱动内核编程-4.内核里操作字符串

    内核里操作字符串 字符串本质上就是一段内存,之所以和内存使用分开讲,是因为内核里的字符串太有花 样了,细数下来竟然有 4 种字符串!这四种字符串,分别是:CHAR*.WCHAR*.ANSI_STRIN ...

  6. WIN64内核编程-的基础知识

    WIN64内核编程基础班(作者:胡文亮)   https://www.dbgpro.com/x64driver 我们先从一份"简历"说起: 姓名:X86或80x86 性别:? 出生 ...

  7. Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)

    驱动安装,通讯,Hello World 开发驱动的简单流程是这样,开发驱动安装程序,开发驱动程序,然后安装程序(或者其他程序)通过通讯给驱动传命令,驱动接到之后进行解析并且执行,然后把执行结果返回. ...

  8. Win64 驱动内核编程-18.SSDT

    SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...

  9. Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)

    SHADOW SSDT HOOK HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似,区别是查找SSSDT的特征码,以及根据索引计算函数地址的公式,还 ...

随机推荐

  1. Flutter Web 支持现已进入稳定版

    作者 / Mariam Hasnany, Product Manager, Flutter 我们对 Flutter 的愿景是成为一个可移植的 UI 框架,在全平台上构建精美的应用体验.做为 Flutt ...

  2. Python开发环境从零搭建-03-安装Python解释器并配置

    想要从零开始搭建一个Python的开发环境说容易也容易 说难也能难倒一片开发人员,在接下来的一系列视频中,会详细的讲解如何一步步搭建python的开发环境 本文章是搭建环境的第3篇 讲解的内容是:安装 ...

  3. Java流程控制:用户交互Scanner

    java.util.Scanner工具类获取用户输入语法:Scanner scanner = new Scanner(System.in);通过Scanner类的next()与nextLine()方法 ...

  4. [NOIP 2020] 微信步数

    一.题目 点此看题 二.题目 首先感谢一下这位大佬的博客,虽然我看不懂您的讲解,但是还是读得懂代码的 思路是 \(\tt jys\) 给我讲明白的,首先我们可以感觉到快速计算它肯定和矩形有关系,也就是 ...

  5. 正则表达式-Python实现

    1.概述: Regular Expression.缩写regex,regexp,R等: 正则表达式是文本处理极为重要的工具.用它可以对字符串按照某种规则进行检索,替换. Shell编程和高级编程语言中 ...

  6. c语言链表从本地文件中读取和写入数据

    1 typedef struct Data{ 2 40 char *name; 3 41 char *IDCARD; 4 42 char *job_id; 5 43 char *length; 6 4 ...

  7. MVC中"删除"按钮无法实现

    出现原因:MVC视图中定义了空的模板页 解决办法:删除模板页 或 改成定义页面标题都可以

  8. 在ASP.NET Core中用HttpClient(四)——提高性能和优化内存

    到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容.但是我们可以通过使用流提高性能和优化内存.因此,在本文中,我们将学习如何在请求和响应中使用HttpClient流. 什么是流 流是以文件. ...

  9. 依赖反转原则DIP 与使用了Repository模式的asp.net core项目结构

    DIP 依赖反转原则 Dependency Inversion Principle 的定义如下: 高级别的模块不应该依赖于低级别的模块, 他们都应该依赖于抽象. 假设Controller依赖于Repo ...

  10. 初识Django(一)

    首先安装Django 1 pip install django==1.11.13 安装 由于django最新的长期支持版本为1.11.x,所以我们安装最新的1.11.13版本 '=='后面跟版本号 安 ...