紧接上文, 我们知道上面 NtReadVirtualMemory() 最终调用 syscall 指令进入内核, 接下来我门查看 syscall 指令的功能
syscall 指令(参考Intel白皮书) 1. 保存 R3 的 Rflags 到 R11 寄存器中, 并 & 上 ~(msr[0xC0000084]) 2. 保存 syscall 指令的下一条地址到 Rcx 中 3. 替换 CS 为 msr[0xC0000081] 的中 32 ~ 48 位的数值, SS = CS+0x8 4. 替换 Rip = msr[0xC0000082]
找到 Rip 对应的地址 KiSystemCall64() 函数
启用IDA分析(注意省略了部分代码,只保留了主线流程代码)
.text:000000014007F640 KiSystemCall64 proc near ; DATA XREF: KiInitializeBootStructures+26Eo .text:000000014007F640 .text:000000014007F640 var_1C0 = qword ptr -1C0h .text:000000014007F640 var_1B8 = qword ptr -1B8h .text:000000014007F640 var_1B0 = qword ptr -1B0h .text:000000014007F640 var_1A8 = qword ptr -1A8h .text:000000014007F640 var_1A0 = qword ptr -1A0h .text:000000014007F640 var_178 = byte ptr -178h .text:000000014007F640 var_110 = byte ptr -110h .text:000000014007F640 arg_F8 = qword ptr 100h .text:000000014007F640 .text:000000014007F640 swapgs ; 置换 GS 由TEB64变为 KPCR .text:000000014007F643 mov gs:10h, rsp ; KPCR.NtTib.StackLimit = R3Rsp .text:000000014007F64C mov rsp, gs:1A8h ; Rsp = KPCR.Prcb.RspBase(默认指向_KTRAP_FRAME.SegSs) .text:000000014007F655 push 2Bh ; KTRAP_FRAME.SegSs = 0x2B .text:000000014007F657 push qword ptr gs:10h ; KTRAP_FRAME.Rsp = R3Rsp .text:000000014007F65F push r11 ; KTRAP_FRAME.EFlags = R11 = Rflags .text:000000014007F661 push 33h ; KTRAP_FRAME.SegCs = 0x33 .text:000000014007F663 push rcx ; KTRAP_FRAME.Rip = Syscall指令的返回地址 .text:000000014007F664 mov rcx, r10 ; Rcx = 函数第一个参数 .text:000000014007F667 sub rsp, 8 ; 跳过 KTRAP_FRAME.ErrorCode .text:000000014007F66B push rbp ; KTRAP_FRAME.Rbp = R3Rbp .text:000000014007F66C sub rsp, 158h ; Rsp = &KTRAP_FRAME.P1Home .text:000000014007F673 lea rbp, [rsp+80h] ; Rbp = &KTRAP_FRAME.Xmm1 .text:000000014007F67B mov [rbp+0C0h], rbx ; KTRAP_FRAME.Rbx = Rbx .text:000000014007F682 mov [rbp+0C8h], rdi ; KTRAP_FRAME.Rdi = Rdi .text:000000014007F689 mov [rbp+0D0h], rsi ; KTRAP_FRAME.Rsi = Rsi .text:000000014007F690 mov byte ptr [rbp-55h], 2 ; KTRAP_FRAME.ExceptionActive = 0x2 .text:000000014007F694 mov rbx, gs:188h ; Rbx = KPCR.Prcb.CurrentThread .text:000000014007F69D prefetchw byte ptr [rbx+1D8h] ; CurrentThread.TrapFrame 保存到缓存 .text:000000014007F6A4 stmxcsr dword ptr [rbp-54h] ; 读取 KTRAP_FRAME.MxCsr .text:000000014007F6A8 ldmxcsr dword ptr gs:180h ; 写入到 KPCR.Prcb.MxCsr .text:000000014007F6B1 cmp byte ptr [rbx+3], 0 ; CurrentThread.Header.DebugActive 是否为 0x0 .text:000000014007F6B5 mov word ptr [rbp+80h], 0 ; KTRAP_FRAME.Dr7 = 0x0 .text:000000014007F6BE jz loc_14007F750 ; DebugActive 为 0x0 跳转, 我们这里假设没有调试跟进看看 .text:000000014007F6C4 mov [rbp-50h], rax ; KTRAP_FRAME.Rax = Rax .text:000000014007F6C8 mov [rbp-48h], rcx ; KTRAP_FRAME.Rcx = Rcx .text:000000014007F6CC mov [rbp-40h], rdx ; KTRAP_FRAME.Rdx = Rdx .text:000000014007F6D0 test byte ptr [rbx+3], 3 .text:000000014007F6D4 mov [rbp-38h], r8 ; KTRAP_FRAME.R8 = R8 .text:000000014007F6D8 mov [rbp-30h], r9 ; KTRAP_FRAME.R9 = R9 .text:000000014007F6DC jz short loc_14007F6E3 .text:000000014007F6DE call KiSaveDebugRegisterState ; 填充调试相关信息
.text:000000014007F750 loc_14007F750: ; CODE XREF: KiSystemCall64+7Ej .text:000000014007F750 sti ; 可被中断 .text:000000014007F751 mov [rbx+1E0h], rcx ; CurrentThread.FirstArgument = 函数第一个参数 .text:000000014007F758 mov [rbx+1F8h], eax ; CurretnThread.SystemCallNumber = 系统服务号 .text:000000014007F75E .text:000000014007F75E KiSystemServiceStart: ; DATA XREF: KiServiceInternal+5Ao .text:000000014007F75E ; .data:00000001401EE648o .text:000000014007F75E mov [rbx+1D8h], rsp ; CurrentThread.TrapFrame = Rsp(当前_KTRAP_FRAME 结构) .text:000000014007F765 mov edi, eax ; Rdi = 系统服务号 .text:000000014007F767 shr edi, 7 ; Rdi = 系统服务号 >> 7 .text:000000014007F76A and edi, 20h ; Rdi = (系统服务号 >> 7) & 0x20 = 服务表的索引 .text:000000014007F76D and eax, 0FFFh ; EAX = 系统服务号 .text:000000014007F772 .text:000000014007F772 KiSystemServiceRepeat: ; CODE XREF: KiSystemCall64+47Bj .text:000000014007F772 lea r10, KeServiceDescriptorTable .text:000000014007F779 lea r11, KeServiceDescriptorTableShadow .text:000000014007F780 test dword ptr [rbx+100h], 80h ; CurrentThread.GuiThread 是否为 0x0(是否是GUI线程) .text:000000014007F78A cmovnz r10, r11 ; 是GUI线程则 R10 = R11 = KeServiceDescriptorTableShadow .text:000000014007F78E cmp eax, [rdi+r10+10h] ; EAX 与 系统服务表函数个数 比较(判断是否越界) .text:000000014007F793 jnb loc_14007FA82 ; 大于跳转 .text:000000014007F799 mov r10, [rdi+r10] ; R10 = 系统服务表的函数地址表 .text:000000014007F79D movsxd r11, dword ptr [r10+rax*4] ; R11 = 加密的SSDT表数据 .text:000000014007F7A1 mov rax, r11 ; Rax = 加密的SSDT表数据 .text:000000014007F7A4 sar r11, 4 ; R11 算术位移 4 位(高位添1或0, 由符号位决定) .text:000000014007F7A8 add r10, r11 ; R10 = 系统服务表的函数地址表 + R11 = 真正系统函数地址 .text:000000014007F7AB cmp edi, 20h ; 判断服务表索引, 是否为影子表 .text:000000014007F7AE jnz short loc_14007F800 ; 不是跳转, 不为影子表跟进看看 .text:000000014007F7B0 mov r11, [rbx+0B8h] ; R11 = CurrentThread.Teb
.text:000000014007F800 loc_14007F800: ; CODE XREF: KiSystemCall64+16Ej .text:000000014007F800 ; KiSystemCall64+17Fj .text:000000014007F800 and eax, 0Fh ; 获取 加密的SSDT表数据 低4位(也就是内核函数参数个数) .text:000000014007F803 jz KiSystemServiceCopyEnd ; 如果没有参数(低4位为0x0)跳转 .text:000000014007F809 shl eax, 3 ; EAX 左移 3位(Eax * 8) .text:000000014007F80C lea rsp, [rsp-70h] ; 开辟堆栈(用于存储Ring3的参数) .text:000000014007F811 lea rdi, [rsp+18h] .text:000000014007F816 mov rsi, [rbp+100h] ; Rsi = R3Rsp = KTRAP_FRAME.Rsp .text:000000014007F81D lea rsi, [rsi+20h] ; Rsi = 函数第五个参数地址-0x8 .text:000000014007F821 test byte ptr [rbp+0F0h], 1 ; KTRAP_FRAME.SegCs 与 0x1 比较, 判断几环过来 .text:000000014007F828 jz short loc_14007F840 ; KernelMode模式跳转 .text:000000014007F82A cmp rsi, cs:MmUserProbeAddress ; 函数第五个参数地址-0x8 与 000007ff`ffff0000 比较 .text:000000014007F831 cmovnb rsi, cs:MmUserProbeAddress ; 大于则 函数第五个参数地址-0x8 = 0x00007ff`ffff0000 .text:000000014007F839 nop dword ptr [rax+00000000h] .text:000000014007F840 .text:000000014007F840 loc_14007F840: ; CODE XREF: KiSystemCall64+1E8j .text:000000014007F840 lea r11, KiSystemServiceCopyEnd .text:000000014007F847 sub r11, rax ; KiSystemServiceCopyEnd - Rax = 拷贝几个参数到 Ring0 堆栈 .text:000000014007F84A jmp r11
.text:000000014007F850 KiSystemServiceCopyStart: ; DATA XREF: KiSystemServiceHandler+1Ao .text:000000014007F850 mov rax, [rsi+70h] .text:000000014007F854 mov [rdi+70h], rax .text:000000014007F858 mov rax, [rsi+68h] .text:000000014007F85C mov [rdi+68h], rax .text:000000014007F860 mov rax, [rsi+60h] .text:000000014007F864 mov [rdi+60h], rax .text:000000014007F868 mov rax, [rsi+58h] .text:000000014007F86C mov [rdi+58h], rax .text:000000014007F870 mov rax, [rsi+50h] .text:000000014007F874 mov [rdi+50h], rax .text:000000014007F878 mov rax, [rsi+48h] .text:000000014007F87C mov [rdi+48h], rax .text:000000014007F880 mov rax, [rsi+40h] .text:000000014007F884 mov [rdi+40h], rax .text:000000014007F888 mov rax, [rsi+38h] .text:000000014007F88C mov [rdi+38h], rax .text:000000014007F890 mov rax, [rsi+30h] .text:000000014007F894 mov [rdi+30h], rax .text:000000014007F898 mov rax, [rsi+28h] .text:000000014007F89C mov [rdi+28h], rax .text:000000014007F8A0 mov rax, [rsi+20h] .text:000000014007F8A4 mov [rdi+20h], rax .text:000000014007F8A8 mov rax, [rsi+18h] .text:000000014007F8AC mov [rdi+18h], rax .text:000000014007F8B0 mov rax, [rsi+10h] .text:000000014007F8B4 mov [rdi+10h], rax .text:000000014007F8B8 mov rax, [rsi+8] ; Rax = 函数的第五个参数 .text:000000014007F8BC mov [rdi+8], rax ; 拷贝到 Ring0 的堆栈中 .text:000000014007F8C0 .text:000000014007F8C0 KiSystemServiceCopyEnd: ; CODE XREF: KiSystemCall64+1C3j .text:000000014007F8C0 ; DATA XREF: KiSystemServiceHandler+27o ... .text:000000014007F8C0 test cs:PerfGlobalGroupMask_0x8, 40h .text:000000014007F8CA jnz loc_14007FB20 .text:000000014007F8D0 call r10 ; 调用 内核函数地址 .text:000000014007F8D3 .text:000000014007F8D3 loc_14007F8D3: ; CODE XREF: KiSystemCall64+535j .text:000000014007F8D3 inc dword ptr gs:2238h ; KPCR.Prcb.KeSystemCalls += 0x1 .text:000000014007F8DB .text:000000014007F8DB KiSystemServiceExit: ; CODE XREF: KiSystemCall64+49Cj .text:000000014007F8DB ; KiSystemCall64+4A7j .text:000000014007F8DB ; DATA XREF: ... .text:000000014007F8DB mov rbx, [rbp+0C0h] ; Rbx = KTRAP_FRAME.Rbx .text:000000014007F8E2 mov rdi, [rbp+0C8h] ; Rdi = KTRAP_FRAME.Rdi .text:000000014007F8E9 mov rsi, [rbp+0D0h] ; Rsi = KTRAP_FRAME.Rsi .text:000000014007F8F0 mov r11, gs:188h ; R11 = KPCR.Prcb.CurrentThread .text:000000014007F8F9 test byte ptr [rbp+0F0h], 1 ; KTRAP_FRAME.SegCs 与 0x1 比较, 判断是几环过来 .text:000000014007F900 jz loc_14007FA55 ; KernelMode跳转 .text:000000014007F906 mov rcx, cr8 .text:000000014007F90A or cl, [r11+1F0h] ; CL |= CurrentThread.ApcStateIndex .text:000000014007F911 or ecx, [r11+1C4h] ; ECX |= CurrentThread.KernelApcDisable .text:000000014007F918 jnz loc_14007FAEC .text:000000014007F91E cli ; 关闭中断 .text:000000014007F91F mov rcx, gs:188h ; Rcx = KPCR.Prcb.CurrentThread .text:000000014007F928 cmp byte ptr [rcx+7Ah], 0 ; CurrentThread.ApcStateFill[0x2A] 与 0x0 比较 .text:000000014007F92C jz short loc_14007F985 ; 判断是否有 APC 需要执行 .text:000000014007F92E mov [rbp-50h], rax ; KTRAP_FRAME.Rax = Rax .text:000000014007F932 xor eax, eax .text:000000014007F934 mov [rbp-48h], rax ; KTRAP_FRAME.Rcx = 0x0 .text:000000014007F938 mov [rbp-40h], rax ; KTRAP_FRAME.Rdx = 0x0 .text:000000014007F93C mov [rbp-38h], rax ; KTRAP_FRAME.R8 = 0x0 .text:000000014007F940 mov [rbp-30h], rax ; KTRAP_FRAME.R9 = 0x0 .text:000000014007F944 mov [rbp-28h], rax ; KTRAP_FRAME.R10 = 0x0 .text:000000014007F948 mov [rbp-20h], rax ; KTRAP_FRAME.R11 = 0x0 .text:000000014007F94C pxor xmm0, xmm0 .text:000000014007F950 movaps xmmword ptr [rbp-10h], xmm0 .text:000000014007F954 movaps xmmword ptr [rbp+0], xmm0 .text:000000014007F958 movaps xmmword ptr [rbp+10h], xmm0 .text:000000014007F95C movaps xmmword ptr [rbp+20h], xmm0 .text:000000014007F960 movaps xmmword ptr [rbp+30h], xmm0 .text:000000014007F964 movaps xmmword ptr [rbp+40h], xmm0 .text:000000014007F968 mov ecx, 1 .text:000000014007F96D mov cr8, rcx .text:000000014007F971 sti .text:000000014007F972 call KiInitiateUserApc ; 处理 User APC .text:000000014007F977 cli .text:000000014007F978 mov ecx, 0 .text:000000014007F97D mov cr8, rcx .text:000000014007F981 mov rax, [rbp-50h] .text:000000014007F985 .text:000000014007F985 loc_14007F985: ; CODE XREF: KiSystemCall64+2ECj .text:000000014007F985 mov rcx, gs:188h ; Rcx = CurrentThread .text:000000014007F98E test dword ptr [rcx], 40020000h ; CurrentThread.Header.Type 与 0x40020000 比较 .text:000000014007F994 jz short loc_14007F9C4 .text:000000014007F996 mov [rbp-50h], rax .text:000000014007F99A test byte ptr [rcx+2], 2 .text:000000014007F99E jz short loc_14007F9AE .text:000000014007F9A0 call KiCopyCounters .text:000000014007F9A5 mov rcx, gs:188h .text:000000014007F9AE .text:000000014007F9AE loc_14007F9AE: ; CODE XREF: KiSystemCall64+35Ej .text:000000014007F9AE test byte ptr [rcx+3], 40h .text:000000014007F9B2 jz short loc_14007F9C0 .text:000000014007F9B4 lea rsp, [rbp-80h] .text:000000014007F9B8 xor rcx, rcx .text:000000014007F9BB call KiUmsExit .text:000000014007F9C0 .text:000000014007F9C0 loc_14007F9C0: ; CODE XREF: KiSystemCall64+372j .text:000000014007F9C0 mov rax, [rbp-50h] .text:000000014007F9C4 .text:000000014007F9C4 loc_14007F9C4: ; CODE XREF: KiSystemCall64+354j .text:000000014007F9C4 ldmxcsr dword ptr [rbp-54h] .text:000000014007F9C8 xor r10, r10 .text:000000014007F9CB cmp word ptr [rbp+80h], 0 .text:000000014007F9D3 jz short loc_14007FA13 .text:000000014007F9D5 mov [rbp-50h], rax .text:000000014007F9D9 call KiRestoreDebugRegisterState .text:000000014007F9DE mov rax, gs:188h .text:000000014007F9E7 mov rax, [rax+70h] .text:000000014007F9EB mov rax, [rax+100h] .text:000000014007F9F2 or rax, rax .text:000000014007F9F5 jz short loc_14007FA0F .text:000000014007F9F7 cmp word ptr [rbp+0F0h], 33h .text:000000014007F9FF jnz short loc_14007FA0F .text:000000014007FA01 mov r10, [rbp+0E8h] .text:000000014007FA08 mov [rbp+0E8h], rax .text:000000014007FA0F .text:000000014007FA0F loc_14007FA0F: ; CODE XREF: KiSystemCall64+3B5j .text:000000014007FA0F ; KiSystemCall64+3BFj .text:000000014007FA0F mov rax, [rbp-50h] .text:000000014007FA13 .text:000000014007FA13 loc_14007FA13: ; CODE XREF: KiSystemCall64+393j .text:000000014007FA13 mov r8, [rbp+100h] ; R8 = KTRAP_FRAME.Rsp = R3Rsp .text:000000014007FA1A mov r9, [rbp+0D8h] ; R9 = KTRAP_FRAME.Rbp .text:000000014007FA21 xor edx, edx .text:000000014007FA23 pxor xmm0, xmm0 .text:000000014007FA27 pxor xmm1, xmm1 .text:000000014007FA2B pxor xmm2, xmm2 .text:000000014007FA2F pxor xmm3, xmm3 .text:000000014007FA33 pxor xmm4, xmm4 .text:000000014007FA37 pxor xmm5, xmm5 .text:000000014007FA3B mov rcx, [rbp+0E8h] ; Rcx = KTRAP_FRAME.Rip = syscall 指令的返回地址 .text:000000014007FA42 mov r11, [rbp+0F8h] ; R11 = KTRAP_FRAME.EFlags .text:000000014007FA49 mov rbp, r9 .text:000000014007FA4C mov rsp, r8 .text:000000014007FA4F swapgs ; 置换 GS 为TEB64 .text:000000014007FA52 sysret ; 返回Ring3, 该指令执行动作如下: .text:000000014007FA52 ; 1. 将 Rcx 的值存入 Rip 中 .text:000000014007FA52 ; 2. R11 的值存入 Rflags 中 .text:000000014007FA52 ; 3. 将 msr[c0000081] 的 48~63 位加载到CS, SS = CS + 0x8 .text:000000014007FA55 .text:000000014007FA55 loc_14007FA55: ; CODE XREF: KiSystemCall64+2C0j .text:000000014007FA55 mov rdx, [rbp+0B8h] .text:000000014007FA5C mov [r11+1D8h], rdx .text:000000014007FA63 mov dl, [rbp-58h] .text:000000014007FA66 mov [r11+1F6h], dl .text:000000014007FA6D cli .text:000000014007FA6E mov rsp, rbp .text:000000014007FA71 mov rbp, [rbp+0D8h] .text:000000014007FA78 mov rsp, [rsp+arg_F8] .text:000000014007FA80 sti .text:000000014007FA81 retn
1. 以上为 X64 内核系统调用的过程, 与 X86 相比, Ring3和Ring0各有一套SSDT和查表方式
2. X64 内核函数地址的计算也发生了改变, 最后SSDT表数据最后4位保存内核函数参数个数, 以及拷贝Ring3数据方式通过 KiSystemServiceCopyStart 函数拷贝, X64系统调用大体就是这样了