存储描述符表(MDL)剖析(一)

一 MDL是什么 

在MSDN中有这样的定义

内存描述符列表 (MDL) 是一个系统定义的结构,通过一系列物理地址描述缓冲区。执行直接 I/O 的驱动程序从 I/O 管理器接收一个 MDL 的指针,并通过 MDL 读写数据。一些驱动程序在执行直接 I/O 来满足设备 I/O 控制请求时也使用 MDL。

http://msdn.microsoft.com/zh-cn/windows/hardware/gg463193.aspx这里有完整的内容,但该文章是机器人翻译过来的,所以看起来有点头疼.

因此通俗的解释一下,MDL仅仅运用于内核中,在应用层并不会涉及这个结构,由于内核中的驱动有跟应用层程序通信的需要,因此可能会接收到来自进程空间的虚拟地址,而在windows的分页机制下,进程空间中的任何一个虚拟地址所属的页面都有可能被内存管理器从RAW置换到页文件中,或者,进程被释放或是取消地址的映射。这些都会导致严重的错误发生。因此内核创建一个MDL,并将其与来自进程空间的虚拟地址相关联,当需要对这些虚拟地址进行读写的时候调用相关的内核函数,锁定这些虚拟地址对应的物理页面和逻辑页面,防止物理页面被置换,逻辑页面被修改或者释放。

另外一种情况下一个驱动程序在执行纯内核任务中也可以使用MDL,特别的仅仅调用非分页内存的话,这些页面是不会置换到页文件中的,因此不需要考虑锁定页面的问题。

 

二 MDL的内容

先看看wdm.h中MDL的定义:

typedef __struct_bcount(Size) struct _MDL {
    struct _MDL *Next;
    CSHORT Size;
    CSHORT MdlFlags;
    struct _EPROCESS *Process;
    PVOID MappedSystemVa;
    PVOID StartVa;
    ULONG ByteCount;
    ULONG ByteOffset;
} MDL, *PMDL;

 

先大概说明一下爱各个字段:

   Next:MDL可以连接成一个单链表,因此可以将分散的虚拟机地址串接起来。

    Size:一个MDL并不单单包含结构里这些东西,在内存中紧接着一个MDL结构,存着这个MDL对应的各个物理页面编号,由于一个物理页面一定是4KB对齐的,所以这个编号相当于一个物理页面起始地址的高20位。Size的值减去sizeof(MDL),等于存放编号的区域的大小。比如该MDL需要三个物理页面来映射虚拟地址空间,则Size-sizeof(MDL)==4*3==12;

    MdlFlags:很重要的字段,用于描述和操控虚拟地址的各种属性。

    Process:如果虚拟地址是某一进程的用户地址空间,那么MDL代表的这块虚拟地址必须是从属于某一个进程,这个成员指向从属进程的结构

    MappedSystemVa:该MDL结构对应的物理页面可能被映射到内核地址空间,这个成员代表这个内核地址空间下的虚拟地址。对MmBuildMdlForNonPagedPool的逆向表明,MappedSystemVa=StartVa+ByteOffset。这是因为这个函数的输入MDL,其StartVa是由ExAllocatePoolWithTag决定的,所以已经从内核空间到物理页面建立了映射,MappedSystemVa自然就可以这样算。 可以猜测,如果是调用MmProbeAndLockPages 返回,则MappedSystemVa不会与StartVa有这样的对应关系,因为此时对应的物理页面还没有被映射到内核空间。(此处未定,MmProbeAndLockPages 是否会到PDE与PTE中建立映射,未知。)

    StartVa:虚拟地址空间的首地址,当这块虚拟地址描述的是一个用户进程地址空间的一块时,这个地址从属于某一个进程。

    ByteCount:虚拟地址块的大小,字节数

    ByteOffset:StartVa+ByteCount等于缓冲区的开始地址


 由于WDK文档中表述得比较模糊,上面的说明有些仅仅是猜测。我们可以通过DBG调试一个驱动,观察它的内存原始数据来证实我们的推测。

