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

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

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

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

3.1.1 MDL读取内存步骤

调用PsLookupProcessByProcessId获取进程Process结构:该函数根据进程ID查找对应的进程对象,通过data->pid获取进程ID,然后调用PsLookupProcessByProcessId获取PEPROCESS结构。如果获取失败,则返回FALSE调用KeStackAttachProcess附加到目标进程:在内核模式下,读取其他进程的内存需要先附加到对应进程的上下文中。调用KeStackAttachProcess函数将当前线程切换到目标进程的上下文,同时保存当前进程的上下文状态。调用ProbeForRead检查内存是否可读写:在内核模式下,访问其他进程的内存需要保证访问合法,因此调用ProbeForRead函数检查读取的内存空间是否可读写。如果不可读写,则会触发异常,通过异常处理机制处理这种情况。将内存空间中的数据拷贝到自己的缓冲区:完成内存空间检查后,使用RtlCopyMemory函数将目标进程的内存数据拷贝到自己的缓冲区中。由于内存空间可能很大,可能需要多次拷贝操作。调用KeUnstackDetachProcess解除绑定:读取完内存数据后,需要将当前线程从目标进程的上下文中解除绑定,返回到原来的上下文中。调用KeUnstackDetachProcess函数完成解绑操作,同时恢复之前保存的当前进程的上下文状态。调用ObDereferenceObject减少对象引用数:由于第一步中调用了PsLookupProcessByProcessId函数获取了对应进程的PEPROCESS结构,因此需要调用ObDereferenceObject函数将其引用计数减1,以便释放对该对象的引用。

通过上述步骤,我们可以封装MDLReadMemory()内存读函数,代码如下,该函数用于在Windows内核模式下读取指定进程的内存数据:

#include #include 

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 DriverUnload = UnDriver;return STATUS_SUCCESS;}

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

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

Trae国内版 Trae国内版

国内首款AI原生IDE,专为中国开发者打造

Trae国内版 815 查看详情 Trae国内版

3.1.2 MDL写入内存步骤

调用PsLookupProcessByProcessId获取目标进程的进程结构:该函数根据传递的进程ID返回对应进程的PEPROCESS结构体,包含了进程的各种信息。使用KeStackAttachProcess附加到目标进程的上下文环境:该函数将当前线程的上下文环境切换到目标进程的上下文环境,使得该线程可以访问和修改目标进程的内存。调用ProbeForRead检查要写入的内存空间是否可读写:这个步骤是为了确保要写入的内存空间没有被保护或被其他进程占用,以避免对系统造成不良影响。将目标进程的内存空间中的数据拷贝到当前进程的缓冲区:便于进行修改操作。调用MmMapLockedPages锁定当前内存页面:该函数将返回一个指向系统虚拟地址的指针,该地址是由系统自动分配的。在写入完成后,需要使用MmUnmapLockedPages函数来释放锁定的内存页面。使用RtlCopyMemory完成内存拷贝操作:将缓冲区中的数据写入到锁定的内存页面中。调用IoFreeMdl释放MDL锁:MDL锁用于锁定MDL描述的内存页面,以便可以对其进行操作。使用KeUnstackDetachProcess解除当前进程与目标进程之间的绑定:使得当前线程的上下文环境恢复到原始的状态。调用ObDereferenceObject将MDL对象的引用计数减1:便于在不再需要该对象时释放它所占用的系统资源。

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

#include 

include

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 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 DriverUnload = UnDriver;return STATUS_SUCCESS;}

写出效果如下:

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

以上就是3.3 Windows驱动开发:内核MDL读写进程内存的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/460727.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月8日 03:23:16
下一篇 2025年11月8日 03:28:09

