驱动开发:内核MDL读写进程内存
MDL内存读写是最常用的一种读写模式,通常需要附加到指定进程空间内然后调用内存拷贝得到对端内存中的数据,在调用结束后再将其空间释放掉,通过这种方式实现内存读写操作,此种模式的读写操作也是最推荐使用的相比于CR3切换来说,此方式更稳定并不会受寄存器的影响。
MDL读取内存步骤
- 1.调用PsLookupProcessByProcessId得到进程Process结构
- 2.调用KeStackAttachProcess附加到对端进程内
- 3.调用ProbeForRead检查内存是否可读写
- 4.拷贝内存空间中的数据到自己的缓冲区内
- 5.调用KeUnstackDetachProcess接触绑定
- 6.调用ObDereferenceObject使对象引用数减1
代码总结起来应该是如下样子,用户传入一个结构体,输出对应长度的字节数据:
#include <ntifs.h>
#include <windef.h>
typedef struct
{
DWORD pid; // 要读写的进程ID
DWORD64 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效果如下所示:

MDL写入内存步骤
- 1.调用PsLookupProcessByProcessId得到进程Process结构
- 2.调用KeStackAttachProcess附加到对端进程内
- 3.调用ProbeForRead检查内存是否可读写
- 4.拷贝内存空间中的数据到自己的缓冲区内
- 5.调用MmMapLockedPages锁定当前内存页面(写入)
- 6.调用RtlCopyMemory内存拷贝完成写入(写入)
- 7.调用IoFreeMdl释放MDL锁(写入)
- 8.调用KeUnstackDetachProcess接触绑定
- 9.调用ObDereferenceObject使对象引用数减1
写入时与读取类似,只是多了锁定页面和解锁操作。
#include <ntifs.h>
#include <windef.h>
typedef struct
{
DWORD pid; // 要读写的进程ID
DWORD64 address; // 要读写的地址
DWORD size; // 读写长度
BYTE* data; // 要读写的数据
}ReadMemoryStruct;
// MDL写内存
BOOL MDLWriteMemory(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;
}
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"));
ReadMemoryStruct 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;
}
写出效果如下:

驱动开发:内核MDL读写进程内存的更多相关文章
- 驱动读写进程内存R3,R0通信
stdafx.h 头文件代码 #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. #defin ...
- Windows驱动开发-内核常用内存函数
搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool
- Linux驱动开发常用头文件
头文件目录中总共有32个.h头文件.其中主目录下有13个,asm子目录中有4个,linux子目录中有10个,sys子目录中有5个.这些头文件各自的功能如下: 1.主目录 <a.out.h> ...
- 驱动开发:内核CR3切换读写内存
首先CR3是什么,CR3是一个寄存器,该寄存器内保存有页目录表物理地址(PDBR地址),其实CR3内部存放的就是页目录表的内存基地址,运用CR3切换可实现对特定进程内存地址的强制读写操作,此类读写属于 ...
- 驱动开发:内核R3与R0内存映射拷贝
在上一篇博文<驱动开发:内核通过PEB得到进程参数>中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这 ...
- 驱动开发:内核中实现Dump进程转储
多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导 ...
- 驱动开发:内核遍历进程VAD结构体
在上一篇文章<驱动开发:内核中实现Dump进程转储>中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD结构,该结构的全程是Virtual Address Descrip ...
- 用Visual studio2012在Windows8上开发内核驱动监视进程创建
在Windows NT中,80386保护模式的“保护”比Windows 95中更坚固,这个“镀金的笼子”更加结实,更加难以打破.在Windows 95中,至少应用程序I/O操作是不受限制的,而在Win ...
- 《Windows内核安全与驱动开发》 3.2 内存与链表
<Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 3.2 内存与链表 1. 尝试生成一个链表头并将其初始化. 2. 尝试向内存 ...
- 驱动开发:通过Async反向与内核通信
在前几篇文章中给大家具体解释了驱动与应用层之间正向通信的一些经典案例,本章将继续学习驱动通信,不过这次我们学习的是通过运用Async异步模式实现的反向通信,反向通信机制在开发中时常被用到,例如一个杀毒 ...
随机推荐
- Spring Boot 整合 Camunda 实现工作流
工作流是我们开发企业应用几乎必备的一项功能,工作流引擎发展至今已经有非常多的产品.最近正好在接触Camunda,所以来做个简单的入门整合介绍.如果您也刚好在调研或者刚开始计划接入,希望本文对您有所帮助 ...
- AtCoder Regular Contest 119 (ABC题)
比赛链接:Here A - 119 × 2^23 + 1 注意到 \(2^{60} > 10^{18}\) ,所以我们可以直接枚举 \(0\) ~ \(59\) int main() { ci ...
- 深入剖析 RSA 密钥原理及实践
一.前言 在经历了人生的很多至暗时刻后,你读到了这篇文章,你会后悔甚至愤怒:为什么你没有早点写出这篇文章?! 你的至暗时刻包括: 1.你所在的项目需要对接银行,对方需要你提供一个加密证书.你手上只有一 ...
- 08.25北京站|阿里云Serverless 技术实践营( AI 专场)开放报名
活动简介 阿里云 Serverless 技术实践营(AI 专场)是一场以聚焦企业级 AIGC 应用开发与落地展开的主题活动,活动受众以关注 Serverless 和 AI 技术的开发者.企业决策人.云 ...
- 一、redis单例安装(linux)
系列导航 一.redis单例安装(linux) 二.redis主从环境搭建 三.redis集群搭建 四.redis增加密码验证 五.java操作redis 环境:centos7.5需要的安装包: re ...
- Vue项目中使用 tinymce 富文本编辑器的方法,附完整源码
Vue项目中使用 tinymce 富文本编辑器的方法,附完整源码 https://blog.csdn.net/snsHL9db69ccu1aIKl9r/article/details/11432414 ...
- 如何一键私有化部署 Laf ?
太长不看:Laf 上架了 Sealos 的模板市场,通过 Laf 应用模板即可一键部署! Laf 是一个完全开源的项目,除了使用公有云之外,还有大量的用户选择私有化部署 Laf.然而,私有化部署通常伴 ...
- Spring cloud gateWay 限流器限流(一)
转载请注明出处: spring cloud 提供了限流操作的功能,其使用步骤如下: 1.引入maven依赖: <dependency> <groupId>org.springf ...
- CAP-BASE
- [转帖]TiDB之修改root密码
https://www.modb.pro/db/337530 当忘记TiDB root 密码时,可以通过设置skip-grant-table参数来跳过密码验证,登录成功以后再修改root密码. 方法一 ...