在上一篇博文《驱动开发:内核通过PEB得到进程参数》中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这次我们将实现一个更加有趣的功能,在某些情况下应用层与内核层需要共享一片内存区域通过这片区域可打通内核与应用层的隔离,此类功能的实现依附于MDL内存映射机制实现。

应用层(R3)数据映射到内核层(R0)

先来实现将R3内存数据拷贝到R0中,功能实现所调用的API如下:

  • IoAllocateMdl 该函数用于创建MDL(类似初始化)
  • MmProbeAndLockPages 用于锁定创建的地址其中UserMode代表用户层,IoReadAccess以读取的方式锁定
  • MmGetSystemAddressForMdlSafe 用于从MDL中得到映射内存地址
  • RtlCopyMemory 用于内存拷贝,将DstAddr应用层中的数据拷贝到pMappedSrc
  • MmUnlockPages 拷贝结束后解锁pSrcMdl
  • IoFreeMdl 释放MDL

内存拷贝SafeCopyMemory_R3_to_R0函数封装代码如下:

#include <ntifs.h>
#include <windef.h>// 分配内存
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');if (InZeroMemory && (Result != NULL))RtlZeroMemory(Result, InSize);return Result;
}// 释放内存
void RtlFreeMemory(void* InPointer)
{ExFreePool(InPointer);
}/*
将应用层中的内存复制到内核变量中SrcAddr  r3地址要复制
DstAddr  R0申请的地址
Size     拷贝长度
*/
NTSTATUS SafeCopyMemory_R3_to_R0(ULONG_PTR SrcAddr, ULONG_PTR DstAddr, ULONG Size)
{NTSTATUS status = STATUS_UNSUCCESSFUL;ULONG nRemainSize = PAGE_SIZE - (SrcAddr & 0xFFF);ULONG nCopyedSize = 0;if (!SrcAddr || !DstAddr || !Size){return status;}while (nCopyedSize < Size){PMDL pSrcMdl = NULL;PVOID pMappedSrc = NULL;if (Size - nCopyedSize < nRemainSize){nRemainSize = Size - nCopyedSize;}// 创建MDLpSrcMdl = IoAllocateMdl((PVOID)(SrcAddr & 0xFFFFFFFFFFFFF000), PAGE_SIZE, FALSE, FALSE, NULL);if (pSrcMdl){__try{// 锁定内存页面(UserMode代表应用层)MmProbeAndLockPages(pSrcMdl, UserMode, IoReadAccess);// 从MDL中得到映射内存地址pMappedSrc = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);}__except (EXCEPTION_EXECUTE_HANDLER){}}if (pMappedSrc){__try{// 将MDL中的映射拷贝到pMappedSrc内存中RtlCopyMemory((PVOID)DstAddr, (PVOID)((ULONG_PTR)pMappedSrc + (SrcAddr & 0xFFF)), nRemainSize);}__except (1){// 拷贝内存异常}// 释放锁MmUnlockPages(pSrcMdl);}if (pSrcMdl){// 释放MDLIoFreeMdl(pSrcMdl);}if (nCopyedSize){nRemainSize = PAGE_SIZE;}nCopyedSize += nRemainSize;SrcAddr += nRemainSize;DstAddr += nRemainSize;}status = STATUS_SUCCESS;return status;
}

调用该函数实现拷贝,如下代码中首先PsLookupProcessByProcessId得到进程EProcess结构,并KeStackAttachProcess附加进程,声明pTempBuffer指针用于存储RtlAllocateMemory开辟的内存空间,nSize则代表读取应用层进程数据长度,ModuleBase则是读入进程基址,调用SafeCopyMemory_R3_to_R0即可将应用层数据拷贝到内核空间,并最终BYTE* data转为BYTE字节的方式输出。

VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}// lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark.com \n");NTSTATUS status = STATUS_UNSUCCESSFUL;PEPROCESS eproc = NULL;KAPC_STATE kpc = { 0 };__try{// HANDLE 进程PIDstatus = PsLookupProcessByProcessId((HANDLE)4556, &eproc);if (NT_SUCCESS(status)){// 附加进程KeStackAttachProcess(eproc, &kpc);// -------------------------------------------------------------------// 开始映射// -------------------------------------------------------------------// 将用户空间内存映射到内核空间PVOID pTempBuffer = NULL;ULONG nSize = 0x1024;ULONG_PTR ModuleBase = 0x0000000140001000;// 分配内存pTempBuffer = RtlAllocateMemory(TRUE, nSize);if (pTempBuffer){// 拷贝数据到R0status = SafeCopyMemory_R3_to_R0(ModuleBase, (ULONG_PTR)pTempBuffer, nSize);if (NT_SUCCESS(status)){DbgPrint("[*] 拷贝应用层数据到内核里 \n");}// 转成BYTE方便读取BYTE* data = pTempBuffer;for (size_t i = 0; i < 10; i++){DbgPrint("%02X \n", data[i]);}}// 释放空间RtlFreeMemory(pTempBuffer);// 脱离进程KeUnstackDetachProcess(&kpc);}}__except (EXCEPTION_EXECUTE_HANDLER){Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码运行后即可将进程中0x0000000140001000处的数据读入内核空间并输出:

内核层(R0)数据映射到应用层(R3)

与上方功能实现相反SafeCopyMemory_R0_to_R3函数则用于将一个内核层中的缓冲区写出到应用层中,写出过程:

  • IoAllocateMdl 分别调用MDL分配,源地址SrcAddr目标地址DstAddr均创建
  • MmBuildMdlForNonPagedPool 该 MDL 指定非分页虚拟内存缓冲区,并对其进行更新以描述基础物理页
  • MmGetSystemAddressForMdlSafe 调用两次得到源地址,分别获取pSrcMdl,pDstMdl两个MDL的
  • MmProbeAndLockPages 以写入方式锁定用户层中pDstMdl的地址

内存拷贝SafeCopyMemory_R0_to_R3函数封装代码如下:

// 分配内存
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');if (InZeroMemory && (Result != NULL))RtlZeroMemory(Result, InSize);return Result;
}// 释放内存
void RtlFreeMemory(void* InPointer)
{ExFreePool(InPointer);
}/*
将内存中的数据复制到R3中SrcAddr  R0要复制的地址
DstAddr  返回R3的地址
Size     拷贝长度
*/
NTSTATUS SafeCopyMemory_R0_to_R3(PVOID SrcAddr, PVOID DstAddr, ULONG Size)
{PMDL  pSrcMdl = NULL, pDstMdl = NULL;PUCHAR pSrcAddress = NULL, pDstAddress = NULL;NTSTATUS st = STATUS_UNSUCCESSFUL;// 分配MDL 源地址pSrcMdl = IoAllocateMdl(SrcAddr, Size, FALSE, FALSE, NULL);if (!pSrcMdl){return st;}// 该 MDL 指定非分页虚拟内存缓冲区,并对其进行更新以描述基础物理页。MmBuildMdlForNonPagedPool(pSrcMdl);// 获取源地址MDL地址pSrcAddress = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);if (!pSrcAddress){IoFreeMdl(pSrcMdl);return st;}// 分配MDL 目标地址pDstMdl = IoAllocateMdl(DstAddr, Size, FALSE, FALSE, NULL);if (!pDstMdl){IoFreeMdl(pSrcMdl);return st;}__try{// 以写入的方式锁定目标MDLMmProbeAndLockPages(pDstMdl, UserMode, IoWriteAccess);// 获取目标地址MDL地址pDstAddress = MmGetSystemAddressForMdlSafe(pDstMdl, NormalPagePriority);}__except (EXCEPTION_EXECUTE_HANDLER){}if (pDstAddress){__try{// 将源地址拷贝到目标地址RtlCopyMemory(pDstAddress, pSrcAddress, Size);}__except (1){// 拷贝内存异常}MmUnlockPages(pDstMdl);st = STATUS_SUCCESS;}IoFreeMdl(pDstMdl);IoFreeMdl(pSrcMdl);return st;
}

调用该函数实现拷贝,此处除去附加进程以外,在拷贝之前调用了ZwAllocateVirtualMemory将内存属性设置为PAGE_EXECUTE_READWRITE可读可写可执行状态,然后在向该内存中写出pTempBuffer变量中的内容,此变量中的数据是0x90填充的区域。

VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}// lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark.com \n");NTSTATUS status = STATUS_UNSUCCESSFUL;PEPROCESS eproc = NULL;KAPC_STATE kpc = { 0 };__try{// HANDLE 进程PIDstatus = PsLookupProcessByProcessId((HANDLE)4556, &eproc);if (NT_SUCCESS(status)){// 附加进程KeStackAttachProcess(eproc, &kpc);// -------------------------------------------------------------------// 开始映射// -------------------------------------------------------------------// 将用户空间内存映射到内核空间PVOID pTempBuffer = NULL;ULONG nSize = 0x1024;PVOID ModuleBase = 0x0000000140001000;// 分配内存pTempBuffer = RtlAllocateMemory(TRUE, nSize);if (pTempBuffer){memset(pTempBuffer, 0x90, nSize);// 设置内存属性 PAGE_EXECUTE_READWRITEZwAllocateVirtualMemory(NtCurrentProcess(), &ModuleBase, 0, &nSize, MEM_RESERVE, PAGE_EXECUTE_READWRITE);ZwAllocateVirtualMemory(NtCurrentProcess(), &ModuleBase, 0, &nSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);// 将数据拷贝到R3中status = SafeCopyMemory_R0_to_R3(pTempBuffer, &ModuleBase, nSize);if (NT_SUCCESS(status)){DbgPrint("[*] 拷贝内核数据到应用层 \n");}}// 释放空间RtlFreeMemory(pTempBuffer);// 脱离进程KeUnstackDetachProcess(&kpc);}}__except (EXCEPTION_EXECUTE_HANDLER){Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

