近期在研究和开发基于虚拟化的虚拟 HOOK 技术。在 Windows 7 x64 环境开发实测期间,发现针对 NtCreateThreadEx 函数的 HOOK 存在问题:该函数大部分情况下变得只返回 0xC00000F2 (STATUS_INVALID_PARAMETER_4) 第 4 个参数无效的状态码。这导致系统出现很多问题,大部分的新线程都无法成功创建。为了解决这个问题,在这篇文章中对问题进行追溯,查找到底是哪里导致的。

经过追踪:发现位于在 NtAllocateVirtualMemory 中如下位置赋值了提到的错误状态码:

kd> p
nt! ?? ::NNGAKEGL::`string'+0x47703:
fffff800`041e171e b8f20000c0      mov     eax,0C00000F2h

调用栈如下:

kd> k
 # Child-SP          RetAddr           Call Site
00 fffff880`028e7470 fffff800`03e918d3 nt! ?? ::NNGAKEGL::`string'+0x47703
01 fffff880`028e7610 fffff800`03e8de70 nt!KiSystemServiceCopyEnd+0x13
02 fffff880`028e7818 fffff800`04180a63 nt!KiServiceLinkage
03 fffff880`028e7820 fffff800`03e918d3 nt!NtSetInformationProcess+0x4c6
04 fffff880`028e7b70 fffff800`03e8de70 nt!KiSystemServiceCopyEnd+0x13
05 fffff880`028e7d08 fffff800`04180226 nt!KiServiceLinkage
06 fffff880`028e7d10 fffff800`0417e84d nt!RtlCreateUserStack+0x122
07 fffff880`028e7e00 fffff800`0417e47e nt!PspAllocateThread+0x299
08 fffff880`028e8020 fffff800`04182385 nt!PspCreateThread+0x1d2
09 fffff880`028e82a0 fffff880`03ea22ee nt!NtCreateThreadEx+0x25d
0a fffff880`028e89f0 fffff800`03e918d3 DetectModule!Handle_SSDT_NtCreateThreadEx+0x9e 
0b fffff880`028e8a70 00000000`76e51d9a nt!KiSystemServiceCopyEnd+0x13
0c 00000000`0366af18 000007fe`fd01b4a3 ntdll!NtCreateThreadEx+0xa
0d 00000000`0366af20 00000000`76bf65b6 KernelBase!CreateRemoteThreadEx+0x163
0e 00000000`0366b3e0 000007fe`f57f7186 kernel32!CreateThreadStub+0x36
0f 00000000`0366b430 00000000`00000000 0x000007fe`f57f7186

具体的调用链:

NtCreateThreadEx > PspCreateThread > PspAllocateThread > RtlCreateUserStack > ZwSetInformationProcess > NtSetInformationProcess > ZwAllocateVirtualMemory > NtAllocateVirtualMemory

最终在 NtAllocateVirtualMemory 函数中因其第4个参数的问题导致了 0xC00000F2 的状态码。报错误码的指令前面的 10 条执行指令如下:

