3.3 Windows驱动开发:内核MDL读写进程内存

MDL内存读写是一种通过创建MDL结构体来实现跨进程内存读写的方式。在Windows操作系统中,每个进程都有自己独立的虚拟地址空间,不同进程之间的内存空间是隔离的。因此,要在一个进程中读取或写入另一个进程的内存数据,需要先将目标进程的物理内存映射到当前进程的虚拟地址空间中,然后才能进行内存读写操作。

MDL结构体是Windows内核中专门用于描述物理内存的数据结构,它包含了一系列的数据元素,包括物理地址、长度、内存映射的虚拟地址等信息。通过创建MDL结构体并调用系统函数将其映射到当前进程的虚拟地址空间中,即可实现跨进程内存读写的操作。

相比于CR3切换方式,MDL内存读写更加稳定、安全,且不会受到寄存器的影响。同时,使用MDL内存读写方式还可以充分利用Windows操作系统的内存管理机制,从而实现更为高效的内存读写操作。因此,MDL内存读写是Windows操作系统中最为常用和推荐的一种跨进程内存读写方式。

3.1.1 MDL读取内存步骤
  • 1.调用PsLookupProcessByProcessId得到进程Process结构,这个函数是用于根据进程ID查找对应的进程对象的函数,通过传入的参数 data->pid 获取到对应的进程ID,然后通过调用 PsLookupProcessByProcessId 函数获取对应的 PEPROCESS 结构。如果获取失败则返回 FALSE。

  • 2.调用KeStackAttachProcess附加到对端进程内,在内核模式下,读取其他进程的内存时需要先附加到对应进程的上下文中,才能读取该进程的内存。因此,这里调用 KeStackAttachProcess 函数将当前线程切换到目标进程的上下文中。同时,为了在后面可以正确地从目标进程的上下文中返回,还需要保存当前进程的上下文状态。

  • 3.调用ProbeForRead检查内存是否可读写,在内核模式下,需要保证访问其他进程的内存是合法的,因此需要先调用 ProbeForRead 函数检查读取的内存空间是否可读写。如果该空间不可读写,则会触发异常,这里通过异常处理机制来处理这种情况。

  • 4.拷贝内存空间中的数据到自己的缓冲区内,在完成对内存空间的检查后,使用 RtlCopyMemory 函数将目标进程的内存数据拷贝到自己的缓冲区中。这里需要注意的是,由于内存空间可能很大,因此可能需要多次进行拷贝操作。

  • 5.调用KeUnstackDetachProcess接触绑定,在读取完内存数据后,需要将当前线程从目标进程的上下文中解除绑定,以便返回到原来的上下文中。这里调用 KeUnstackDetachProcess 函数完成解绑操作,同时恢复之前保存的当前进程的上下文状态。

  • 6.调用ObDereferenceObject使对象引用数减1,由于在第一步中调用了 PsLookupProcessByProcessId 函数获取了对应进程的 PEPROCESS 结构,因此需要调用 ObDereferenceObject 函数将其引用计数减1,以便释放对该对象的引用。

有了上述具体实现方法,那么我们就可以封装MDLReadMemory()内存读函数了,代码如下,该函数用于在 Windows 内核模式下读取指定进程的内存数据。下面是对这个函数的详细步骤分析:

  • 1.通过进程 ID 找到对应的进程对象:PsLookupProcessByProcessId 用于通过进程 ID 查找对应的进程对象。如果找不到该进程对象,则直接返回 FALSE。
PsLookupProcessByProcessId(data->pid, &process);
  • 2.在内核模式下,必须使用内核提供的函数来分配内存。这里使用的是 ExAllocatePool 函数,用于在内核堆中分配指定大小的内存缓冲区。如果分配失败,则返回 FALSE。
BYTE* GetData;
__try
{GetData = ExAllocatePool(PagedPool, data->size);
}
__except (1)
{return FALSE;
}
  • 3.在内核模式下,访问其他进程的内存必须先将当前进程的上下文切换到目标进程的上下文。这里使用的是 KeStackAttachProcess 函数,将当前进程的上下文切换到目标进程的上下文。同时,为了在后面可以正确地从目标进程的上下文中返回,还需要保存当前进程的上下文状态。
KAPC_STATE stack = { 0 };
KeStackAttachProcess(process, &stack);
  • 4.读取目标进程的内存数据,这段代码使用 ProbeForRead 函数检查要读取的内存区域是否合法,并且将目标进程的内存数据读取到之前分配的内存缓冲区中。如果读取过程中出现异常,则返回 FALSE。
__try
{ProbeForRead(data->address, data->size, 1);RtlCopyMemory(GetData, data->address, data->size);
}
__except (1)
{bRet = FALSE;
}
  • 5.恢复当前进程的上下文,这里使用的是 ObDereferenceObject 函数和 KeUnstackDetachProcess 函数,用于恢复之前保存的当前进程的上下文状态,同时解除对目标进程的引用计数。
ObDereferenceObject(process);
KeUnstackDetachProcess(&stack);
  • 6.将读取的数据拷贝到输出参数中,将读取到的数据拷贝到输出参数中,并释放之前分配的内存缓冲区。
