zoukankan      html  css  js  c++  java
  • MDL数据结构

    微软的文档里对MDL的描述感觉语焉不详,这两天在找工作的间隙逆向+黑盒测试了一下MmBuildMdlForNonPagedPool,把得到的一些理解描述下来。

    一.MDL数据结构

        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;  

        各field的解释:

        Next:MDL可以连接成一个单链表,这在IRP的结构里能找到。具体是做什么用的参考对IRP的描述

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

        MdlFlags:与这个MDL相关的一些标记

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

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

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

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

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

    二.对MmBuildMdlForNonPagedPool的黑盒测试

       测试的程序主要执行如下步骤:

       1.用ExAllocatePoolWithTag在内核地址空间的NonpagedPool分配一块10000自己的区域

        2.用上述得到的地址和大小调用IoAllocateMdl,返回一个MDL

        3.打印该MDL个成员的值

        4.调用MmBuildMdlForNonPagedPool

        5.打印MDL各成员的值,比较与步骤3中的不同

        代码如下:

    1. #include "ntddk.h"  
    2. #include "wdm.h"  
    3. #include "ntdef.h"  
    4. #define BUF_LENGTH 10000  
    5. static void OutputMDL(PMDL pMDL);  
    6. static void Unload( IN PDRIVER_OBJECT pDriverObject);  
    7. NTSTATUS DriverEntry( IN PDRIVER_OBJECT  pDriverObject, IN PUNICODE_STRING RegistryPath )  
    8. {  
    9.     PVOID pBuf = NULL;  
    10.     PMDL pMDL;  
    11. //set up unload routing  
    12.     pDriverObject->DriverUnload = Unload;  
    13. //allocate memory from non-paged pool  
    14.     pBuf = ExAllocatePoolWithTag(NonPagedPool,BUF_LENGTH,(ULONG)DriverEntry);  
    15.     if(!pBuf) {  
    16.         DbgPrint("ExAllocatePoolWithTag failed./n");  
    17.         return STATUS_SUCCESS;  
    18.     }  
    19.     DbgPrint("MDL_TEST: pBuf=0x%08x/n",(ULONG)pBuf);  
    20. //allocate a MDL      
    21.     pMDL = IoAllocateMdl(pBuf,BUF_LENGTH,FALSE,FALSE,NULL);  
    22.     if(!pMDL) {  
    23.         DbgPrint("IoAllocateMdl failed./n");  
    24.         ExFreePoolWithTag(pBuf,(ULONG)DriverEntry);  
    25.         return STATUS_SUCCESS;  
    26.     }  
    27. //print MDL right after IoAllocateMdl  
    28.     OutputMDL(pMDL);  
    29. //  
    30.     DbgPrint("****************************************/n");  
    31. //call MmBuildMdlForNonPagedPool  
    32.     MmBuildMdlForNonPagedPool(pMDL);  
    33. //print MDL after MmBuildMdlForNonPagedPool is called  
    34.     OutputMDL(pMDL);  
    35. //return  
    36.     IoFreeMdl(pMDL);  
    37.     ExFreePoolWithTag(pBuf,(ULONG)DriverEntry);  
    38.     return STATUS_SUCCESS;  
    39. }  
    40. void Unload( IN PDRIVER_OBJECT pDriverObject)  
    41. {  
    42.     DbgPrint("MDL_TEST: Unloading. 88/n");  
    43. }  
    44. void OutputMDL(PMDL pMDL)  
    45. {  
    46.     int i;  
    47.     ULONG * p = (ULONG*)(pMDL+1);  
    48.       
    49.     DbgPrint("MDL_TEST: Size=%d/n",pMDL->Size);  
    50.     DbgPrint("MDL_TEST: MdlFlags=0x%04x/n",pMDL->MdlFlags);  
    51.     DbgPrint("MDL_TEST: Process=0x%08x/n",(ULONG)pMDL->Process);  
    52.     DbgPrint("MDL_TEST: MappedSystemVa=0x%08x/n",(ULONG)pMDL->MappedSystemVa);  
    53.     DbgPrint("MDL_TEST: StartVa=0x%08x/n",(ULONG)pMDL->StartVa);  
    54.     DbgPrint("MDL_TEST: ByteCount=%u/n",pMDL->ByteCount);  
    55.     DbgPrint("MDL_TEST: ByteOffset=%u/n",pMDL->ByteOffset);  
    56. //print a few 4-bytes after the MDL structure  
    57.     for(i=0;i<5;i++)  
    58.         DbgPrint("MDL_TEST: p[%d]=0x%08x/n",i,p[i]);  
    59.       
    60. }  

    执行的结果如下:

    1. MDL_TEST: pBuf=0xadc92000  
    2. MDL_TEST: Size=40  
    3. MDL_TEST: MdlFlags=0x0008  
    4. MDL_TEST: Process=0x87e85c88  
    5. MDL_TEST: MappedSystemVa=0x95fb1cc4  
    6. MDL_TEST: StartVa=0xadc92000  
    7. MDL_TEST: ByteCount=10000  
    8. MDL_TEST: ByteOffset=0  
    9. MDL_TEST: p[0]=0x0002d72f  
    10. MDL_TEST: p[1]=0x0002e2b0  
    11. MDL_TEST: p[2]=0x0007e15a  
    12. MDL_TEST: p[3]=0x0007e15b  
    13. MDL_TEST: p[4]=0x0007e15c  
    14. ****************************************  
    15. MDL_TEST: Size=40  
    16. MDL_TEST: MdlFlags=0x000c  
    17. MDL_TEST: Process=0x00000000  
    18. MDL_TEST: MappedSystemVa=0xadc92000  
    19. MDL_TEST: StartVa=0xadc92000  
    20. MDL_TEST: ByteCount=10000  
    21. MDL_TEST: ByteOffset=0  
    22. MDL_TEST: p[0]=0x0005bd23  
    23. MDL_TEST: p[1]=0x0005bea2  
    24. MDL_TEST: p[2]=0x0005bb21  
    25. MDL_TEST: p[3]=0x0007e15b  
    26. MDL_TEST: p[4]=0x0007e15c 

    对驱动程序采用Direct I/O方式进行数据读的测试

    采用这种方式进行读数据时,I/O Manager调用MmProbeAndLockPages将ReadFile参数提供的用户空间缓冲区对应的物理页面锁定为不可换出,然后将得到的 MDL放在Irp->MdlAddress里,将IRP传递给相应驱动程序的DispatchRead。根据Walter Oney在书中的描述,此时I/O Manager的行为可以用下面的代码来描述:

    1. KPROCESSOR_MODE mode;   // <== either KernelMode or UserMode  
    2. PMDL mdl = IoAllocateMdl(uva, length, FALSE, TRUE, Irp);  
    3. MmProbeAndLockPages(mdl, mode,  
    4.   reading ? IoWriteAccess : IoReadAccess);  
    5. <code to send and await IRP>  
    6. MmUnlockPages(mdl);  
    7. IoFreeMdl(mdl);  

    这里主要关注的地方是MmProbeAndLockPages有没有进行实际的虚拟地址的映射,即将物理页面映射到内核地址空间中。我们用下面的驱动代码来测试这一行为。

    1. NTSTATUS DispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)  
    2. {  
    3.     PVOID pSysAddr;  
    4.     PMDL pMDL = pIrp->MdlAddress;  
    5.     DbgPrint("******************DispatchRead******************/n");  
    6.     DbgPrint("Before MmGetSystemAddressForMdlSafe/n");  
    7.     OutputMDL(pMDL);  
    8.     pSysAddr = MmGetSystemAddressForMdlSafe(pMDL,LowPagePriority);  
    9.     if(!pSysAddr) {  
    10.         DbgPrint("MmGetSystemAddressForMdlSafe failed./n");  
    11.         return STATUS_SUCCESS;  
    12.     }  
    13.       
    14.     DbgPrint("After MmGetSystemAddressForMdlSafe/n");  
    15.     OutputMDL(pMDL);  
    16.     pIrp->IoStatus.Status = STATUS_SUCCESS;  
    17.     pIrp->IoStatus.Information = MmGetMdlByteCount(pMDL);  
    18.     IoCompleteRequest(pIrp,IO_NO_INCREMENT);  
    19.       
    20.     return STATUS_SUCCESS;  
    21. }  

    再写一个应用程序来发起一个读操作:

    1. void TestMDLDriver()  
    2. {  
    3.     HANDLE hDevice;  
    4.     BOOL bRet;  
    5.     DWORD dwRead;  
    6.     BYTE buf[10000] = {'S','Q','U','I'};  
    7.     hDevice = CreateFile(_T("////.//MDLTest"),GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);  
    8.     if(hDevice==INVALID_HANDLE_VALUE)   
    9.     {  
    10.         fprintf(stderr,"CreateFile error : %d/n",GetLastError());  
    11.         return;  
    12.     }  
    13. //issue a read request  
    14.     bRet = ReadFile(hDevice,buf,sizeof(buf),&dwRead,NULL);  
    15.     if(!bRet)  
    16.     {  
    17.         fprintf(stderr,"ReadFile error : %d/n",GetLastError());  
    18.         return;  
    19.     }  
    20.     printf("Read bytes:%d/n",dwRead);  
    21. //  
    22.     CloseHandle(hDevice);  
    23. }  

    导致的内核输出如下:

    1. 00000009    4.27463436  ******************DispatchRead******************  
    2. 00000010    4.27464771  Before MmGetSystemAddressForMdlSafe  
    3. 00000011    4.27465439  MDL_TEST: Size=40  
    4. 00000012    4.27466011  MDL_TEST: MdlFlags=0x008a  
    5. 00000013    4.27466583  MDL_TEST: Process=0x86ca7b58  
    6. 00000014    4.27467155  MDL_TEST: MappedSystemVa=0x92b1f000  
    7. 00000015    4.27467775  MDL_TEST: StartVa=0x001ad000  
    8. 00000016    4.27468348  MDL_TEST: ByteCount=10000  
    9. 00000017    4.27468824  MDL_TEST: ByteOffset=1148  
    10. 00000018    4.27469397  MDL_TEST: p[0]=0x00064429  
    11. 00000019    4.27469969  MDL_TEST: p[1]=0x000619fc  
    12. 00000020    4.27470541  MDL_TEST: p[2]=0x000618ee  
    13. 00000021    4.27471066  MDL_TEST: p[3]=0x00060749  
    14. 00000022    4.27471685  MDL_TEST: p[4]=0x86abca24  
    15. 00000023    4.27472448  After MmGetSystemAddressForMdlSafe  
    16. 00000024    4.27472973  MDL_TEST: Size=40  
    17. 00000025    4.27473545  MDL_TEST: MdlFlags=0x008b  
    18. 00000026    4.27474070  MDL_TEST: Process=0x86ca7b58  
    19. 00000027    4.27474689  MDL_TEST: MappedSystemVa=0xb01e747c  
    20. 00000028    4.27475214  MDL_TEST: StartVa=0x001ad000  
    21. 00000029    4.27475786  MDL_TEST: ByteCount=10000  
    22. 00000030    4.27476311  MDL_TEST: ByteOffset=1148  
    23. 00000031    4.27476835  MDL_TEST: p[0]=0x00064429  
    24. 00000032    4.27477455  MDL_TEST: p[1]=0x000619fc  
    25. 00000033    4.27477980  MDL_TEST: p[2]=0x000618ee  
    26. 00000034    4.27478504  MDL_TEST: p[3]=0x00060749  
    27. 00000035    4.27479029  MDL_TEST: p[4]=0x86abca24  

    此时从VS的调试器中看到,应用程序中buf[10000]的地址为0x001ad47c

    从输出可以得到如下结论:

    1.MmProbeAndLockPages并不将物理页面映射到内核地址空间,而仅锁定物理页面;MappedSystemVa的变化可以显示这一点

    2.buf的地址=StartVa+ByteOffset;

    3.MmGetSystemAddressForMdlSafe进行实际的映射操作,并设置MdlFlags的MDL_MAPPED_TO_SYSTEM_VA标志。

    4.MmProbeAndLockPages将MdlFlags=MDL_WRITE_OPERATION | MDL_ALLOCATED_FIXED_SIZE | MDL_PAGES_LOCKED

  • 相关阅读:
    Sample XPS Documents Download
    触发器中的inserted表和deleted表
    Using freewheel.labs.autodesk.com to auto generate preview images of DWF files on your web site
    解除SQL对组件"Ad Hoc Distributed Queries"的"STATEMENT'OpenRowset OpenDatasource"的访问
    读写xps
    XPS文件,在Windows XP下的打开查看阅读和打印方法。
    Learning to Reference Inserted and Deleted Tables
    Get value from updated, inserted and deleted
    Reinstall Microsoft Helper Viewer
    如何查找文件的IFilter
  • 原文地址:https://www.cnblogs.com/sideny/p/3295831.html
Copyright © 2011-2022 走看看