使用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;
}