Advertisement

浅谈MDL(壹)(Windows内核学习笔记)

阅读量:

内存描述符表(MDL)是一种用于在内核态管理内存映射的结构体。它通过物理页帧号数组描述虚拟内存范围,并支持锁定特定页面以避免不可预测的地址映射问题。MDL在Win7和Windows 10中实现了类似的功能,但细节略有不同。使用MDL可以解决线程调度导致的页面不可交换问题,并支持对用户态或内核态虚拟地址进行映射。尽管建立和撤销MDL有一定开销,但对于较大缓冲区来说是有效的方法之一。相关函数包括IoAllocateMdl用于分配并初始化MDL,以及MmInitializeMdl用于配置其属性等。
当爱和责任合二为一,则恩典便与你同在。

先来了解下MDL是什么吧。查看MSDN官方定义:

复制代码
    //Win10下的定义
    //0x1c bytes (sizeof)
    struct _MDL
    {
    struct _MDL* Next;     		//0x0	MDL队列中的下一个成员
    SHORT Size;            		//0x4	整个MDL列表的长度,包括MDL结构体和物理页帧号所占的内存 物理页帧号起始地址紧跟在结构体后
    SHORT MdlFlags;        		//0x6	标志,设置一些内存属性
    struct _EPROCESS* Process;	//0x8	缓冲区所属进程
    VOID* MappedSystemVa;    	//0xc	映射之后的系统空间虚拟地址
    VOID* StartVa;       	 	//0x10	缓冲区所在第一个页面的虚拟地址
    ULONG ByteCount;       		//0x14	字节数,虚拟地址的大小
    ULONG ByteOffset;     	 	//0x18	StartVa + ByteOffset缓冲区开始的地址
    }; 
    //Win7的Windbg版本
    0: kd> dt nt!_MDL
       +0x000 Next             : Ptr32 _MDL
       +0x004 Size             : Int2B
       +0x006 MdlFlags         : Int2B
       +0x008 Process          : Ptr32 _EPROCESS
       +0x00c MappedSystemVa   : Ptr32 Void
       +0x010 StartVa          : Ptr32 Void
       +0x014 ByteCount        : Uint4B
       +0x018 ByteOffset       : Uint4B

好像在Win7和Win下的定义,并没有什么变化。。。
为什么要使用MDL?
我们都知道在内核中运行的代码,可以访问用户层的内存空间,但是当线程调度发生,当前的页面映射表不再是之前的用户层进程的页面映射,但是CPU仍然访问原本的用户层虚拟地址,就会发生不可预知的错误,因为用户层的低2G虚拟空间空间是独立的。也就是说,我们访问到的地址可能并不是真正的目标地址。解决办法之一,就是使用MDL映射一份对应的物理内存到系统空间的虚拟地址中来,并且被MDL锁定之后,页面不会被换出。MDL只能在内核态使用,它可以指定对内核虚拟地址或者用户虚拟地址的映射。
接下来说一下MDL的使用:
对于很小的缓冲区来说,使用MDL不太划算,毕竟建立和撤销一个新的映射需要一定的开销,对于大一点的缓冲区可以使用。
之前我说过的一个代码,用的是内核态映射用户态的虚拟地址,使用MDL进行缓冲区数据拷贝。看一些常用的MDL操作函数:

复制代码
    //该宏未初始化MappedSystemVa,因为还没有进行映射
    #define MmInitializeMdl(_MemoryDescriptorList, \
                        _BaseVa, \
                        _Length) \
    { \
      (_MemoryDescriptorList)->Next = (PMDL) NULL; \
      (_MemoryDescriptorList)->Size = (CSHORT) (sizeof(MDL) + \
    (sizeof(PFN_NUMBER) * ADDRESS_AND_SIZE_TO_SPAN_PAGES(_BaseVa, _Length))); \
      (_MemoryDescriptorList)->MdlFlags = 0; \
      (_MemoryDescriptorList)->StartVa = (PVOID) PAGE_ALIGN(_BaseVa); \
      (_MemoryDescriptorList)->ByteOffset = BYTE_OFFSET(_BaseVa); \
      (_MemoryDescriptorList)->ByteCount = (ULONG) _Length; \
    }
    PMDL
    NTAPI
    IoAllocateMdl(IN PVOID VirtualAddress,
              IN ULONG Length,
              IN BOOLEAN SecondaryBuffer,
              IN BOOLEAN ChargeQuota,
              IN PIRP Irp)
    {
    PMDL Mdl = NULL, p;
    ULONG Flags = 0;
    ULONG Size;
    
    /*断言长度有效*/
    ASSERT(Length != 0);
    
    /*超过地址空间的一半,肯定无法映射,因为MDL存在于内核中,且不能被换出*/
    if (Length & 0x80000000) return NULL;
    
    /*计算缓冲区跨过的页面数量*/
    Size = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, Length);
    if (Size > 23)
    {
        /*超过23个页面,根据实际大小估算MDL的大小*/
        Size *= sizeof(PFN_NUMBER);//sizeof(PFN_NUMBER)物理页帧号的大小
        Size += sizeof(MDL);
        if (Size > MAXUSHORT) return NULL;
    }
    else
    {
        /*不超过23,按照标准大小23计算大小*/
        Size = (23 * sizeof(PFN_NUMBER)) + sizeof(MDL);
        Flags |= MDL_ALLOCATED_FIXED_SIZE;
    
        /*Lookaside防止内存空洞,相当于一个内存管理器一样*/
        Mdl = IopAllocateMdlFromLookaside(LookasideMdlList);//现成的数据结构分配内存,效率较高
    }
    
    /**/
    if (!Mdl)
    {
        /*若失败,非分页内存池中申请内存*/
        Mdl = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MDL);
        if (!Mdl) return NULL;
    }
    
    /*初始化MDL*/
    MmInitializeMdl(Mdl, VirtualAddress, Length);
    Mdl->MdlFlags |= Flags;
    
    /* Check if an IRP was given too */
    if (Irp)
    {
        /* Check if it came with a secondary buffer */
        if (SecondaryBuffer)
        {
            /* Insert the MDL at the end */
            p = Irp->MdlAddress;
            while (p->Next) p = p->Next;
            p->Next = Mdl;
      }
      else
      {
            /* Otherwise, insert it directly */
            Irp->MdlAddress = Mdl;
      }
       }
    
    /* Return the allocated mdl */
    return Mdl;
    }

当前篇幅较为冗长, 建议下次再深入分析剩余的经典MDL功能. 具体包括以下几点: MmProbeAndLockPages, MmMapLockedPagesSpecifyCache, MmUnmapLockedPages, MmUnlockPages, IoFreeMdl等.

当爱与责任融为一体时,则恩典将与您共存.”

参考书籍: 《Windows内核情景分析》

全部评论 (0)

还没有任何评论哟~