< 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中SrcAddrR0要复制的地址DstAddr返回R3的地址Size拷贝长度*/NTSTATUS SafeCopyMemory_R0_to_R3(PVOID SrcAddr, PVOID DstAddr, ULONG Size){ PMDLpSrcMdl = 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.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;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;}
经验总结扩展阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [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搭建微服务的心路历程