拷贝成功后,应用层进程内将会被填充为Nop指令。

驱动开发:内核R3与R0内存映射拷贝相关推荐

  1. Linux驱动开发—内核I2C驱动详解

    Linux驱动开发--内核I2C驱动 I2C驱动文件结构 I2C数据传输过程 i2c_transfer i2c_msg I2C通讯常用的接口函数(老版本) 快速读写接口函数:(连续读写) 常用的读操作 ...

  2. v15.03 鸿蒙内核源码分析(内存映射) | 映射真是个好东西 | 百篇博客分析HarmonyOS源码

    子曰:"德不孤,必有邻." <论语>:里仁篇 百篇博客系列篇.本篇为: v15.xx 鸿蒙内核源码分析(内存映射篇) | 映射真是个好东西 内存管理相关篇为: v11. ...

  3. Windows驱动开发 - 内核模式下的字符串操作

    1 ASCII字符串和宽字符串 char型,记录ansi字符集.每个字符一个字节.以0标志结束.在KdPrint中用%s输出. 宽字符型,wchar_t,描述unicode字符集的字符串,每个字符两个 ...

  4. 驱动开发:内核中实现Dump进程转储

    多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导 ...

  5. Linux 驱动开发 三十五:Linux 内核时钟管理

    参考: linux时间管理,时钟中断,系统节拍_u010936265的博客-CSDN博客_系统节拍时钟中断 Linux内核时钟系统和定时器实现_anonymalias的专栏-CSDN博客_linux内 ...

  6. nio java 内核拷贝_大文件拷贝,试试NIO的内存映射

    最近项目里有个需求需要实现文件拷贝,在java中文件拷贝流的读写,很容易就想到IO中的InputStream和OutputStream之类的,但是上网查了一下文件拷贝也是有很多种方法的,除了IO,还有 ...

  7. Windows核心编程——》第十七章 内存映射文件 (Memory-Mapped Files)

    1.概览 (1)什么是内存映射文件 内存映射文件是由一个文件到一块内存的映射,使进程虚拟地址空间的某个区域与磁盘上某个文件的部分或全部内容的建立映射. 建立映射后,通过该区域可以直接对被映射的磁盘文件 ...

  8. 《Linux Device Drivers》第十五章 内存映射和DMA——note

    简单介绍 很多类型的驱动程序编程都须要了解一些虚拟内存子系统怎样工作的知识 当遇到更为复杂.性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到 本章的内容分成三个部分 讲述mmap系统调用的实现 ...

  9. 内存映射与DMA笔记

    内存映射与DMA笔记 2009-08-04 14:49 3项技术: 1,mmap系统调用可以实现将设备内存映射到用户进程的地址空间. 2,使用get_user_pages,可以把用户空间内存映射到内核 ...

最新文章

  1. python自动化测试视频百度云-Python接口自动化测试 PDF 超清版
  2. electron调试html,electron桌面应用程序开发入门
  3. Aspose Cells 控件如何实现数据过滤(附代码和下载地址)
  4. Cool Kitten:新鲜出炉的视差滚动 响应式框架
  5. 解决WCF接口无法传递object参数的问题
  6. 当年,兔子学姐靠这个面试小抄拿了个22k
  7. android x866.0 教程,海尔暴风AmlogicT866平台升级步骤教程
  8. MyCat分布式数据库集群架构工作笔记0006---Mycat启动
  9. 计算机二级2018VB题库百度云,2018年计算机二级VB考试真题
  10. net实现基础的舆情监测系统思路
  11. 各国货币json文件
  12. python求直角三角形两边求锐角_在二元数组中求直角三角形坐标
  13. String对象intern方法
  14. 用于提升多样性的Maximum Mutual Information算法
  15. 大数据学习:大数据就业前景和就业方向
  16. 【EMC电磁兼容】01.05——标准测试类目之EMI
  17. 75寸的电视长宽各是多少厘米
  18. C++11 std::thread-使用lambda表达式-GCC 4.9编译通过
  19. jQuery遍历之closest()方法
  20. Android模拟打电话

热门文章

  1. There is no getter for property named ‘user‘ in ‘class com.jyr.wh.domain.User问题
  2. Python 技术篇-用zipfile库进行zip文件的压缩与解压实例演示,python压缩本地文件夹为zip文件并保留目录结构
  3. iOS App上架审核经验
  4. Haproxy加持KeepAlived+进程自动检测
  5. vue3和vite双向加持,uni-app性能再次提升
  6. python有限元传热求解_Python进行有限元编程-平面应力问题(三节点三角形单元)...
  7. 双轮平衡车实现自平衡功能
  8. 高频电路模拟电路课程实验配套装置
  9. ChatGPT大流行的思考-设想篇
  10. 数组超实用的常见方法:find、findIndex、some、every