相关推荐

  • Golang如何使用桥接模式拆分抽象与实现_Golang 桥接模式设计优化实践

    桥接模式通过组合而非继承将抽象与实现分离,提升Go语言中多维度扩展的灵活性。定义Renderer接口及Windows、Mac具体实现,再构建Shape抽象并嵌入Renderer,使图形绘制与平台解耦。新增图形或渲染器无需修改原有代码,符合开闭原则。主函数根据系统动态选择渲染器,体现运行时灵活性。该模…

    2025年12月16日
    000
  • 使用Go语言实现基于域名的透明反向代理

    本文将深入探讨如何利用Go语言的`net/http/httputil.ReverseProxy`实现一个透明的反向代理,以将不同域名的请求路由到同一物理机上运行的不同后端服务。我们将详细介绍`Director`函数的核心作用,并通过示例代码演示如何根据请求的`Host`头动态转发请求,确保用户和搜索…

    2025年12月16日
    000
  • Golang如何在WSL环境中安装配置_Golang WSL开发环境实践

    先安装WSL并选择Ubuntu等发行版,再下载官方Go压缩包解压至/usr/local,配置PATH和GOPATH环境变量,验证go version确认安装成功,接着安装gopls、dlv等开发工具,最后通过VS Code的Remote-WSL插件实现高效开发,完整搭建适用于Go语言的WSL开发环境…

    2025年12月16日
    000
  • Go语言库中随机数生成的最佳实践

    本文深入探讨了在Go语言库中初始化和使用随机数的最佳实践。针对不同场景,文章提出了三种主要策略:通过依赖注入提供灵活的伪随机数生成器(PRNG)源,使用`crypto/rand`包实现高安全性的加密随机数,以及在库内部私有化`math/rand`实例以避免全局状态冲突。文章强调了在库中避免全局`ra…

    2025年12月16日
    000
  • Go 性能分析:使用 pprof 工具进行图形化可视化教程

    本教程详细介绍了如何使用 go 语言自带的 `go tool pprof` 工具对性能分析文件进行图形化可视化。文章将指导用户解决常见的函数名解析问题,并通过 `web` 命令生成直观的调用图(call graph),从而帮助开发者高效识别程序中的性能瓶颈。教程涵盖了从生成分析文件到解读图形化结果的…

    2025年12月16日
    000
  • 深入理解Go语言的CPU依赖性与跨平台编译

    go语言作为一种编译型语言,其程序最终会被编译成特定cpu架构的机器码,因此生成的二进制文件具有cpu依赖性。这意味着为arm架构编译的程序无法直接在x86架构上运行。然而,go语言通过其强大的跨平台编译能力,极大地简化了为不同操作系统和cpu架构生成可执行文件的过程,开发者无需多台物理机即可实现多…

    2025年12月16日
    000
  • Golang安装后无法运行go命令怎么办_Golang命令行环境修复与调试技巧

    首先确认Go安装路径是否正确,常见路径为/usr/local/go或C:Go;检查bin目录下是否存在go可执行文件;将Go的bin路径添加到系统PATH环境变量:Linux/macOS在~/.bashrc或~/.zshrc中添加export GOROOT=/usr/local/go和export …

    2025年12月16日
    000
  • 使用IntelliJ Go插件高效运行Go项目:从配置到最佳实践

    本文旨在指导用户如何使用intellij go插件正确配置和运行完整的go项目,而非仅仅单个文件。我们将探讨go项目的构建机制、intellij中“go application”配置的正确用法,并针对插件可能出现的“仅构建单文件”问题提供解决方案和最佳实践,包括更新插件、检查配置细节及必要时提交问题…

    2025年12月16日
    000
  • Go库中随机数生成的最佳实践与策略

    在go库中处理随机数时,选择合适的生成策略至关重要,以避免冲突并满足特定需求。本文探讨了三种主要方法:通过依赖注入提供`rand.source`以实现灵活性和可测试性;使用`crypto/rand`包生成高熵的加密安全随机数,适用于敏感场景;以及在简单非关键应用中,通过内部私有`*rand.rand…

    2025年12月16日
    000
  • Go 库代码中随机数生成与使用的最佳实践

    本文探讨go库中随机数生成的最佳实践。针对不同场景,我们提供三种策略:通过接口进行依赖注入,允许用户控制伪随机数源(如monte carlo模拟);内部使用`crypto/rand`生成高熵、密码学安全的随机数(如密钥生成);以及创建包私有的`math/rand.rand`实例,以避免全局状态污染并…

    2025年12月16日
    000
  • 如何在Windows系统中安装Golang_Windows平台Golang开发环境搭建指南

    下载并安装Go语言,使用默认路径C:Go;2. 命令行输入go version验证安装;3. 可选配置GOPATH为自定义项目路径如D:goprojects;4. 创建hello.go文件并运行go run hello.go输出成功信息,环境即搭建完成。 在Windows系统中安装Golang并搭建…

    2025年12月16日
    000
  • 使用 go tool pprof 进行 Go 性能剖析文件的图形化可视化

    本教程详细介绍了如何利用 Go 语言内置的 `go tool pprof` 工具对性能剖析文件进行图形化可视化。文章首先解决常见的函数名显示问题,强调提供程序可执行文件的必要性,随后指导用户通过 `pprof` 交互模式下的 `web` 命令生成直观的性能图表,并涵盖了 Graphviz 等关键依赖…

    2025年12月16日
    000
  • Golang如何配置环境变量以支持命令行工具_Golang命令行开发环境完整指南

    正确配置Golang环境变量是命令行工具开发的关键。需设置GOROOT、GOBIN并将其加入PATH,Linux/macOS通过shell配置文件添加export,Windows通过系统变量或PowerShell设置;开发时使用go install将编译后的工具安装到GOBIN目录,确保项目含mai…

    2025年12月16日
    000
  • Go语言归并排序深度解析:避免栈溢出与正确实现指南

    本文深入探讨go语言中归并排序的正确实现方法,重点分析了常见的栈溢出问题,并提供了基于索引和切片两种优化方案的详细代码示例。通过理解归并排序的递归逻辑和合并操作,读者将能有效避免性能陷阱,实现高效稳定的排序算法。 归并排序概述 归并排序(Merge Sort)是一种高效、稳定的排序算法,其核心思想是…

    2025年12月16日
    000
  • Golang编译器路径找不到怎么办_Golang编译器环境配置与错误修复方法

    答案:Go编译器路径找不到通常因环境变量未配置或Go未安装,需检查go version命令是否可用,若提示命令不存在则需下载安装并正确设置GOROOT、GOPATH及PATH,确保$GOROOT/bin加入系统路径,最后通过go version和go env验证配置。 遇到Golang编译器路径找不…

    2025年12月16日
    000
  • 如何快速定位Golang安装路径_Golang安装目录定位与环境检测技巧

    使用go env命令可直接查看GOROOT和GOPATH,定位Go安装路径;通过which go或where go查找可执行文件位置;运行go version、go env和go list验证环境配置是否正确;不同系统默认路径分别为/usr/local/go(macOS/Linux)和C:Go(Wi…

    2025年12月16日
    000
  • Go 库中随机数生成的最佳实践

    在Go库中处理随机数生成时,核心挑战在于平衡灵活性、安全性与避免全局状态冲突。本文将探讨三种主要策略:通过接口实现依赖注入以提供用户可控的伪随机数源;在库内部私有化并初始化`math/rand`实例以简化使用;以及在需要高强度随机性的场景下,利用`crypto/rand`确保加密安全。这些方法旨在帮…

    2025年12月16日
    000
  • Go语言中从控制台读取标准输入的实用指南

    本文深入探讨了go语言中从控制台读取标准输入的多种方法,包括使用`bufio`包进行高效的行读取,以及`fmt`包中的`scanln`和`scanf`函数进行格式化输入。文章通过详细的代码示例、关键注意事项和常见错误解析,旨在帮助开发者准确理解并有效实现在go应用程序中与用户交互的输入功能,提升程序…

    2025年12月16日
    000
  • Go语言:控制台标准输入的读取方法详解

    本文深入探讨了go语言中从控制台读取标准输入的多种方法,包括使用`bufio.reader`读取整行文本,以及`fmt`包中的`scanln`和`scanf`函数进行格式化输入。文章详细解释了每种方法的用法、适用场景,并强调了在使用`fmt`系列函数时变量需要传入指针的关键注意事项,辅以清晰的代码示…

    2025年12月16日
    000
  • Go 性能剖析文件图形化可视化教程:使用 pprof 及 Graphviz

    本教程详细介绍了如何利用 Go 语言内置的 go tool pprof 工具对性能剖析文件进行图形化可视化。我们将解决常见的函数名显示问题,并通过 web 命令结合 Graphviz 生成直观的调用图,从而帮助开发者高效分析程序性能瓶颈。 1. 理解 Go 性能剖析与 pprof Go 语言提供了一…

    2025年12月16日
    000

发表回复

登录后才能评论
关注微信