MDL解析

使用MDL映射非分页内存

PMDL MdlForFunc;
	PVOID Msct;
	KIRQL f_oldirql;
	MdlForFunc = MmCreateMdl(NULL, HookData.HookAddress, HookData.HookLenth);
	if (MdlForFunc)
	{
		MmBuildMdlForNonPagedPool(MdlForFunc);
		MdlForFunc->MdlFlags = MdlForFunc->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
		__try
		{
			MmProbeAndLockPages(MdlForFunc, KernelMode, IoWriteAccess);
		}
		__except (EXCEPTION_EXECUTE_HANDLER)
		{
			IoFreeMdl(MdlForFunc);
			ExFreePool(HookData.OldData);
			return;
		}
		Msct = MmMapLockedPagesSpecifyCache(MdlForFunc, KernelMode, MmWriteCombined, NULL, FALSE, 0);

		KIRQL Irql = KeRaiseIrqlToDpcLevel();
		//do something
		KeLowerIrql(Irql);
		MmUnmapLockedPages(Msct, MdlForFunc);
		MmUnlockPages(MdlForFunc);
		IoFreeMdl(MdlForFunc);
	}

映射分页内存

PMDL mdl;
// 驱动卸载的回调函数,会在卸载的时候被调用
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	//释放MDL
	IoFreeMdl(mdl);
	UNREFERENCED_PARAMETER(DriverObject);
}

// 2. 提供驱动程序的入口函数
NTSTATUS DriverEntry(
	PDRIVER_OBJECT DriverObject,		// 驱动对象,类似实例句柄
	PUNICODE_STRING RegistryPath)		// 字符串指针,保存驱动在注册表的路径
{

	

	//设置回调函数
	DriverObject->DriverUnload = DriverUnload;


	// 创建一个指针指向常量字符串
	char source[0x4096] = { 0 };

	//创建一个souce的MDL  只映射0x1000字节
	mdl = IoAllocateMdl(source, 0x1000, FALSE, FALSE, NULL);


	// 对于分页内存,需要使用下面的函数锁定内存不会被换出
	// 对于非分页内存,需要调用 MmBuildMdlForNonPagedPool 建立描述符
	// 锁定MDL
	MmProbeAndLockPages(mdl, KernelMode, IoModifyAccess);

	// 1. 需要进行重新映射的 MDL 结构,必须是绑定好了的
	// 2. 重新映射的虚拟地址位于什么空间
	// 3. 表示缓存的级别,允许 CPU 进行缓存
	// 4. 想要映射到哪个地址,NULL 表示不指定
	// 5. 如果映射失败,是否蓝屏
	// 6. 当前映射的级别,通常使用普通级别

	//调用MmMapLockedPagesSpecifyCache后,标志位mdl->MdlFlags = mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA.;
	//有此标志位后,MmGetSystemAddressForMdlSafe就是直接取mdl->MappedSystemVa,如果没有该标志那么就会调用MmMapLockedPagesSpecifyCache.
	char* dest = MmMapLockedPagesSpecifyCache(mdl,
		KernelMode,
		MmCached,
		NULL,
		FALSE,
		NormalPagePriority);

	dest[0] = 'C';
	DbgBreakPoint();

	/*

1: kd> ??mdl



struct _MDL * 0xffffc789`c4a98100
   +0x000 Next             : (null)
   +0x008 Size             : 0n64     v9 = (((unsigned __int16)VirtualAddress & 0xFFF) + (unsigned __int64)Length + 4095) >> 12;
									  Size = 8 * (v9 + 6);   Size值有什么用?


   +0x00a MdlFlags         : 0n139
   +0x010 Process          : (null)

   +0x018 MappedSystemVa   : 0xffff8f81`dbb40830 Void    用于保存映射后的虚拟地址指针,映射时会分配一个虚拟内存,
														 这个虚拟内存会和被映射内存使用同一物理页。

   ---------------------------   以下字段记录被映射内存的相关信息       --------------
   +0x020 StartVa          : 0xfffffc08`58af7000 Void    被映射的虚拟地址的开始页
   +0x028 ByteCount        : 0x1000		                 总共映射的字节数   后面使用的内存不能超过这个值+被映射地址							 
   +0x02c ByteOffset       : 0x830						 被映射的虚拟地址的页内偏移   StartVa+ByteOffset就是被映射地址  



1: kd> ??dest
char * 0xffff8f81`dbb40830
 "C"
1: kd> ??source
char [16534] 0xfffffc08`58af7830
67 'C'

   */

	KdPrint(("%S\n", dest));
	//取消映射-取消锁定
	MmUnmapLockedPages(dest, mdl);
	MmUnlockPages(mdl);

	// 3. 必须需要提供一个返回值,如果返回成功
	//	那么就会加载成功,其他值都是加载失败
	return STATUS_SUCCESS;
}

