IoAllocateMdl函数逆向分析
0x00前言
主要分析mdl结构体创建和应用
0x01分析
函数原型
PMDL __stdcall IoAllocateMdl(
PVOID VirtualAddress,//指向 MDL 要描述的缓冲区的基虚拟地址的指针。
ULONG Length,//指定 MDL 要描述的缓冲区的长度(以字节为单位)
BOOLEAN SecondaryBuffer,//指示缓冲区是主缓冲区还是辅助缓冲区
BOOLEAN ChargeQuota,//预留给系统使用
PIRP Irp)//指向要与 MDL 关联的 IRP 的指针
IoAllocateMdl 返回指向 MDL 的指针;如果无法分配 MDL,则返回 NULL。
PMDL的返回结构
TYPEDEF struct _MDL{ STRUCT _MDL *NEXT;//下一个节点_INT64 short int Size;//长度 short int MdlFlags;//flag int Processor//进程环境 int64 StartVa ulong ByteCount ulong ByteOffset }MDL,PMDL
Next:MDL可以连接成一个单链表,因此可以将分散的虚拟机地址串接起来
Size:整个MDL列表的长度,MDL只是整个列表的头部,后面跟着一块内存,整个MDL列表则是对于一个缓冲区页面的描述。
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等于缓冲区的开始地址
PMDL __stdcall IoAllocateMdl( PVOID VirtualAddress, ULONG Length, BOOLEAN SecondaryBuffer, BOOLEAN ChargeQuota, PIRP Irp) { __int16 v5; // si __int16 v8; // r15 unsigned __int64 v9; // rbx struct _KPRCB *CurrentPrcb; // r14 _GENERAL_LOOKASIDE *P; // rbp PMDL result; // rax unsigned int Number; // ecx _GENERAL_LOOKASIDE *L; // rbp __int64 Size; // rdx void *(__fastcall *AllocateEx)(_POOL_TYPE, unsigned __int64, unsigned int, _LOOKASIDE_LIST_EX *); // rax __int64 Tag; // r8 __int64 Type; // rcx unsigned int v19; // eax _MDL *MdlAddress; // rcx _MDL *i; // rdx v5 = (__int16)VirtualAddress; v8 = 0; v9 = (((unsigned __int16)VirtualAddress & 0xFFF) + (unsigned __int64)Length + 4095) >> 12; if ( (unsigned int)v9 > 0x11 ) // 大于就按正常大小申请 { v19 = 8 * v9 + 48; } else { v8 = 8; CurrentPrcb = KeGetCurrentPrcb(); P = CurrentPrcb->PPLookasideList[3].P; ++P->TotalAllocates; result = (PMDL)RtlpInterlockedPopEntrySList(&P->ListHead); if ( result ) goto LABEL_3; ++P->AllocateMisses; L = CurrentPrcb->PPLookasideList[3].L; ++L->TotalAllocates; result = (PMDL)RtlpInterlockedPopEntrySList(&L->ListHead); if ( result || (Size = L->Size, AllocateEx = L->AllocateEx, Tag = L->Tag, Type = (unsigned int)L->Type, ++L->AllocateMisses, (result = (PMDL)((__int64 (__fastcall *)(__int64, __int64, __int64))AllocateEx)(Type, Size, Tag)) != 0i64) ) { LABEL_3: LODWORD(result->Next) = CurrentPrcb->Number; } if ( result ) { LOWORD(Number) = result->Next; goto LABEL_6; } v19 = 184; // 小于0x11时实际大小 } result = (PMDL)ExAllocatePoolWithTag(NonPagedPoolNx, v19, 0x206C644Du);// 申请内存 if ( !result ) return result; Number = KeGetPcr()->Prcb.Number; LABEL_6: result->AllocationProcessorNumber = Number; result->Next = 0i64; result->Size = 8 * (v9 + 6); result->StartVa = (void *)((unsigned __int64)VirtualAddress & 0xFFFFFFFFFFFFF000ui64); result->ByteOffset = v5 & 0xFFF; result->ByteCount = Length; result->MdlFlags = v8; if ( Irp ) // 返回irp值 { if ( SecondaryBuffer ) { MdlAddress = Irp->MdlAddress; for ( i = MdlAddress->Next; i; i = i->Next ) MdlAddress = i; MdlAddress->Next = result; } else { Irp->MdlAddress = result; } } return result; }