看流星社区

 找回密码
 注册账号
查看: 2193|回复: 0

关于MDL的一些事情

[复制链接]

该用户从未签到

发表于 2017-6-1 17:25:37 | 显示全部楼层 |阅读模式
微软的文档里对MDL的描述感觉语焉不详,这两天在找工作的间隙逆向+黑盒测试了一下MmBuildMdlForNonPagedPool,把得到的一些理解描述下来。

一.MDL数据结构

MDL是用来建立一块虚拟地址空间与物理页面之间的映射,结构定义如下:





[cpp]view
plaincopy






typedefstruct_MDL{
struct_MDL*Next;
CSHORTSize;
CSHORTMdlFlags;
struct_EPROCESS*Process;
PVOIDMappedSystemVa;
PVOIDStartVa;
ULONGByteCount;
ULONGByteOffset;
}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中的不同

代码如下:



[cpp]view
plaincopy






#include"ntddk.h"
#include"wdm.h"
#include"ntdef.h"
#defineBUF_LENGTH10000
staticvoidOutputMDL(PMDLpMDL);
staticvoidUnload(INPDRIVER_OBJECTpDriverObject);
NTSTATUSDriverEntry(INPDRIVER_OBJECTpDriverObject,INPUNICODE_STRINGRegistryPath)
{
PVOIDpBuf=NULL;
PMDLpMDL;
//setupunloadrouting
pDriverObject->DriverUnload=Unload;
//allocatememoryfromnon-pagedpool
pBuf=ExAllocatePoolWithTag(NonPagedPool,BUF_LENGTH,(ULONG)DriverEntry);
if(!pBuf){
DbgPrint("ExAllocatePoolWithTagfailed./n");
returnSTATUS_SUCCESS;
}
DbgPrint("MDL_TEST:pBuf=0x%08x/n",(ULONG)pBuf);
//allocateaMDL
pMDL=IoAllocateMdl(pBuf,BUF_LENGTH,FALSE,FALSE,NULL);
if(!pMDL){
DbgPrint("IoAllocateMdlfailed./n");
ExFreePoolWithTag(pBuf,(ULONG)DriverEntry);
returnSTATUS_SUCCESS;
}
//printMDLrightafterIoAllocateMdl
OutputMDL(pMDL);
//
DbgPrint("****************************************/n");
//callMmBuildMdlForNonPagedPool
MmBuildMdlForNonPagedPool(pMDL);
//printMDLafterMmBuildMdlForNonPagedPooliscalled
OutputMDL(pMDL);
//return
IoFreeMdl(pMDL);
ExFreePoolWithTag(pBuf,(ULONG)DriverEntry);
returnSTATUS_SUCCESS;
}
voidUnload(INPDRIVER_OBJECTpDriverObject)
{
DbgPrint("MDL_TEST:Unloading.88/n");
}
voidOutputMDL(PMDLpMDL)
{
inti;
ULONG*p=(ULONG*)(pMDL+1);

DbgPrint("MDL_TEST:Size=%d/n",pMDL->Size);
DbgPrint("MDL_TEST:MdlFlags=0x%04x/n",pMDL->MdlFlags);
DbgPrint("MDL_TESTrocess=0x%08x/n",(ULONG)pMDL->rocess);
DbgPrint("MDL_TEST:MappedSystemVa=0x%08x/n",(ULONG)pMDL->MappedSystemVa);
DbgPrint("MDL_TEST:StartVa=0x%08x/n",(ULONG)pMDL->StartVa);
DbgPrint("MDL_TEST:ByteCount=%u/n",pMDL->ByteCount);
DbgPrint("MDL_TEST:ByteOffset=%u/n",pMDL->ByteOffset);
//printafew4-bytesaftertheMDLstructure
for(i=0;i<5;i++)
DbgPrint("MDL_TEST:p[%d]=0x%08x/n",i,p[i]);

}




执行的结果如下:



[c-sharp]view
plaincopy






