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

 

 
posted @ 2022-03-28 21:07  紅人  阅读(447)  评论(0编辑  收藏  举报