下面是取自tdifw中的一段代码,它为一个IPV4地址ctx->tai 分配一个非分页内存块,然后调用IoAllocateMdl创建一个针对这个虚拟地址的MDL,最后调用MmBuildMdlForNonPagedPool来建立虚拟地址与物理页面直接的映射。

 ctx->tai = (TDI_ADDRESS_INFO *)malloc_np(TDI_ADDRESS_INFO_MAX);
 if (ctx->tai == NULL) {
  KdPrint(("[tdi_fw] tdi_create_addrobj_complete: malloc_np!\n"));

  status = STATUS_INSUFFICIENT_RESOURCES;
  goto done;
 }

可以看到tai的首地址是0x82040928,这是一个非分页内存中的虚拟地址,长度是0x55。

 mdl = IoAllocateMdl(ctx->tai, TDI_ADDRESS_INFO_MAX, FALSE, FALSE, NULL);
 if (mdl == NULL) {
  KdPrint(("[tdi_fw] tdi_create_addrobj_complete: IoAllocateMdl!\n"));
  
  status = STATUS_INSUFFICIENT_RESOURCES;
  goto done;
 } 

分配MDL之后的内存:

其中MDL的首地址是0x82010210,size是32,这是MDL结构本身的大小,mdlflags是8,mappedsystemva的值是0xf8c9oa9c

startva是0x82040000,这说明startva目前表示的是tai所指向的虚拟地址的页起始地址,bytecount是55,这代表了虚拟地址的大小,byteoffset是0x929,因此这个字节表示的是虚拟地址相对于页的偏移地址。
 MmBuildMdlForNonPagedPool(mdl);

调用此函数之后:

仅有3个字段发生了变化

mdlflags变成了12,mappedsystemva真正指向了虚拟地址,process被置0,说明这是一个非分页地址,它不属于任何一个进程的地址空间。

实际上size的大小并不等于MDL结构的大小,因为在MDL后面紧跟着一个表示物理页面的数组,只是没有在结构体中表现出来,这应该是为了避免一般的驱动程序直接修改这些物理数组,因为虚拟地址和物理页面的映射只应该由内存管理器来维护。在刚才的调试中观察内存发现,在MDL后面只有一个物理页面编号。此编号在调用IoAllocateMdl的时候并未初始化,而是在 MmBuildMdlForNonPagedPool(mdl)中被赋的值。有些人认为 MmBuildMdlForNonPagedPool是把物理页面映射到系统地址空间中,这种说法应该是错误的,因为对于非分页内存,在调用ExAllocatePool系列函数的时候,内存管理器就建立了映射关系,否则这些内存根本无法使用,实际上, MmBuildMdlForNonPagedPool的作用是把这种映射保存到MDL中,使其变得不透明,以满足某些驱动的需求,文章不宜太长,下一篇继续分析。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
信息系统分析与设计远程教育系统是为了满足不同地区学生的远程学习需求而设计的。在mdl文件中,我们可以找到系统的各种信息,包括远程教育系统的需求分析、系统设计、数据结构、系统功能模块等。需要在mdl文件中详细描述远程教育系统的各个模块的功能和关系。 在文件中,我们需要包含远程教育系统的需求分析,这将包括使用远程教育系统的学生和教师的需求,以及系统所满足的功能和性能需求。系统设计方面,需要包括系统的整体架构设计、模块设计和数据库设计等内容。此外,还应该包括系统的数据结构,包括数据库的设计和数据之间的关系。 另外,mdl文件还应该包含远程教育系统的功能模块,包括学生端和教师端的功能模块设计。比如,学生端应该包括课程学习、作业提交、在线测试等功能,教师端应该包括课程发布、批改作业、在线授课等功能。 在mdl文件中,还需要对系统的安全性、可维护性和可扩展性进行分析和设计。其中,安全性是指系统的数据和用户信息的安全保护,可维护性是指系统的易维护程度,可扩展性是指系统的灵活性和可扩展的能力。 总之,在信息系统分析与设计远程教育系统的mdl文件中,需要对系统的各个方面进行详细的描述和设计,以确保系统能够满足远程教育的需求并且具有良好的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值