MDL_TEST:pBuf=0xadc92000
MDL_TEST:Size=40
MDL_TEST:MdlFlags=0x0008
MDL_TESTrocess=0x87e85c88
MDL_TEST:MappedSystemVa=0x95fb1cc4
MDL_TEST:StartVa=0xadc92000
MDL_TEST:ByteCount=10000
MDL_TEST:ByteOffset=0
MDL_TEST:p[0]=0x0002d72f
MDL_TEST:p[1]=0x0002e2b0
MDL_TEST:p[2]=0x0007e15a
MDL_TEST:p[3]=0x0007e15b
MDL_TEST:p[4]=0x0007e15c
****************************************
MDL_TEST:Size=40
MDL_TEST:MdlFlags=0x000c
MDL_TESTrocess=0x00000000
MDL_TEST:MappedSystemVa=0xadc92000
MDL_TEST:StartVa=0xadc92000
MDL_TEST:ByteCount=10000
MDL_TEST:ByteOffset=0
MDL_TEST:p[0]=0x0005bd23
MDL_TEST:p[1]=0x0005bea2
MDL_TEST:p[2]=0x0005bb21
MDL_TEST:p[3]=0x0007e15b
MDL_TEST:p[4]=0x0007e15c





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



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



[c-sharp]view plaincopy

KPROCESSOR_MODEmode;//<==eitherKernelModeorUserModePMDLmdl=IoAllocateMdl(uva,length,FALSE,TRUE,Irp);MmProbeAndLockPages(mdl,mode,reading?IoWriteAccess:IoReadAccess);<codetosendandawaitIRP>MmUnlockPages(mdl);IoFreeMdl(mdl);


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



[c-sharp]view plaincopy

NTSTATUSDispatchRead(INPDEVICE_OBJECTpDeviceObject,INPIRPpIrp){PVOIDpSysAddrMDLpMDL=pIrp->MdlAddress;DbgPrint("******************DispatchRead******************/n");DbgPrint("BeforeMmGetSystemAddressForMdlSafe/n");OutputMDL(pMDL);pSysAddr=MmGetSystemAddressForMdlSafe(pMDL,LowPagePriority);if(!pSysAddr){DbgPrint("MmGetSystemAddressForMdlSafefailed./n");returnSTATUS_SUCCESS;}DbgPrint("AfterMmGetSystemAddressForMdlSafe/n");OutputMDL(pMDL);pIrp->IoStatus.Status=STATUS_SUCCESS;pIrp->IoStatus.Information=MmGetMdlByteCount(pMDL);IoCompleteRequest(pIrp,IO_NO_INCREMENT);returnSTATUS_SUCCESS;}


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



[c-sharp]view plaincopy

voidTestMDLDriver(){HANDLEhDevice;BOOLbRet;DWORDdwRead;BYTEbuf[10000]={'S','Q','U','I'};hDevice=CreateFile(_T("////.//MDLTest"),GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hDevice==INVALID_HANDLE_VALUE){fprintf(stderr,"CreateFileerror:%d/n",GetLastError());return;}//issueareadrequestbRet=ReadFile(hDevice,buf,sizeof(buf),&amp;dwRead,NULL);if(!bRet){fprintf(stderr,"ReadFileerror:%d/n",GetLastError());return;}printf("Readbytes:%d/n",dwRead);//CloseHandle(hDevice);}


导致的内核输出如下:



[cpp]view plaincopy

000000094.27463436******************DispatchRead******************000000104.27464771BeforeMmGetSystemAddressForMdlSafe000000114.27465439MDL_TEST:Size=40000000124.27466011MDL_TEST:MdlFlags=0x008a000000134.27466583MDL_TESTrocess=0x86ca7b58000000144.27467155MDL_TEST:MappedSystemVa=0x92b1f000000000154.27467775MDL_TEST:StartVa=0x001ad000000000164.27468348MDL_TEST:ByteCount=10000000000174.27468824MDL_TEST:ByteOffset=1148000000184.27469397MDL_TEST:p[0]=0x00064429000000194.27469969MDL_TEST:p[1]=0x000619fc000000204.27470541MDL_TEST:p[2]=0x000618ee000000214.27471066MDL_TEST:p[3]=0x00060749000000224.27471685MDL_TEST:p[4]=0x86abca24000000234.27472448AfterMmGetSystemAddressForMdlSafe000000244.27472973MDL_TEST:Size=40000000254.27473545MDL_TEST:MdlFlags=0x008b000000264.27474070MDL_TESTrocess=0x86ca7b58000000274.27474689MDL_TEST:MappedSystemVa=0xb01e747c000000284.27475214MDL_TEST:StartVa=0x001ad000000000294.27475786MDL_TEST:ByteCount=10000000000304.27476311MDL_TEST:ByteOffset=1148000000314.27476835MDL_TEST:p[0]=0x00064429000000324.27477455MDL_TEST:p[1]=0x000619fc000000334.27477980MDL_TEST:p[2]=0x000618ee000000344.27478504MDL_TEST:p[3]=0x00060749000000354.27479029MDL_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
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 15:06

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表