运行结果得出: mdl的 StartVa + ByteOffset =buffer 映射的buffer地址.
调用函数MmMapLockedPagesSpecifyCache取得映射的地址buffer2 等于MappedSystemVa.

总结:
实际上MDL映射是系统申请了一块我们要映射大小的内存,我们操作的是映射后的内存.
这块内存跟被映射的内存使用的是同一个物理页

建立映射后的mdl

1: kd> ??mdl
struct _MDL * 0xffffbe8f`e412c940
   +0x000 Next             : (null) 
   +0x008 Size             : 0n88
   +0x00a MdlFlags         : 0n139
   +0x010 Process          : (null) 
   +0x018 MappedSystemVa   : 0xffffd181`c6bf5810 Void	 //映射后的虚拟地址	
   +0x020 StartVa          : 0xffffa883`8f349000 Void    //被映射的虚拟地址
   +0x028 ByteCount        : 0x4096						 //映射的大小	
   +0x02c ByteOffset       : 0x810
1: kd> dq 0xffffbe8f`e412c940 l 20
ffffbe8f`e412c940  00000000`00000000 00000000`008b0058
ffffbe8f`e412c950  00000000`00000000 ffffd181`c6bf5810
ffffbe8f`e412c960  ffffa883`8f349000 00000810`00004096
ffffbe8f`e412c970  00000000`000028ff 00000000`000028fe 		//大小是0x4096  需要5个物理页,MDL结构的后面就记录了这5个物理页
ffffbe8f`e412c980  00000000`000013fd 00000000`000028fc
ffffbe8f`e412c990  00000000`000028fb 00000000`00000000      //物理页结束
ffffbe8f`e412c9a0  00000000`00000000 00000000`00000000
ffffbe8f`e412c9b0  00000000`00000000 00000000`00000000
ffffbe8f`e412c9c0  00000000`00000000 00000000`00000000

在这里插入图片描述

那就可以得出真正的MDL结构其实是这样的

typedef struct _MDL {
    struct _MDL *Next;
    CSHORT Size;
    CSHORT MdlFlags;

    struct _EPROCESS *Process;
    PVOID MappedSystemVa;   /* see creators for field size annotations. */
    PVOID StartVa;   /* see creators for validity; could be address 0.  */
    ULONG ByteCount;
    ULONG ByteOffset;
    PPFN_NUMBER pfnNumber[1];  //该数组大小=ByteCount/0x1000+1
} MDL, *PMDL;

			

映射3环地址

BOOLEAN MapSection()
{

	OBJECT_ATTRIBUTES ob = { 0 };
	ob.Length = 0x30;
	ob.Attributes = OBJ_KERNEL_HANDLE;

	LARGE_INTEGER MaxSize;
	MaxSize.QuadPart = 0x1000;
	HANDLE SectionHandle = NULL;
	if (!ZwCreateSection(&SectionHandle, SECTION_MAP_READ | SECTION_QUERY, &ob, &MaxSize, PAGE_READONLY, 0x8000000, 0))
	{
		ULONG_PTR ViewSize;
		PVOID BaseAddress;
		BaseAddress = 0;
		ViewSize = 0x1000;
		if (!ZwMapViewOfSection(
			SectionHandle,
			(HANDLE)0xFFFFFFFFFFFFFFFF,
			&BaseAddress,   //三环地址
			0,
			0x1000,
			0,
			&ViewSize,
			ViewUnmap,
			0,
			PAGE_READONLY))
		{
			//pMdl在新分配的内存里面
			MDL Mdl = {0};
			Mdl.Next = NULL;
			Mdl.Size = 8 * ((((ULONG64)BaseAddress & 0xfff + 4095) >> 12) + 7);
			Mdl.MdlFlags = 0;
			Mdl.MappedSystemVa = NULL;
			Mdl.StartVa = (ULONG64)BaseAddress & 0xFFFFFFFFFFFFF000;  
			Mdl.ByteCount = 0x1000;
			Mdl.ByteOffset = (ULONG64)BaseAddress & 0xfff;

			__try
			{
				MmProbeAndLockPages(&Mdl, UserMode, IoReadAccess);
			}
			__except (1)
			{
				//锁定内存失败
				ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFF, BaseAddress);
				ZwClose(SectionHandle);
				return FALSE;
			}

			PVOID MapBuf = MmMapLockedPagesSpecifyCache(&Mdl, 0, MmCached, 0, 0, NormalPagePriority);
			if (MapBuf)
			{
				//把应用层内存映射到内核中  MappedSystemVa字段就是MapBuf
			}
			MmUnlockPages(&Mdl);
			ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFF, BaseAddress);
		}
	}

	if (SectionHandle)
	{
		ZwClose(SectionHandle);
		SectionHandle = NULL;
	}
	return TRUE;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值