通过MDL读写进程内存
通过MDL读写进程内存本文总结于lyshark的《Windows内核安全编程技术实践》。MDLWindows采用了分页机制来管理内存。考虑一个虚拟地址要经过逐级查表才可以得到物理地址。逻辑上连续的虚拟页很大概率会被映射到不连续的物理页中。假设现在要求对一块内存进行高效的操作比如IO频繁地进行查表转换操作效率太低所以可以把需要用到的页表的物理地址以链表的形式进行存储这样就避免了反复查表的开销。MDL的具体结构Windows并未公开这里只能参考其他人逆向得到的布局typedefstruct_MDL{struct_MDL*Next;// 指向下一个 MDL形成 MDL 链CSHORT Size;// 本 MDL 结构的总大小字节CSHORT MdlFlags;// 标志位见下表struct_EPROCESS*Process;// 所属进程若为用户模式缓冲区PVOID MappedSystemVa;// 映射后的系统空间虚拟地址若已映射PVOID StartVa;// 缓冲区起始页的虚拟地址页对齐ULONG ByteCount;// 缓冲区总字节数ULONG ByteOffset;// 缓冲区在起始页内的字节偏移}MDL,*PMDL;引用自Microsoft官方文档链接MDL 结构是半不透明的。 驱动程序应仅直接访问此结构的 Next 和 MdlFlags 成员。通过MDL读取进程内存主要步骤1. KeStackAttachProcess() // 附加到目标进程 2. IoAllocateMdl() // 创建 MDL 3. MmProbeAndLockPages() // 探测并锁定页面 4. KeUnstackDetachProcess() // 脱离目标进程 5. MmGetSystemAddressForMdlSafe() // 获取系统空间地址 6. RtlCopyMemory() // 读取数据 7. MmUnlockPages() IoFreeMdl() // 清理资源附加到目标进程首先我们需要切换当前线程的地址空间到目标进程中因为IoAllocateMdl需要知道目标虚拟地址在哪个进程上下文中并且我们还要在目标进程的地址空间中验证地址有效性确保后续的MmProbeAndLockPages能正确访问目标进程的页表。创建MDLPMDL pMdlIoAllocateMdl((PVOID)data-pTargetAddr,// 目标虚拟地址data-dwSize,// 要读取的大小false,// SecondaryBuffer通常为falsefalse,// ChargeQuota通常为falsenullptr// Irp可选);创建一个 MDL 来描述目标内存区域。关键探测并锁定页面MmProbeAndLockPages(pMdl,UserMode,IoReadAccess);这是最关键的一步包含两个操作探测Probe验证地址有效性确保地址在用户模式地址空间0x00000000-0x7FFFFFFF检查访问权限验证当前是否有读取权限对齐检查确保地址和大小符合对齐要求异常处理如果地址无效会抛出异常代码中缺少 try/except锁定Lock确保页面驻留将相关页面锁定在物理内存中防止被换出到磁盘建立物理映射在 MDL 中记录物理页面信息增加引用计数防止页面被释放参数说明UserMode表示地址是用户模式地址IoReadAccess指定只读访问权限脱离目标进程MDL 已经捕获了物理页面信息物理页面被锁定在内存中不再需要目标进程的地址空间上下文。这样一来我们就可以减少对目标进程的干扰、允许在脱离状态下长时间访问内存、避免保持附加状态可能导致的死锁问题。获取系统空间地址PVOID mappedAddressMmGetSystemAddressForMdlSafe(pMdl,NormalPagePriority);目的将 MDL 描述的物理页面映射到系统空间。工作原理根据 MDL 中的物理页面信息创建系统空间的虚拟地址映射返回一个在内核中可以直接访问的指针该函数返回的地址在内核空间中我们可以直接使用RtlCopyMemory等进行访问。示例代码#includentifs.h#includewindef.hstructData{DWORD dwPID;PVOID pTargetAddr;PBYTE data;DWORD dwSize;};VOIDDriverUnload(PDRIVER_OBJECT pDriverObj){UNREFERENCED_PARAMETER(pDriverObj);DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_INFO_LEVEL,Uninstall Driver Successfully!\n);}BOOLMDL_ReadMemory(Data*data){PEPROCESS pEpnullptr;NTSTATUS statusPsLookupProcessByProcessId((HANDLE)data-dwPID,pEp);if(!NT_SUCCESS(status)||pEpnullptr){DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_INFO_LEVEL,find process failed!\n);returnFALSE;}// 附加到进程KAPC_STATE stack{0};KeStackAttachProcess(pEp,stack);// 创建MDLPMDL pMdlIoAllocateMdl((PVOID)data-pTargetAddr,data-dwSize,false,false,nullptr);if(!pMdl){DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_INFO_LEVEL,find process failed!\n);KeUnstackDetachProcess(stack);ObDereferenceObject(pEp);returnfalse;}// 探测并锁定页面MmProbeAndLockPages(pMdl,UserMode,IoReadAccess);// 取消附加KeUnstackDetachProcess(stack);// 获取系统空间映射地址PVOID mappedAddressMmGetSystemAddressForMdlSafe(pMdl,NormalPagePriority);if(!mappedAddress){MmUnlockPages(pMdl);IoFreeMdl(pMdl);ObDereferenceObject(pEp);returnfalse;}// 读取数据RtlCopyMemory(data-data,mappedAddress,data-dwSize);// 清理资源MmUnlockPages(pMdl);IoFreeMdl(pMdl);ObDereferenceObject(pEp);returntrue;}EXTERN_C NTSTATUSDriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegPath){UNREFERENCED_PARAMETER(pRegPath);DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_INFO_LEVEL,Installing Driver...\n);pDriverObj-DriverUnloadDriverUnload;Data data;data.dwPID6776;data.pTargetAddr(PVOID)0x00000056ac965000;data.dwSize20;if(MDL_ReadMemory(data)){for(size_t i0;idata.dwSize/sizeof(DWORD);i){DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_INFO_LEVEL,Readed: %x\n,((PDWORD)data.data)[i]);}}else{DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_INFO_LEVEL,Installing Driver...\n);}returnSTATUS_SUCCESS;}