龙马谷

 找回密码
 立即注册

QQ登录

只需一步,快速开始

龙马谷VIP会员办理客服QQ:82926983(如果临时会话没有收到回复,请先加QQ好友再发。)
1 [已完结] GG修改器新手入门与实战教程 31课 2 [已完结] GG修改器美化修改教程 6课 3 [已完结] GG修改器Lua脚本新手入门教程 12课
4 [已完结] 触动精灵脚本新手入门必学教程 22课 5 [已完结] 手游自动化脚本入门实战教程 9课 6 [已完结] C++射击游戏方框骨骼透视与自瞄教程 27课
7 [已完结] C++零基础UE4逆向开发FPS透视自瞄教程 29课 8 [已完结] C++零基础大漠模拟器手游自动化辅助教程 22课
以下是天马阁VIP教程,本站与天马阁合作,赞助VIP可以获得天马阁对应VIP会员,名额有限! 点击进入天马阁论坛
1 [已完结] x64CE与x64dbg入门基础教程 7课 2 [已完结] x64汇编语言基础教程 16课 3 [已完结] x64辅助入门基础教程 9课
4 [已完结] C++x64内存辅助实战技术教程 149课 5 [已完结] C++x64内存检测与过检测技术教程 10课 6 [已完结] C+x64二叉树分析遍历与LUA自动登陆教程 19课
7 [已完结] C++BT功能原理与x64实战教程 29课 8 [已完结] C+FPS框透视与自瞄x64实现原理及防护思路
查看: 3928|回复: 0

Windows驱动中通过MDL实现用户态与核心态共享内存

[复制链接]

19

主题

0

回帖

28

积分

编程入门

Rank: 1

龙马币
94
Windows驱动跑在核心态(Kernel mode),驱动的调用者跑在用户态。如何使用户态进程与核心态驱动共享内存呢 ?

我们知道32位Windows中,默认状态下虚拟空间有4G,前2G是每个进程私有的,也就是说在进程切换的时候会变化,后2G是操作系统的,所以是固定的。既然用户态进程和核心态驱动在同一个进程空间里,是不是只要直接传个内存地址过来,就可以访问了?理论上可以但实际上不行,因为用户态的进程在不断地切换,使驱动运行时没法保证前面的用户态进程是哪个,也就不确定前2G虚拟地址空间的映射情况,那么用户态进程传来的地址也许不是合法的。

比较常用的做法是通过MDL进行内存的重映射。简单地说就是将同一块物理内存同时映射到用户态空间和核心态空间。

具体来说,可以有两种做法:用户态进程分配空间,内核态去映射。另一种是内核态分配空间,用户态进程去映射。

前者伪码:
  1. // assume uva is a virtual address in user space, uva_size is its size
  2. MDL * mdl  = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
  3. ASSERT(mdl);
  4. __try {
  5.         MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
  6. } __except(EXCEPTION_EXECUTE_HANDLER) {
  7.         DbgPrint("error code = %d", GetExceptionCode);
  8. }
  9. PVOID kva = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
  10. // use kva
  11. // …

  12. MmUnlockPages(mdl);
  13. IoFreeMdl(mdl);
复制代码

*记得在driver unload之前把mdl unlock和free掉,否则会BSoD。

后者伪码:
  1. PVOID kva = ExAllocatePoolWithTag(NonPagedPool, 1024, (ULONG)'PMET');
  2. MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
  3. ASSERT(mdl);
  4. __try {
  5.         MmBuildMdlForNonPagedPool(mdl);
  6. } __except(EXCEPTION_EXECUTE_HANDLER) {
  7.         DbgPrint("error code = %d", GetExceptionCode);
  8. }
复制代码


  1. PVOID uva = MmMapLockedPagesSpecifyCache(mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority);
复制代码

*如果kva是分配在nonpagedpool,那这些物理页本身就是被lock住的,因此用的是MmBuildMdlForNonPagedPool,如果是分配在paged pool里的用MmProbeAndLockPages。

除了这种最原始的方式,Windows还提供了两种称为DO_BUFFERED_IO 和DO_DIRECT_IO的方式,前者中系统自动将用户态空间内存拷贝到了到核心态空间(Associated-Irp.SystemBuffer),后者由系统自动生成MDL(Irp->MdlAddress)。其实这两种方法本质都是系统帮忙做了上面的部分流程,从而可以让程序员省了那些操作。


前面提到了一个关键数据结构MDL(memorydescriptor list )
系统用它来描述虚拟空间对应物理内存的layout。MDL分为两部分:固定长部分和变长部分,固定长部分结构如下:
  1. typedef struct _MDL {
  2.   struct _MDL *Next;
  3.   CSHORT Size;
  4.   CSHORT MdlFlags;
  5.   struct _EPROCESS *Process;
  6.   PVOID MappedSystemVa;
  7.   PVOID StartVa;
  8.   ULONG ByteCount;
  9.   ULONG ByteOffset;
  10. } MDL, *PMDL;
复制代码


Next: 指向下一个MDL结构,从而构成链表,有时一个IRP会包含多个MDL
Size:MDL本身的大小,注意包含了定长部分和变长两部分的size
MdlFlags:属性标记,如所描述的物理页有没有被lock住等
Process:顾名思义,指向该包含该虚拟地址的地址空间的对应进程结构
MappedSystemVa:内核态空间中的对应地址
StartVa:用户或者内核地址空间中的虚拟地址,取决于在哪allocate的,该值是页对齐的
ByteCount:MDL所描述的虚拟地址段的大小,byte为单位
ByteOffset:起始地址的页内偏移,因为MDL所描述的地址段不一定是页对齐的
如allocate出来的虚拟地址为0xac004010,则StartVa为0xac004000,ByteOffset为0x10,MmGetMdlVirtualAddress给出StartVa + ByteOffset。


变长部分包含了物理页编号数组,可以用

PPFN_NUMBER  pfn = MmGetMdlPfnArray(mdl)

来得到,注意里面只包含了pfn,不包含页内偏移量。数组的元素个数可以由ADDRESS_AND_SIZE_TO_SPAN_PAGES得到。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

龙马谷| C/C++辅助教程| 安卓逆向安全| 论坛导航| 免责申明|Archiver|
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表龙马谷立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2018-2021 All Right Reserved.
在线客服
快速回复 返回顶部 返回列表