RtlCopyMemory(data->data, GetData, data->size);

将如上代码片段整合起来即可得到一个完整的内存读数据案例,读者可传入一个结构体实现对特定进程特定内存的动态读取功能,完整代码如下所示;

#include <ntifs.h>
#include <windef.h>typedef struct
{DWORD pid;                // 要读写的进程IDDWORD64 address;          // 要读写的地址DWORD size;               // 读写长度BYTE* data;               // 要读写的数据
}ReadMemoryStruct;// MDL读内存
BOOL MDLReadMemory(ReadMemoryStruct* data)
{BOOL bRet = TRUE;PEPROCESS process = NULL;PsLookupProcessByProcessId(data->pid, &process);if (process == NULL){return FALSE;}BYTE* GetData;__try{GetData = ExAllocatePool(PagedPool, data->size);}__except (1){return FALSE;}KAPC_STATE stack = { 0 };KeStackAttachProcess(process, &stack);__try{ProbeForRead(data->address, data->size, 1);RtlCopyMemory(GetData, data->address, data->size);}__except (1){bRet = FALSE;}ObDereferenceObject(process);KeUnstackDetachProcess(&stack);RtlCopyMemory(data->data, GetData, data->size);ExFreePool(GetData);return bRet;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint(("hello lyshark \n"));ReadMemoryStruct ptr;ptr.pid = 6672;ptr.address = 0x402c00;ptr.size = 100;// 分配空间接收数据ptr.data = ExAllocatePool(PagedPool, ptr.size);// 读内存MDLReadMemory(&ptr);// 输出数据for (size_t i = 0; i < 100; i++){DbgPrint("%x \n", ptr.data[i]);}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

读取内存地址0x402c00效果如下所示:

3.1.2 MDL写入内存步骤
  • 1.首先需要通过调用PsLookupProcessByProcessId函数获取目标进程的进程结构,该函数将根据传递的进程ID返回对应进程的PEPROCESS结构体,该结构体中包含了进程的各种信息。

  • 2.接下来使用KeStackAttachProcess函数附加到目标进程的上下文环境中,以便可以读取和写入该进程的内存空间。该函数将当前线程的上下文环境切换到目标进程的上下文环境中,使得该线程可以访问和修改目标进程的内存。

  • 3.在进行内存写入操作之前,需要调用ProbeForRead函数来检查要写入的内存空间是否可读写。这个步骤是为了确保要写入的内存空间没有被保护或被其他进程占用,以避免对系统造成不良影响。

  • 4.如果检查通过,接下来需要将目标进程的内存空间中的数据拷贝到当前进程的缓冲区中,以便进行修改操作。

  • 5.接下来需要调用MmMapLockedPages函数来锁定当前内存页面,以便可以对其进行修改。该函数将返回一个指向系统虚拟地址的指针,该地址是由系统自动分配的。在写入完成后,需要使用MmUnmapLockedPages函数来释放锁定的内存页面。

  • 6.然后,使用RtlCopyMemory函数完成内存拷贝操作,将缓冲区中的数据写入到锁定的内存页面中。

  • 7.写入操作完成后,需要调用IoFreeMdl函数来释放MDL锁。MDL锁用于锁定MDL描述的内存页面,以便可以对其进行操作。

  • 8.最后使用KeUnstackDetachProcess函数解除当前进程与目标进程之间的绑定,使得当前线程的上下文环境恢复到原始的状态。

此外在完成MDL写入内存操作后,还需要调用ObDereferenceObject函数将MDL对象的引用计数减1,以便在不再需要该对象时释放它所占用的系统资源。

从如上分析来看写入时与读取基本类似,只是多了锁定页面和解锁操作,这段MDL写内存完整实现代码如下所示;

#include <ntifs.h>
#include <windef.h>typedef struct
{DWORD pid;                // 要读写的进程IDDWORD64 address;          // 要读写的地址DWORD size;               // 读写长度BYTE* data;               // 要读写的数据
}WriteMemoryStruct;// MDL写内存
BOOL MDLWriteMemory(WriteMemoryStruct* data)
{BOOL bRet = TRUE;PEPROCESS process = NULL;PsLookupProcessByProcessId(data->pid, &process);if (process == NULL){return FALSE;}BYTE* GetData;__try{GetData = ExAllocatePool(PagedPool, data->size);}__except (1){return FALSE;}for (int i = 0; i < data->size; i++){GetData[i] = data->data[i];}KAPC_STATE stack = { 0 };KeStackAttachProcess(process, &stack);PMDL mdl = IoAllocateMdl(data->address, data->size, 0, 0, NULL);if (mdl == NULL){return FALSE;}MmBuildMdlForNonPagedPool(mdl);BYTE* ChangeData = NULL;__try{ChangeData = MmMapLockedPages(mdl, KernelMode);RtlCopyMemory(ChangeData, GetData, data->size);}__except (1){bRet = FALSE;goto END;}END:IoFreeMdl(mdl);ExFreePool(GetData);KeUnstackDetachProcess(&stack);ObDereferenceObject(process);return bRet;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint(("hello lyshark \n"));WriteMemoryStruct ptr;ptr.pid = 6672;ptr.address = 0x402c00;ptr.size = 5;// 需要写入的数据ptr.data = ExAllocatePool(PagedPool, ptr.size);// 循环设置for (size_t i = 0; i < 5; i++){ptr.data[i] = 0x90;}// 写内存MDLWriteMemory(&ptr);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

写出效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/185974.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVAEE初阶 计算机如何工作

计算机的工作 一.冯诺依曼体系二.CPU基本工作流程1.逻辑门1.1电子开关1.2门电路 2.算术逻辑单元2.1 进制的理解2.2 算术单元2.3逻辑单元 3.寄存器和内存4.控制单元5.指令 一.冯诺依曼体系 二.CPU基本工作流程 1.逻辑门 1.1电子开关 1.2门电路 1.非门 2.与门 3.或门 4.异或…

【用unity实现100个游戏之15】开发一个类保卫萝卜的Unity2D塔防游戏2(附项目源码)

文章目录 先看本次实现的最终效果前言敌人生命值扣血测试&#xff0c;敌人死亡控制敌人动画敌人死亡动画敌人转向问题源码完结 先看本次实现的最终效果 前言 本期紧接着上一篇&#xff0c;本期主要内容是实现敌人血条、动画和行为逻辑。 敌人生命值 绘制血条UI 新建 publ…

嵌入式酒精壁炉:时尚生活的新宠

在这个注重品质与生活方式的时代&#xff0c;我们对于家居生活的要求早已不仅仅停留在实用性上。越来越多的人希望能够在家中营造一种时尚、温馨的氛围&#xff0c;而酒精壁炉恰好成为了这个潮流生活的代表。 如今&#xff0c;品质生活已经成为时尚的代名词。酒精壁炉以其精湛的…

使用VMware安装linux虚拟机

文章目录 一、介绍二、下载VMware三、下载centOS镜像文件四、新建虚拟机1. 选择配置类型2. 安装centOS操作系统3. 命名4. 指定磁盘容量5. 虚拟机配置信息确认6. 稍后选择系统镜像 五、安装虚拟机1. 选择语言2. 设置时间、系统、以及yum源3. 开始安装4. 设置root用户密码5. 完成…

抖音自动评论助手,其开发流程与需要的技术和代码分享

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 在当今这个信息爆炸的时代&#xff0c;我们不难发现&#xff0c;无论是在线上还是线下&#xff0c;我们都无法忽视一个重要的信息传播工具——抖音。这款短视频平台以其强大的传播…

应用在城市井盖积水检测中的深水液位传感芯片

城市井盖积水检测系统以实现城市下水道水雨情信息“全要素、全量程、全覆盖”自动测报为目标&#xff0c;具备下水道水位、雨量、流速、流量、雨量、气象参数、现场图像、视频等水文信息采集、传输、处理及预警等功能&#xff0c;有效提升了雨水情信息的时效性和准确度&#xf…

二进制分析工具-radare2使用教程

二进制分析工具-radare2使用教程 按照如下执行命令 按照如下执行命令 r2 -A 二进制文件

解析CAD图纸出现乱码的原因及解决方法

解析CAD图纸出现乱码的原因及解决方法 CAD&#xff08;计算机辅助设计&#xff09;是现代工程设计中不可或缺的工具&#xff0c;它能够帮助工程师们高效地完成复杂的设计任务。然而&#xff0c;有时在使用CAD软件过程中&#xff0c;可能会遇到图纸出现乱码的问题&#xff0c;影…

6块钱改变世界,网易和拼多多踏入同一条河流?

年底将至&#xff0c;各种颁奖盛典星光熠熠。如果要给今年深蹲反弹中的互联网大厂颁奖&#xff0c;2023表现最突出的可能是师出同门的兄弟网易和拼多多。 从市场表现来看&#xff0c;两家企业录得今年互联网中概股最高涨幅&#xff0c;被称为“中概股之光”&#xff1a;2023年…

python列出本地文件路径

按照之前的设想&#xff0c;如果要罗列出本地文件的列表&#xff0c;那不是需要不断的判断文件夹里面的文件夹吗&#xff1f;或者需要使用递归函数本身&#xff0c;才能达到目的吧&#xff1f;没想到使用pop这个函数就可以了。pop是取出元素&#xff0c;那列表里就少了一个&…

babel 无法加载解决方案

Visual Studio Code babel无法加载文件 babel : 无法加载文件 C:\Users\18327\AppData\Roaming\npm\babel.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c; 请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 所在…

会声会影2024什么时候发布更新?有哪些新功能

会声会影2024升级来袭&#xff0c;Corel公司这次为用户带来了多项功能更新&#xff0c;软件风格整体更偏向于“轻松剪辑&#xff0c;快速出片”。会声会影的本次更新还是很令人惊喜的&#xff0c;在各种人工智能算法的加持下&#xff0c;用户只需要进行几步简单地设置&#xff…