在上一篇博文《驱动开发:内核通过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);}/*将应用层中的内存复制到内核变量中SrcAddrr3地址要复制DstAddrR0申请的地址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 = https://www.huyubaike.com/biancheng/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.comNTSTATUS 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 = https://www.huyubaike.com/biancheng/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
处的数据读入内核空间并输出:
推荐阅读
- [Android开发学iOS系列] iOS写UI的几种方式
- AgileBoot - 基于SpringBoot + Vue3的前后端快速开发脚手架
- 驱动开发:内核通过PEB得到进程参数
- 完 golang开发:go并发的建议
- 三十七 Java开发学习----SpringBoot多环境配置及配置文件分类
- 🔥支持 Java 19 的轻量级应用开发框架,Solon v1.10.4 发布
- 驱动开发:内核取ntoskrnl模块基地址
- VScode开发STM32/GD32单片机-MakeFile工程JlinkRTT配置
- 一个C#开发者学习SpringCloud搭建微服务的心路历程
- VScode开发STM32/GD32单片机-环境搭建