eb05            jmp     nt!NtAllocateVirtualMemory+0x119 (fffff800`04189da9)
488b056092f3ff  mov     rax,qword ptr [nt!MmHighestUserAddress (fffff800`040c3010)]
48050000ffff    add     rax,0FFFFFFFFFFFF0000h
483bd8          cmp     rbx,rax
0f8755790500    ja      nt! ?? ::NNGAKEGL::`string'+0x476f9 (fffff800`041e1714)
488b054a92f3ff  mov     rax,qword ptr [nt!MmHighestUserAddress (fffff800`040c3010)]
482bc3          sub     rax,rbx
482dffff0000    sub     rax,0FFFFh
493bc7          cmp     rax,r15
0f8246790500    jb      nt! ?? ::NNGAKEGL::`string'+0x47703 (fffff800`041e171e)
b8f20000c0      mov     eax,0C00000F2h

通过 IDA Hex 插件查看:

if ( v147 > MmHighestUserAddress - 0x10000 )
    return 0xC00000F0i64;
if ( MmHighestUserAddress - v147 - 0xFFFF < v19 )
    return 0xC00000F2i64;

在上面的第二个 if 判断命中条件,返回了 0xC00000F2 状态码。

在 Windbg 中捕获的该函数的指令执行路径如下,在分析时由于篇幅关系,在下面的指令片段中均把机器码左侧 64 位地址的高 32 位隐去:

nt!NtAllocateVirtualMemory:
`04189c90 4c894c2420      mov     qword ptr [rsp+20h],r9
`04189c95 4c89442418      mov     qword ptr [rsp+18h],r8
`04189c9a 4889542410      mov     qword ptr [rsp+10h],rdx
`04189c9f 48894c2408      mov     qword ptr [rsp+8],rcx
`04189ca4 53              push    rbx
`04189ca5 56              push    rsi
`04189ca6 57              push    rdi
`04189ca7 4154            push    r12
`04189ca9 4155            push    r13
`04189cab 4156            push    r14
`04189cad 4157            push    r15
`04189caf 4881ec60010000  sub     rsp,160h
`04189cb6 4d8bd1          mov     r10,r9
`04189cb9 498bc0          mov     rax,r8
`04189cbc 4c8bca          mov     r9,rdx
`04189cbf 4c8be9          mov     r13,rcx
`04189cc2 4d85c0          test    r8,r8
`04189cc5 0f8523080000    jne     nt!NtAllocateVirtualMemory+0x85e (`0418a4ee)
`0418a4ee 4883f820        cmp     rax,20h
`0418a4f2 0f8223710500    jb      nt! ?? ::NNGAKEGL::`string'+0x475f0 (`041e161b)
`0418a4f8 488bc8          mov     rcx,rax
`0418a4fb e82002ceff      call    nt!RtlFindMostSignificantBit (`03e6a720)
`0418a500 440fbec0        movsx   r8d,al
`0418a504 b83f000000      mov     eax,3Fh
`0418a509 412bc0          sub     eax,r8d
`0418a50c 4898            cdqe
`0418a50e 48898424b0010000 mov     qword ptr [rsp+1B0h],rax
`0418a516 4883f835        cmp     rax,35h
`0418a51a 0f86abf7ffff    jbe     nt!NtAllocateVirtualMemory+0x3b (`04189ccb)
`04189ccb 4533c0          xor     r8d,r8d
`04189cce 418bf8          mov     edi,r8d
`04189cd1 654c8b242588010000 mov   r12,qword ptr gs:[188h]
`04189cda 4d8b5c2470      mov     r11,qword ptr [r12+70h]
`04189cdf 4c895c2478      mov     qword ptr [rsp+78h],r11
`04189ce4 8bb424c0010000  mov     esi,dword ptr [rsp+1C0h]
`04189ceb 448bf6          mov     r14d,esi
`04189cee 4183e67f        and     r14d,7Fh
`04189cf2 0fb7050f93f3ff  movzx   eax,word ptr [nt!KeNumberNodes (`040c3008)]
`04189cf9 443bf0          cmp     r14d,eax
`04189cfc 0f8734790500    ja      nt! ?? ::NNGAKEGL::`string'+0x4760b (`041e1636)
`04189d02 83e680          and     esi,0FFFFFF80h
`04189d05 f7c6ffcf07df    test    esi,0DF07CFFFh
`04189d0b 0f852f790500    jne     nt! ?? ::NNGAKEGL::`string'+0x47615 (`041e1640)
`04189d11 f7c600300800    test    esi,83000h
`04189d17 0f842d790500    je      nt! ?? ::NNGAKEGL::`string'+0x4761f (`041e164a)
`04189d1d f7c60000e820    test    esi,20E80000h
`04189d23 0f85e2090000    jne     nt!NtAllocateVirtualMemory+0xa7b (`0418a70b)
`04189d29 8b8424c8010000  mov     eax,dword ptr [rsp+1C8h]
`04189d30 89442460        mov     dword ptr [rsp+60h],eax
`04189d34 8bc8            mov     ecx,eax
`04189d36 e8c5bcd1ff      call    nt!MiMakeProtectionMask (`03ea5a00)
`04189d3b 89442474        mov     dword ptr [rsp+74h],eax
`04189d3f 83f8ff          cmp     eax,0FFFFFFFFh
`04189d42 0f84c2790500    je      nt! ?? ::NNGAKEGL::`string'+0x476ef (`041e170a)
`04189d48 410fb69424f6010000 movzx edx,byte ptr [r12+1F6h]
`04189d51 88542470        mov     byte ptr [rsp+70h],dl
`04189d55 84d2            test    dl,dl
`04189d57 7436            je      nt!NtAllocateVirtualMemory+0xff (`04189d8f)
`04189d8f 498b19          mov     rbx,qword ptr [r9]
`04189d92 48899c24a0000000 mov     qword ptr [rsp+0A0h],rbx
`04189d9a 4d8b3a          mov     r15,qword ptr [r10]
`04189d9d 4c897c2468      mov     qword ptr [rsp+68h],r15
`04189da2 eb05            jmp     nt!NtAllocateVirtualMemory+0x119 (`04189da9)
`04189da9 488b056092f3ff  mov     rax,qword ptr [nt!MmHighestUserAddress (`040c3010)]
`04189db0 48050000ffff    add     rax,0FFFFFFFFFFFF0000h
`04189db6 483bd8          cmp     rbx,rax
`04189db9 0f8755790500    ja      nt! ?? ::NNGAKEGL::`string'+0x476f9 (`041e1714)
`04189dbf 488b054a92f3ff  mov     rax,qword ptr [nt!MmHighestUserAddress (`040c3010)]
`04189dc6 482bc3          sub     rax,rbx
`04189dc9 482dffff0000    sub     rax,0FFFFh
`04189dcf 493bc7          cmp     rax,r15
`04189dd2 0f8246790500    jb      nt! ?? ::NNGAKEGL::`string'+0x47703 (`041e171e)
`041e171e b8f20000c0      mov     eax,0C00000F2h

根据上面的指令执行路径可知,关键的寄存器是 r9 寄存器。但在函数稍开始位置将第二个参数寄存器 rdx 的值赋值给 r9 寄存器。

参数4:在赋值 r9 之前,函数将 r9 寄存器赋值给 r10 寄存器;在函数稍后位置将 r10 寄存器存储的值作为地址取指针长度的值赋值给 r15 寄存器,并且在最后作为 cmp 比较的依据之一,即代码中的 v19 变量。

参数2:在将 rdx 的值赋值 r9 寄存器之后,在稍后位置将 r9 寄存器的值作为地址取指针长度的值赋值 rbx 寄存器,并通过将 MmHighestUserAddress-rbx-0xFFFF 获得的值与前面的 r15 寄存器进行大小比较。

函数原型:

NTSTATUS
NTAPI
NtAllocateVirtualMemory(
    IN     HANDLE      ProcessHandle,
    IN OUT PVOID      *BaseAddress,
    IN     ULONG_PTR   ZeroBits,
    IN OUT PSIZE_T     RegionSize,
    IN     ULONG       AllocationType,
    IN     ULONG       Protect
    );

MmHighestUserAddress 初值:

mov     rax, 7FFFFFEFFFFh
lea     rdx, aSafeboot_0 ; "SAFEBOOT:"
mov     cs:MmHighestUserAddress, rax
mov     rax, 7FFFFFF0000h
mov     cs:MmUserProbeAddress, rax

如果前面相减得到的 rax 值小于 r15 值,那么报错。

if ( MmHighestUserAddress - *BaseAddress - 0xFFFF < *RegionSize )
    return 0xC00000F2i64;

在报错指令位置下断点,截获到一次报错的上下文,判断的两个寄存器值如下:

kd> r rax, r15
rax=000007fffffe0000 r15=fffffa8000000000

显而易见地,问题出在 r15 寄存器的值,而 r15 寄存器的值是由 r10 寄存器存储的指针指向地址的值赋值的。

kd> r r10
r10=fffff880030f8918
kd> dq fffff880030f8918 l1
fffff880`030f8918  fffffa80`00000000

显而易见地这是一个非常离谱的值,通常此处通过 RegionSize 参数指向域的值确定分配虚拟内存的区域大小,而 0xfffffa8000000000 显然超过了正常用户地址空间的范围。

至于为什么会传入这样无效的值,继续向上探究:

nt!NtSetInformationProcess:
`041d659c fff3            push    rbx
kd> r rdx
rdx=0000000000000029
kd> p
nt!NtSetInformationProcess+0x2:
`041d659e 56              push    rsi
`041d659f 57              push    rdi
`041d65a0 4154            push    r12
`041d65a2 4155            push    r13
`041d65a4 4156            push    r14
`041d65a6 4157            push    r15
`041d65a8 4881ec10030000  sub     rsp,310h
`041d65af 488b052a44e8ff  mov     rax,qword ptr [nt!_security_cookie (`0405a9e0)]
`041d65b6 4833c4          xor     rax,rsp
`041d65b9 4889842400030000 mov     qword ptr [rsp+300h],rax
`041d65c1 458bf9          mov     r15d,r9d
`041d65c4 4d8be0          mov     r12,r8
`041d65c7 4c8bd1          mov     r10,rcx
`041d65ca 48894c2458      mov     qword ptr [rsp+58h],rcx
`041d65cf 4c898424b0000000 mov     qword ptr [rsp+0B0h],r8
`041d65d7 44897c2474      mov     dword ptr [rsp+74h],r15d
`041d65dc 654c8b342588010000 mov   r14,qword ptr gs:[188h] ;; Get _KTHREAD pointer
`041d65e5 4c89b42480000000 mov     qword ptr [rsp+80h],r14
`041d65ed 418abef6010000  mov     dil,byte ptr [r14+1F6h] ;; PreviousMode
`041d65f4 33f6            xor     esi,esi
`041d65f6 403afe          cmp     dil,sil ;; PreviousMode == KernelMode
`041d65f9 0f8461030000    je      nt!NtSetInformationProcess+0x3c3 (`041d6960)
`041d6960 bb08000000      mov     ebx,8
`041d6965 448d6bf9        lea     r13d,[rbx-7]
`041d6969 e949fdffff      jmp     nt!NtSetInformationProcess+0x11a (`041d66b7)
`041d66b7 83fa17          cmp     edx,17h
`041d66ba 0f8f76010000    jg      nt!NtSetInformationProcess+0x299 (`041d6836)
`041d6836 83fa27          cmp     edx,27h
`041d6839 0f8e2f010000    jle     nt!NtSetInformationProcess+0x3d1 (`041d696e)
`041d683f 83ea28          sub     edx,28h
`041d6842 0f84c95f0600    je      nt! ?? ::NNGAKEGL::`string'+0x4ce75 (`0423c811)
`041d6848 412bd5          sub     edx,r13d ; r13d=029h
`041d684b 0f8573030000    jne     nt!NtSetInformationProcess+0x627 (`041d6bc4)
`041d6851 4883cbff        or      rbx,0FFFFFFFFFFFFFFFFh
`041d6855 4c3bd3          cmp     r10,rbx
`041d6858 0f850b5f0600    jne     nt! ?? ::NNGAKEGL::`string'+0x4cdbf (`0423c769)
`041d685e 4183ff28        cmp     r15d,28h
`041d6862 0f85155f0600    jne     nt! ?? ::NNGAKEGL::`string'+0x4cdd3 (`0423c77d)
`041d6868 403afe          cmp     dil,sil
`041d686b 0f85d8030000    jne     nt!NtSetInformationProcess+0x6ac (`041d6c49)
`041d6871 4c8bfe          mov     r15,rsi
`041d6874 458b3424        mov     r14d,dword ptr [r12]
`041d6878 4183fe40        cmp     r14d,40h
`041d687c 0f87f15e0600    ja      nt! ?? ::NNGAKEGL::`string'+0x4cdc9 (`0423c773)
`041d6882 418b44240c      mov     eax,dword ptr [r12+0Ch]
`041d6887 410b442408      or      eax,dword ptr [r12+8]
`041d688c 410b442404      or      eax,dword ptr [r12+4]
`041d6891 0f85dc5e0600    jne     nt! ?? ::NNGAKEGL::`string'+0x4cdc9 (`0423c773)
`041d6897 4983c410        add     r12,10h
`041d689b 49393424        cmp     qword ptr [r12],rsi
kd> r r12
r12=fffff88003aefd98
kd> p
`041d689f 0f84215f0600    je      nt! ?? ::NNGAKEGL::`string'+0x4ce23 (`0423c7c6)
`041d68a5 49ba1400000080f7ffff mov r10,0FFFFF78000000014h
`041d68af 4d8b12          mov     r10,qword ptr [r10]
`041d68b2 0f31            rdtsc
`041d68b4 48c1e220        shl     rdx,20h
`041d68b8 480bc2          or      rax,rdx
`041d68bb 4c03d0          add     r10,rax
`041d68be 4183e21f        and     r10d,1Fh
`041d68c2 4503d5          add     r10d,r13d
`041d68c5 4d8b1c24        mov     r11,qword ptr [r12] ;; 从r12指向地址取值赋给r11
`041d68c9 4c899c24f8000000 mov     qword ptr [rsp+0F8h],r11 ;; 为[rsp+0F8h]变量赋值
`041d68d1 65488b042588010000 mov   rax,qword ptr gs:[188h]
`041d68da 488b4870        mov     rcx,qword ptr [rax+70h]
`041d68de 0fbaa13c04000011 bt      dword ptr [rcx+43Ch],11h
`041d68e6 0f823e010000    jb      nt!NtSetInformationProcess+0x48d (`041d6a2a)
`041d68ec 4d8b4c2408      mov     r9,qword ptr [r12+8]
`041d68f1 4c3bce          cmp     r9,rsi
`041d68f4 0f85d65e0600    jne     nt! ?? ::NNGAKEGL::`string'+0x4ce2d (`0423c7d0)
`0423c7d0 4983f920        cmp     r9,20h
`0423c7d4 7217            jb      nt! ?? ::NNGAKEGL::`string'+0x4ce51 (`0423c7ed)
`0423c7d6 498bc9          mov     rcx,r9
`0423c7d9 e8423fc8ff      call    nt!RtlFindMostSignificantBit (`03ec0720)
`0423c7de 0fbec8          movsx   ecx,al
`0423c7e1 b83f000000      mov     eax,3Fh
`0423c7e6 2bc1            sub     eax,ecx
`0423c7e8 4c63c8          movsxd  r9,eax
`0423c7eb eb04            jmp     nt! ?? ::NNGAKEGL::`string'+0x4ce55 (`0423c7f1)
`0423c7f1 4983f935        cmp     r9,35h
`0423c7f5 0f8739a2f9ff    ja      nt!NtSetInformationProcess+0x497 (`041d6a34)
`0423c7fb e9faa0f9ff      jmp     nt!NtSetInformationProcess+0x35d (`041d68fa)
`041d68fa 4d8d6c2410      lea     r13,[r12+10h]
`041d68ff 4c896c2420      mov     qword ptr [rsp+20h],r13
`041d6904 4533c0          xor     r8d,r8d
`041d6907 498bd3          mov     rdx,r11
`041d690a 418bca          mov     ecx,r10d
`041d690d e8863e0000      call    nt!MiScanUserAddressSpace (`041da798)
`041d6912 8bc8            mov     ecx,eax
`041d6914 3bc6            cmp     eax,esi
`041d6916 7c28            jl      nt!NtSetInformationProcess+0x3a3 (`041d6940)
`041d6940 3bce            cmp     ecx,esi
`041d6942 0f8cec000000    jl      nt!NtSetInformationProcess+0x497 (`041d6a34)
`041d6a34 498d542410      lea     rdx,[r12+10h]
`041d6a39 488932          mov     qword ptr [rdx],rsi
`041d6a3c 410fbaee0d      bts     r14d,0Dh
`041d6a41 c744242804000000 mov     dword ptr [rsp+28h],4
`041d6a49 4489742420      mov     dword ptr [rsp+20h],r14d
`041d6a4e 4c8d8c24f8000000 lea     r9,[rsp+0F8h] ;; 取[rsp+0F8h]变量的地址给r9寄存器
`041d6a56 4d8b442408      mov     r8,qword ptr [r12+8]
`041d6a5b 488bcb          mov     rcx,rbx
`041d6a5e e87da4d0ff      call    nt!ZwAllocateVirtualMemory (`03ee0ee0)
kd> p
nt!NtSetInformationProcess+0x4c6:
`041d6a63 8bc8            mov     ecx,eax
kd> r rax
rax=00000000c00000f2
kd> r rsp
rsp=fffff88003aef820
kd> dq rsp+0x0f8 l1
fffff880`03aef918  fffff800`00000000
kd> dq fffff88003aefd98 l1
fffff880`03aefd98  fffff800`00000000

函数定义:

NTSTATUS
NTAPI
NtSetInformationProcess (
    IN HANDLE           ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    IN PVOID            ProcessInformation,
    IN ULONG            ProcessInformationLength
    );

其中第二个参数是定义设置进程信息的类型。根据上面的指令执行路径得知,这次调用传递给该函数的第二个参数值为0x29,根据 PROCESSINFOCLASS 定义:

typedef enum _PROCESSINFOCLASS {
    ...
    ProcessThreadStackAllocation = 0x29, // 0x29, 41
    ...
} PROCESSINFOCLASS;

进程信息类型为 0x29 时表示设置进程的线程栈分配信息。

在这次 NtSetInformationProcess 函数调用中,可以看到传入 ZwAllocateVirtualMemory 函数的第四个参数来自它自身第三个参数 r8 寄存器指向的结构体对象基址 +10 偏移的域值。

第三个参数是 ProcessInformation,指向一个由调用者分配的缓冲区,作为用于提供指定的进程信息类型所需的各种数据的结构。

在该函数调用初期查看这个缓冲区中的数据:

kd> dq 0xfffff880`03aefd88
fffff880`03aefd88  00000000`00000000 00000000`00000000
fffff880`03aefd98  fffff880`00000000 00000000`00000000
fffff880`03aefda8  00000000`00000000 00000000`00000000

可以确定的是上级函数传入 NtSetInformationProcess 的第三个参数时缓冲区中的数据就是这样的,就是说偏移为 +10h 位置的域其值为 0xFFFFF88000000000 这种异常的值。

所以此时继续向上探究。在 nt!RtlCreateUserStack 函数首地址下断点捕获其调用路径:

nt!RtlCreateUserStack:
`041cf104 4c89442418      mov     qword ptr [rsp+18h],r8
`041cf109 4889542410      mov     qword ptr [rsp+10h],rdx
`041cf10e 48894c2408      mov     qword ptr [rsp+8],rcx
`041cf113 53              push    rbx
`041cf114 56              push    rsi
`041cf115 4154            push    r12
`041cf117 4155            push    r13
`041cf119 4156            push    r14
`041cf11b 4157            push    r15
`041cf11d 4881ecb8000000  sub     rsp,0B8h
`041cf124 4d8be9          mov     r13,r9
`041cf127 488bf2          mov     rsi,rdx
`041cf12a 488bd9          mov     rbx,rcx
`041cf12d 4d8bf9          mov     r15,r9
`041cf130 49c1ef38        shr     r15,38h
`041cf134 44887c2430      mov     byte ptr [rsp+30h],r15b
`041cf139 48b8ffffffffffffff00 mov rax,0FFFFFFFFFFFFFFh
`041cf143 4c23e8          and     r13,rax
`041cf146 4c89ac2408010000 mov     qword ptr [rsp+108h],r13
`041cf14e 4180ff40        cmp     r15b,40h
`041cf152 0f8764060400    ja      nt! ?? ::NNGAKEGL::`string'+0x7020 (`0420f7bc)
`041cf158 4b8d446d00      lea     rax,[r13+r13*2]
`041cf15d 4889442460      mov     qword ptr [rsp+60h],rax
`041cf162 65488b042588010000 mov   rax,qword ptr gs:[188h]
`041cf16b 488b4870        mov     rcx,qword ptr [rax+70h]
`041cf16f 4c8bb138030000  mov     r14,qword ptr [rcx+338h]
`041cf176 4c89742458      mov     qword ptr [rsp+58h],r14
`041cf17b 4885db          test    rbx,rbx
`041cf17e 0f848b010000    je      nt!RtlCreateUserStack+0x20b (`041cf30f)
`041cf184 4885d2          test    rdx,rdx
`041cf187 0f8482010000    je      nt!RtlCreateUserStack+0x20b (`041cf30f)
`041cf18d b800400000      mov     eax,4000h
`041cf192 4885db          test    rbx,rbx
`041cf195 480f44d8        cmove   rbx,rax
`041cf199 483bde          cmp     rbx,rsi
`041cf19c 0f832c060400    jae     nt! ?? ::NNGAKEGL::`string'+0x7032 (`0420f7ce)
`0420f7ce 488db3ffff0f00  lea     rsi,[rbx+0FFFFFh]
`0420f7d5 4881e60000f0ff  and     rsi,0FFFFFFFFFFF00000h
`0420f7dc e9c1f9fbff      jmp     nt!RtlCreateUserStack+0x9e (`041cf1a2)
`041cf1a2 498d4dff        lea     rcx,[r13-1]
`041cf1a6 48f7d1          not     rcx
`041cf1a9 4e8d642bff      lea     r12,[rbx+r13-1]
`041cf1ae 4c23e1          and     r12,rcx
`041cf1b1 4881c6ffff0000  add     rsi,0FFFFh
`041cf1b8 4881e60000ffff  and     rsi,0FFFFFFFFFFFF0000h
`041cf1bf 498b8618030000  mov     rax,qword ptr [r14+318h]
`041cf1c6 48898424a0000000 mov     qword ptr [rsp+0A0h],rax
`041cf1ce eb05            jmp     nt!RtlCreateUserStack+0xd1 (`041cf1d5)
`041cf1d5 4885c0          test    rax,rax
`041cf1d8 0f8503060400    jne     nt! ?? ::NNGAKEGL::`string'+0x7045 (`0420f7e1)
`041cf1de 410fb6c7        movzx   eax,r15b
`041cf1e2 89442478        mov     dword ptr [rsp+78h],eax
`041cf1e6 8364247c00      and     dword ptr [rsp+7Ch],0
`041cf1eb 83a4248400000000 and     dword ptr [rsp+84h],0
`041cf1f3 83a4248000000000 and     dword ptr [rsp+80h],0
`041cf1fb 4889b42488000000 mov     qword ptr [rsp+88h],rsi ; assign-> RegionSize
`041cf203 4c89842490000000 mov     qword ptr [rsp+90h],r8
`041cf20b 41b928000000    mov     r9d,28h
`041cf211 4c8d442478      lea     r8,[rsp+78h]
`041cf216 418d5101        lea     edx,[r9+1]
`041cf21a 4983cfff        or      r15,0FFFFFFFFFFFFFFFFh
`041cf21e 498bcf          mov     rcx,r15
`041cf221 e83aadd0ff      call    nt!ZwSetInformationProcess (`03ed9f60)
kd> r r8
r8=fffff8800422bd88
kd> dq [r8]
fffff880`0422bd88  00000000`00000000 00000000`00000000
fffff880`0422bd98  fffff880`00100000 00000000`00000000
fffff880`0422bda8  00000000`00000384 00000000`00000000
fffff880`0422bdb8  00000000`00000018 fffff880`0422bdd0
kd> r
rax=0000000000000000 rbx=fffff88000002000 rcx=ffffffffffffffff
rdx=0000000000000029 rsi=fffff88000100000 rdi=0000000000000000
rip=fffff800041cf221 rsp=fffff8800422bd10 rbp=fffffa8004170b30
 r8=fffff8800422bd88  r9=0000000000000028 r10=0000000000000001
r11=fffff8800422bde0 r12=fffff88000002000 r13=0000000000001000
r14=000007fffffd7000 r15=ffffffffffffffff
iopl=0         nv up ei ng nz na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b  efl=00000286

根据执行路径可知,传入给 ZwSetInformationProcess 的第三个参数指针指向的是位于 RtlCreateUserStack 栈区域的局部结构体变量。在将结构体地址赋值给 r8 寄存器之前,函数执行对结构体 6 个域赋值操作,其中需要关注的是第 5 个域,即在前面一直追寻的指示 RegionSize 的来源的域,这个域是通过 rsi 寄存器赋值的。

向上追溯,发现在 rsi 寄存器取 rbx+0xFFFFF 地址之后,再经过几次逻辑与运算:

lea     rsi,[rbx+0FFFFFh]
and     rsi,0FFFFFFFFFFF00000h
add     rsi,0FFFFh
and     rsi,0FFFFFFFFFFFF0000h

此时 rbx 的值是 0xFFFFF88000002000,而 rbx 是在函数执行开始时由 rcx 寄存器直接赋值的,而 rcx 寄存器是作为函数的第 1 个参数传入的。该函数原型如下:

NTSTATUS
NTAPI
RtlCreateUserStack (
    _In_opt_ SIZE_T       CommittedStackSize,
    _In_opt_ SIZE_T       MaximumStackSize,
    _In_opt_ ULONG_PTR    ZeroBits,
    _In_     SIZE_T       PageSize,
    _In_     ULONG_PTR    ReserveAlignment,
    _Out_    PINITIAL_TEB InitialTeb
);

其中第一个参数是 CommittedStackSize,作为栈的初始提交大小。显然地在这里传入该函数的这个参数就是不寻常的值。那么继续向上探究:

nt!PspAllocateThread:
`041cd5b4 488bc4          mov     rax,rsp
`041cd5b7 4c894820        mov     qword ptr [rax+20h],r9
`041cd5bb 44884018        mov     byte ptr [rax+18h],r8b
`041cd5bf 48895010        mov     qword ptr [rax+10h],rdx
`041cd5c3 48894808        mov     qword ptr [rax+8],rcx
`041cd5c7 53              push    rbx
`041cd5c8 56              push    rsi
`041cd5c9 57              push    rdi
`041cd5ca 4154            push    r12
`041cd5cc 4155            push    r13
`041cd5ce 4156            push    r14
`041cd5d0 4157            push    r15
`041cd5d2 4881ece0010000  sub     rsp,1E0h
`041cd5d9 458ad0          mov     r10b,r8b
`041cd5dc 4c8be9          mov     r13,rcx
`041cd5df 33ff            xor     edi,edi
`041cd5e1 48897c2470      mov     qword ptr [rsp+70h],rdi
`041cd5e6 4c3bcf          cmp     r9,rdi
`041cd5e9 740b            je      nt!PspAllocateThread+0x42 (`041cd5f6)
`041cd5eb 410fba210f      bt      dword ptr [r9],0Fh
`041cd5f0 0f823c0ff6ff    jb      nt! ?? ::NNGAKEGL::`string'+0x241f0 (`0412e532)
`041cd5f6 4c8ba42460020000 mov     r12,qword ptr [rsp+260h]
`041cd5fe 65488b042588010000 mov   rax,qword ptr gs:[188h]
`041cd607 4889842498000000 mov     qword ptr [rsp+98h],rax
`041cd60f 4c8b842478020000 mov     r8,qword ptr [rsp+278h]
`041cd617 4189b878010000  mov     dword ptr [r8+178h],edi
`041cd61e 4588907c010000  mov     byte ptr [r8+17Ch],r10b
`041cd625 4c8bb42440020000 mov     r14,qword ptr [rsp+240h]
`041cd62d 4c3bf7          cmp     r14,rdi
`041cd630 0f848a060000    je      nt!PspAllocateThread+0x70b (`041cdcc0)
`041cd636 483b0df349f4ff  cmp     rcx,qword ptr [nt!PsInitialSystemProcess (`04112030)]
`041cd63d 0f841a0ff6ff    je      nt! ?? ::NNGAKEGL::`string'+0x2421f (`0412e55d)
`041cd643 483bd7          cmp     rdx,rdi
`041cd646 0f851a040000    jne     nt!PspAllocateThread+0x4b2 (`041cda66)
`041cd64c 498b85c8010000  mov     rax,qword ptr [r13+1C8h]
`041cd653 48f7d8          neg     rax
`041cd656 1bf6            sbb     esi,esi
`041cd658 83e658          and     esi,58h
`041cd65b 41bf98040000    mov     r15d,498h
`041cd661 4103f7          add     esi,r15d
`041cd664 488d442458      lea     rax,[rsp+58h]
`041cd669 4889442440      mov     qword ptr [rsp+40h],rax
`041cd66e 89742438        mov     dword ptr [rsp+38h],esi
`041cd672 897c2430        mov     dword ptr [rsp+30h],edi
`041cd676 89742428        mov     dword ptr [rsp+28h],esi
`041cd67a 48897c2420      mov     qword ptr [rsp+20h],rdi
`041cd67f 458aca          mov     r9b,r10b
`041cd682 4c8bc2          mov     r8,rdx
`041cd685 488b15c449f4ff  mov     rdx,qword ptr [nt!PsThreadType (`04112050)]
`041cd68c 418aca          mov     cl,r10b
`041cd68f e84cd60000      call    nt!ObCreateObject (`041dace0)
`041cd694 8bd8            mov     ebx,eax
`041cd696 89442450        mov     dword ptr [rsp+50h],eax
`041cd69a 3bc7            cmp     eax,edi
`041cd69c 0f8cc00ef6ff    jl      nt! ?? ::NNGAKEGL::`string'+0x24224 (`0412e562)
`041cd6a2 448bc6          mov     r8d,esi
`041cd6a5 33d2            xor     edx,edx
`041cd6a7 488b5c2458      mov     rbx,qword ptr [rsp+58h]
`041cd6ac 488bcb          mov     rcx,rbx
`041cd6af e8dc61d1ff      call    nt!memset (`03ee3890)
`041cd6b4 413bf7          cmp     esi,r15d
`041cd6b7 0f87b40ef6ff    ja      nt! ?? ::NNGAKEGL::`string'+0x24233 (`0412e571)
`041cd6bd 4889bb30040000  mov     qword ptr [rbx+430h],rdi
`041cd6c4 488d8bb0030000  lea     rcx,[rbx+3B0h]
`041cd6cb 48898c2480000000 mov     qword ptr [rsp+80h],rcx
`041cd6d3 498b8580010000  mov     rax,qword ptr [r13+180h]
`041cd6da 488901          mov     qword ptr [rcx],rax
`041cd6dd 41f6042404      test    byte ptr [r12],4
`041cd6e2 0f85c00ef6ff    jne     nt! ?? ::NNGAKEGL::`string'+0x2426a (`0412e5a8)
`041cd6e8 488db338040000  lea     rsi,[rbx+438h]
`041cd6ef 48893e          mov     qword ptr [rsi],rdi
`041cd6f2 c7834004000007000000 mov dword ptr [rbx+440h],7
`041cd6fc 488d8bc0030000  lea     rcx,[rbx+3C0h]
`041cd703 33d2            xor     edx,edx
`041cd705 448d4201        lea     r8d,[rdx+1]
`041cd709 e88e56d0ff      call    nt!KeInitializeSemaphore (`03ed2d9c)
`041cd70e 488d8b68030000  lea     rcx,[rbx+368h]
`041cd715 48894908        mov     qword ptr [rcx+8],rcx
`041cd719 488909          mov     qword ptr [rcx],rcx
`041cd71c 488d83e8030000  lea     rax,[rbx+3E8h]
`041cd723 48894008        mov     qword ptr [rax+8],rax
`041cd727 488900          mov     qword ptr [rax],rax
`041cd72a 4889bb80040000  mov     qword ptr [rbx+480h],rdi
`041cd731 4889bb98030000  mov     qword ptr [rbx+398h],rdi
`041cd738 488d83a0030000  lea     rax,[rbx+3A0h]
`041cd73f 48894008        mov     qword ptr [rax+8],rax
`041cd743 488900          mov     qword ptr [rax],rax
`041cd746 48a11400000080f7ffff mov rax,qword ptr [FFFFF78000000014h]
`041cd750 488b4c2458      mov     rcx,qword ptr [rsp+58h]
`041cd755 48898160030000  mov     qword ptr [rcx+360h],rax
`041cd75c 48898c24b8000000 mov     qword ptr [rsp+0B8h],rcx
`041cd764 89bc24c0000000  mov     dword ptr [rsp+0C0h],edi
`041cd76b f0480fba2e00    lock bts qword ptr [rsi],0
`041cd771 0f823d0ef6ff    jb      nt! ?? ::NNGAKEGL::`string'+0x24276 (`0412e5b4)
`041cd777 4c8b7c2458      mov     r15,qword ptr [rsp+58h]
`041cd77c 498d9fb8030000  lea     rbx,[r15+3B8h]
`041cd783 48899c24b0000000 mov     qword ptr [rsp+0B0h],rbx
`041cd78b 488d9424b8000000 lea     rdx,[rsp+0B8h]
`041cd793 488b0d2eb4ebff  mov     rcx,qword ptr [nt!PspCidTable (`04088bc8)]
`041cd79a e8a5050000      call    nt!ExCreateHandle (`041cdd44)
`041cd79f 488903          mov     qword ptr [rbx],rax
`041cd7a2 483bc7          cmp     rax,rdi
`041cd7a5 0f84170ef6ff    je      nt! ?? ::NNGAKEGL::`string'+0x24284 (`0412e5c2)
`041cd7ab 4c3bf7          cmp     r14,rdi
`041cd7ae 0f8429050000    je      nt!PspAllocateThread+0x728 (`041cdcdd)
`041cd7b4 668bf7          mov     si,di
`041cd7b7 89742460        mov     dword ptr [rsp+60h],esi
`041cd7bb 41f6042480      test    byte ptr [r12],80h
`041cd7c0 0f8592020000    jne     nt!PspAllocateThread+0x4a4 (`041cda58)
`041cd7c6 41f6042402      test    byte ptr [r12],2
`041cd7cb 0f85dc040000    jne     nt!PspAllocateThread+0x6f8 (`041cdcad)
`041cd7d1 41b808000000    mov     r8d,8
`041cd7d7 4c8ba42470020000 mov     r12,qword ptr [rsp+270h]
`041cd7df 4c3be7          cmp     r12,rdi
`041cd7e2 0f84c90ef6ff    je      nt! ?? ::NNGAKEGL::`string'+0x2437b (`0412e6b1)
`041cd7e8 4939bd20030000  cmp     qword ptr [r13+320h],rdi
`041cd7ef 0f8512030000    jne     nt!PspAllocateThread+0x552 (`041cdb07)
`041cd7f5 41f6042401      test    byte ptr [r12],1
`041cd7fa 0f858e0ef6ff    jne     nt! ?? ::NNGAKEGL::`string'+0x24358 (`0412e68e)
`041cd800 bb00100000      mov     ebx,1000h
`041cd805 41397c2420      cmp     dword ptr [r12+20h],edi
`041cd80a 0f853e0ef6ff    jne     nt! ?? ::NNGAKEGL::`string'+0x24318 (`0412e64e)
`041cd810 488d942450010000 lea     rdx,[rsp+150h]
`041cd818 498bcd          mov     rcx,r13
`041cd81b e8f04bd4ff      call    nt!KeStackAttachProcess (`03f12410)
`041cd820 4c8bb42448020000 mov     r14,qword ptr [rsp+248h]
`041cd828 4c89742428      mov     qword ptr [rsp+28h],r14
`041cd82d 48c744242000000100 mov   qword ptr [rsp+20h],10000h
`041cd836 4c8bcb          mov     r9,rbx
`041cd839 4d8b442408      mov     r8,qword ptr [r12+8]
`041cd83e 498b542418      mov     rdx,qword ptr [r12+18h]
`041cd843 498b4c2410      mov     rcx,qword ptr [r12+10h]
`041cd848 e8b7180000      call    nt!RtlCreateUserStack (`041cf104)
kd> r
rax=0000000000000000 rbx=0000000000001000 rcx=fffff88000002000
rdx=000007fe00000000 rsi=fffffa8003020000 rdi=0000000000000000
rip=041cd848 rsp=fffff8800422be00 rbp=fffffa8004170b30
 r8=0000000000000000  r9=0000000000001000 r10=0000000000000001
r11=fffff8800422bde0 r12=fffff8800422c310 r13=fffffa8004170b30
r14=fffff8800422c340 r15=fffffa8003028b60
kd> dq [rsp+270h] l1
fffff880`0422c070  fffff880`0422c310
kd> dq fffff880`0422c310 l4
fffff880`0422c310  00000000`00000000 00000000`00000000
fffff880`0422c320  fffff880`00002000 000007fe`00000000

根据上面的执行路径,传入 RtlCreateUserStack 函数的第一个参数 rcx 寄存器是由 qword ptr [r12+10h] 赋值的。而 r12 寄存器在稍早时候取 [rsp+270h] 指向地址的指针长度的值。

[rsp+270h] 是调用者传递给该函数的第 11 个参数。继续向上追溯:

nt!PspCreateThread:
`041cd2ac 48895c2410      mov     qword ptr [rsp+10h],rbx
`041cd2b1 55              push    rbp
`041cd2b2 56              push    rsi
`041cd2b3 57              push    rdi
`041cd2b4 4154            push    r12
`041cd2b6 4155            push    r13
`041cd2b8 4156            push    r14
`041cd2ba 4157            push    r15
`041cd2bc 4881ec40020000  sub     rsp,240h
`041cd2c3 488b051667e8ff  mov     rax,qword ptr [nt!_security_cookie (`040539e0)]
`041cd2ca 4833c4          xor     rax,rsp
`041cd2cd 4889842430020000 mov     qword ptr [rsp+230h],rax
`041cd2d5 4c8bac24c0020000 mov     r13,qword ptr [rsp+2C0h]
`041cd2dd 488bbc24a0020000 mov     rdi,qword ptr [rsp+2A0h]
`041cd2e5 4c8bb424a8020000 mov     r14,qword ptr [rsp+2A8h]
`041cd2ed 4c8bbc24c8020000 mov     r15,qword ptr [rsp+2C8h]
`041cd2f5 65488b1c2588010000 mov   rbx,qword ptr gs:[188h]
`041cd2fe 48898c2480000000 mov     qword ptr [rsp+80h],rcx
`041cd306 488b8c24b0020000 mov     rcx,qword ptr [rsp+2B0h]
`041cd30e 4c89842488000000 mov     qword ptr [rsp+88h],r8
`041cd316 4533c0          xor     r8d,r8d
`041cd319 48898c2490000000 mov     qword ptr [rsp+90h],rcx
`041cd321 488b8c24b8020000 mov     rcx,qword ptr [rsp+2B8h]
`041cd329 498bc1          mov     rax,r9
`041cd32c 48898c2498000000 mov     qword ptr [rsp+98h],rcx
`041cd334 488b8c24e8020000 mov     rcx,qword ptr [rsp+2E8h]
`041cd33c 89542464        mov     dword ptr [rsp+64h],edx
`041cd340 458ae0          mov     r12b,r8b
`041cd343 48894c2470      mov     qword ptr [rsp+70h],rcx
`041cd348 4d3be8          cmp     r13,r8
`041cd34b 7407            je      a8 (`041cd354)
`041cd34d 448aa3f6010000  mov     r12b,byte ptr [rbx+1F6h]
`041cd354 4c89442468      mov     qword ptr [rsp+68h],r8
`041cd359 be080000c0      mov     esi,0C0000008h
`041cd35e 493bc0          cmp     rax,r8
`041cd361 0f842a020000    je      2e5 (`041cd591)
`041cd367 4c89442430      mov     qword ptr [rsp+30h],r8
`041cd36c 4c8b05ad4cf4ff  mov     r8,qword ptr [nt!PsProcessType (`04112020)]
`041cd373 488d4c2478      lea     rcx,[rsp+78h]
`041cd378 48894c2428      mov     qword ptr [rsp+28h],rcx
`041cd37d 458acc          mov     r9b,r12b
`041cd380 ba02000000      mov     edx,2
`041cd385 488bc8          mov     rcx,rax
`041cd388 c744242044666c74 mov     dword ptr [rsp+20h],746C6644h
`041cd390 e84ba10000      call    nt!ObReferenceObjectByHandleWithTag (`041d74e0)
`041cd395 488b6c2478      mov     rbp,qword ptr [rsp+78h]
`041cd39a 4533c0          xor     r8d,r8d
`041cd39d 413bc0          cmp     eax,r8d
`041cd3a0 0f8c9a010000    jl      294 (`041cd540)
`041cd3a6 453ae0          cmp     r12b,r8b
`041cd3a9 0f85bc010000    jne     2bf (`041cd56b)
`041cd56b 483b2dbe4af4ff  cmp     rbp,qword ptr [nt!PsInitialSystemProcess (`04112030)]
`041cd572 0f8537feffff    jne     103 (`041cd3af)
`041cd3af 8b8c24d0020000  mov     ecx,dword ptr [rsp+2D0h]
`041cd3b6 41b901000000    mov     r9d,1
`041cd3bc 418bc0          mov     eax,r8d
`041cd3bf 4184c9          test    r9b,cl
`041cd3c2 410f45c1        cmovne  eax,r9d
`041cd3c6 f6c102          test    cl,2
`041cd3c9 0f85ba010000    jne     2dd (`041cd589)
`041cd3cf 89442460        mov     dword ptr [rsp+60h],eax
`041cd3d3 f6c104          test    cl,4
`041cd3d6 0f855e200500    jne     nt! ?? ::NNGAKEGL::`string'+0x2a7ca (`0421f43a)
`041cd3dc f6c108          test    cl,8
`041cd3df 0f8598010000    jne     2d1 (`041cd57d)
`041cd3e5 66ff8bc4010000  dec     word ptr [rbx+1C4h]
`041cd3ec 0f0d8d78010000  prefetchw [rbp+178h]
`041cd3f3 488b8578010000  mov     rax,qword ptr [rbp+178h]
`041cd3fa 4883e0fe        and     rax,0FFFFFFFFFFFFFFFEh
`041cd3fe 488d4802        lea     rcx,[rax+2]
`041cd402 f0480fb18d78010000 lock cmpxchg qword ptr [rbp+178h],rcx
`041cd40b 0f8535200500    jne     nt! ?? ::NNGAKEGL::`string'+0x2a7d6 (`0421f446)
`041cd411 488bcd          mov     rcx,rbp
`041cd414 e8d70ad2ff      call    nt!ObfReferenceObject (`03eedef0)
`041cd419 488b442470      mov     rax,qword ptr [rsp+70h]
`041cd41e 488b942488000000 mov     rdx,qword ptr [rsp+88h]
`041cd426 4c8d9c24a0000000 lea     r11,[rsp+0A0h]
`041cd42e 4d8bce          mov     r9,r14
`041cd431 458ac4          mov     r8b,r12b
`041cd434 4c895c2458      mov     qword ptr [rsp+58h],r11
`041cd439 4889442450      mov     qword ptr [rsp+50h],rax
`041cd43e 488d442468      lea     rax,[rsp+68h]
`041cd443 4889442448      mov     qword ptr [rsp+48h],rax
`041cd448 488d442460      lea     rax,[rsp+60h]
`041cd44d 488bcd          mov     rcx,rbp
`041cd450 4889442440      mov     qword ptr [rsp+40h],rax
`041cd455 488b8424e0020000 mov     rax,qword ptr [rsp+2E0h]
`041cd45d 4889442438      mov     qword ptr [rsp+38h],rax
`041cd462 488b8424d8020000 mov     rax,qword ptr [rsp+2D8h]
`041cd46a 4889442430      mov     qword ptr [rsp+30h],rax
`041cd46f 4c897c2428      mov     qword ptr [rsp+28h],r15
`041cd474 4c896c2420      mov     qword ptr [rsp+20h],r13
`041cd479 e836010000      call    nt!PspAllocateThread (`041cd5b4)
kd> dq [rsp+50h] l1
fffff880`04286070  fffff880`04286310
kd> dq fffff880`04286310 l4
fffff880`04286310  00000000`00000000 00000000`00000000
fffff880`04286320  fffff880`00000000 00000144`00000000

观察上面的执行路径不难发现传递给 PspAllocateThread 函数的第 11 个参数来自 PspCreateThread 函数的 [rsp+2E8h] 第 14 个参数,而在当前函数中没有进行任何修改和取值访问。

而在 NtCreateThreadEx 函数中由于虚拟化设置问题暂时无法进行单步追踪。所以通过 IDA 对其汇编代码进行分析,再加上通过 PspAllocateThread 函数返回后的上下文,对运行现场的环境进一步的追踪:

`403701DF loc_1403701DF:
`403701DF    mov     [rsp+748h+var_6D8], bl
`403701E3    mov     rax, [rsp+748h+arg_40]
`403701EB    mov     [rsp+748h+var_6C8], rax
`403701F3    mov     rax, [rsp+748h+arg_48]
`403701FB    mov     [rsp+748h+var_6C0], rax
`40370203    mov     rax, [rsp+748h+arg_38]
`4037020B    mov     [rsp+748h+var_6D0], rax
`40370210    mov     [rsp+748h+var_6B8], ebx
`40370217    xor     edx, edx        ; Val
`40370219    mov     r8d, 150h       ; Size
`4037021F    lea     rcx, [rsp+748h+var_198] ; Dst
`40370227    call    memset
`4037022C    test    rdi, rdi
`4037022F    jz      short loc_140370285
`40370231    mov     rax, gs:188h
`4037023A    lea     r9, [rsp+748h+var_198]
`40370242    mov     r8d, 1
`40370248    mov     dl, [rax+1F6h]
`4037024E    mov     rcx, rdi
`40370251    call    PspBuildCreateProcessContext
`40370256    test    eax, eax
`40370258    js      loc_140370396
`4037025E    lea     rax, [rsp+748h+var_70]
`40370266    bt      dword ptr [rsp+748h+var_198], 0Ch
`4037026F    cmovnb  rax, rbx
`40370273    mov     rbx, rax
`40370276    bt      dword ptr [rsp+748h+var_198], 0Eh
`4037027F    jb      loc_1403C8D38
`40370285
`40370285 loc_140370285:
`40370285    mov     [rsp+748h+var_638], 10000Bh
`40370290    mov     rax, cs:PspUserThreadStart
`40370297    mov     [rsp+748h+var_570], rax
`4037029F    mov     rax, [rsp+748h+arg_20]
`403702A7    mov     [rsp+748h+var_5E8], rax
`403702AF    mov     rax, [rsp+748h+arg_28]
`403702B7    mov     [rsp+748h+var_5E0], rax
`403702BF    mov     ecx, 2Bh
`403702C4    mov     [rsp+748h+var_62E], cx
`403702CC    mov     [rsp+748h+var_62C], cx
`403702D4    lea     eax, [rcx+28h]
`403702D7    mov     [rsp+748h+var_62A], ax
`403702DF    mov     [rsp+748h+var_628], cx
`403702E7    mov     [rsp+748h+var_626], cx
`403702EF    lea     eax, [rcx+8]
`403702F2    mov     [rsp+748h+var_630], ax
`403702FA    mov     ecx, 1F80h
`403702FF    mov     [rsp+748h+var_634], ecx
`40370306    mov     eax, 27Fh
`4037030B    mov     [rsp+748h+var_568], ax
`40370313    mov     [rsp+748h+var_550], ecx
`4037031A    lea     rax, [rsp+748h+var_6D8]
`4037031F    mov     [rsp+748h+var_6E0], rax
`40370324    and     [rsp+748h+var_6E8], 0
`4037032A    and     [rsp+748h+var_6F0], 0
`40370330    mov     dword ptr [rsp+748h+var_6F8], r12d ; __int64
`40370335    lea     rax, [rsp+748h+var_6A8]
`4037033D    mov     [rsp+748h+var_700], rax ; __int64
`40370342    lea     rax, [rsp+748h+var_668]
`4037034A    mov     [rsp+748h+var_708], rax ; __int64
`4037034F    mov     rax, qword ptr [rsp+748h+var_190]
`40370357    mov     qword ptr [rsp+748h+var_710], rax ; int
`4037035C    mov     qword ptr [rsp+748h+var_718], rbx ; int
`40370361    lea     rax, [rsp+748h+var_198]
`40370369    mov     [rsp+748h+var_720], rax ; __int64
`4037036E    and     [rsp+748h+var_728], 0
`40370374    mov     r9, r15         ; __int64
`40370377    mov     r8, r14         ; __int64
`4037037A    mov     edx, r13d       ; __int64
`4037037D    mov     rcx, rsi        ; PVOID
`40370380    call    PspCreateThread

上面的反汇编指令片段是在 IDA 中截取的。根据汇编代码显示,[rsp+748h+var_6E0] 是传入 PspCreateThread 函数的第 14 个参数。其取值为 [rsp+748h+var_6D8] 局部变量的地址。var_6D8 是位于栈上的一个结构体对象,需要关注的是其中 +10 偏移的域,在稍早时候对结构体赋初值,这个域由第 9 个参数 [rsp+748h+arg_40] 赋值。

`403701DF    mov     [rsp+748h+var_6D8], bl
`403701E3    mov     rax, [rsp+748h+arg_40]
`403701EB    mov     [rsp+748h+var_6C8], rax

由第 9 个参数赋初值,那就说明这个值是由 NtCreateThreadEx 的调用者传入的。该函数原型如下:

NTSTATUS
NTAPI
Handle_SSDT_NtCreateThreadEx(
    OUT PHANDLE                  ThreadHandle,
    IN  ACCESS_MASK              DesiredAccess,
    IN  POBJECT_ATTRIBUTES       ObjectAttributes,
    IN  HANDLE                   ProcessHandle,
    IN  LPTHREAD_START_ROUTINE   StartAddress,
    IN  LPVOID                   Parameter,
    IN  BOOL                     CreateSuspended,
    IN  ULONG                    StackZeroBits,
    IN  ULONG                    SizeOfStackCommit,
    IN  ULONG                    SizeOfStackReserve,
    OUT LPVOID                   BytesBuffer
);

第 9 个参数是 SizeOfStackCommit,但我在 Hook 处理函数中监控这个参数,它的值始终是处于正常范围的数值,从未出现前面分析中的 0xFFFFF88000000000 这样的数据。突然注意到在 Hook 处理函数中调用原函数时对栈上的参数进行赋值的代码:

`050721af 488b8424d0000000 mov     rax,qword ptr [rsp+0D0h]
`050721b7 4889442450       mov     qword ptr [rsp+50h],rax
`050721bc 8b8424c8000000   mov     eax,dword ptr [rsp+0C8h]
`050721c3 89442448         mov     dword ptr [rsp+48h],eax
`050721c7 8b8424c0000000   mov     eax,dword ptr [rsp+0C0h]
`050721ce 89442440         mov     dword ptr [rsp+40h],eax
`050721d2 8b8424b8000000   mov     eax,dword ptr [rsp+0B8h]
`050721d9 89442438         mov     dword ptr [rsp+38h],eax
`050721dd 8b8424b0000000   mov     eax,dword ptr [rsp+0B0h]
`050721e4 89442430         mov     dword ptr [rsp+30h],eax
`050721e8 488b8424a8000000 mov     rax,qword ptr [rsp+0A8h]
`050721f0 4889442428       mov     qword ptr [rsp+28h],rax
`050721f5 488b8424a0000000 mov     rax,qword ptr [rsp+0A0h]
`050721fd 4889442420       mov     qword ptr [rsp+20h],rax
`05072202 4c8b8c2498000000 mov     r9,qword ptr [rsp+98h]
`0507220a 4c8b842490000000 mov     r8,qword ptr [rsp+90h]
`05072212 8b942488000000   mov     edx,dword ptr [rsp+88h]
`05072219 488b8c2480000000 mov     rcx,qword ptr [rsp+80h]
`05072221 ff542468         call    qword ptr [rsp+68h]

上面代码中 mov dword ptr [rsp+40h], eax 这条指令对 SizeOfStackCommit 参数进行传值。在传值之前我无意中看了一下 rsp+40h 地址的原值:

kd> dq [rsp+40h] l1
fffff880`0310ea30  fffff880`0310ea98

原来如此,由于 Hook 处理函数和原函数指针类型定义失误,将这个参数定义成了 ULONG32 类型,所以在传参时只通过 eax 进行赋值,栈上的参数位置高 32 位被忽略并保持原值;但在实际的 NtCreateThreadEx 函数中,应是将这个参数作为 ULONG_PTR 进行解析,在 64 位下应是 ULONG64 宽度,所以将参数中没有清零的高 32 位也作为参数值的一部分了,这就最终导致了前面的错误状态码。而这次的错误和虚拟化无关。

真是一次坑爹的分析。

- THE END -

文章链接: https://xiaodaozhi.com/kernel/31.html