驱动开发:内核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异步模式实现的反向通信,反向通信机制在开发中时常被用到,例如一个杀毒 ...
随机推荐
- 【每日一题】12.Running Median (对顶堆)
补题链接:Here 题意:动态的维护中位数的问题,依次读入一个整数,每当总个数为奇数时输出此时序列的中位数 使用对顶堆的在线做法. 为了实时找到中位数,我们可以建议两个二叉堆:一个小根堆.一个大根堆. ...
- django的简单学习
前言 以下项目实现基于一个投票系统 安装django 命令行安装 pip install django pycharm安装 pycharm的setting里找到这个,点击+号,搜索django 点击I ...
- vue学习笔记 十、状态管理基础结构
系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...
- C#将日期格式化为指定格式
private void btn_GetTime_Click(object sender, EventArgs e) { lab_time.Text = DateTime.Now.ToString(& ...
- Liunx常用操作(三)-如何忽略大小写查找
1.vim 中的查找 搜索文件内容时加上 /c 参数可以忽略搜索字符的大小写 正常搜索:/helloworld 忽略操作:/helloworld/c 2.find 查找 使用find命令搜索文件时如果 ...
- Postman调试grpc
转载请注明出处: 1.检查自己的postman是否支持 grpc,通过 File -> new -> ,出现如下图,则表示支持: 2.点击上图的grpc就会自动创建一个 grpc 的req ...
- git或gitee 提交代码到远程仓库
本文为博主原创,未经允许不得转载: 1. 选中远程仓库,并fork 指定的项目到自己的私仓: fork 之后,打开我的仓库便能看到刚刚fork 的项目. 2. clone 项目代码到自己电脑的本地仓库 ...
- SV 自定义数据类型
概述 自定义类型 枚举类型 定义枚举值 自定义枚举类型 枚举类型之间进行赋值是可以的 枚举类型可以赋值给整型,整型不能直接赋值给枚举类型 枚举类型 + 1 ==> 会进行隐式的转换,枚举类型转换 ...
- java - 正确关闭流
package stream; import java.io.*; public class FileReaderTest { public static void main(String[] arg ...
- [转帖]WinXP添加TLS1.1、TLS1.2支持
现象 HTTPS服务在Win7及Win10能够正常打开,但是在XP下用IE浏览器却无法打开,XP下用第三方浏览器(我试了谷歌浏览器)却能正常打开.经过抓包分析,用IE浏览器是协商用的是TLS1而用第三 ...