win64系统调用

win64系统调用比win32要简洁不少,因为寄存器增多,切换起来更方便了。还是看CreateFile的例子,直接看ntdll!ZwCreateFile。


lkd> uf ntdll!NtCreateFile
ntdll!NtCreateFile:
00000000`76f50400 4c8bd1          mov     r10,rcx
00000000`76f50403 b852000000      mov     eax,52h
00000000`76f50408 0f05            syscall
00000000`76f5040a c3              ret
lkd> uf ntdll!ZwCreateFile
ntdll!NtCreateFile:
00000000`76f50400 4c8bd1          mov     r10,rcx
00000000`76f50403 b852000000      mov     eax,52h
00000000`76f50408 0f05            syscall
00000000`76f5040a c3              ret

rcx保存在r10中,eax保存SSDT的下标,然后执行syscall指令。这个指令在Intel上只支持64位,官方文档是这样说的:


SYSCALL saves the RIP of the instruction following SYSCALL to RCX and loads a new 
RIP from the IA32_LSTAR (64-bit mode). Upon return, SYSRET copies the value 
saved in RCX to the RIP. 
SYSCALL saves RFLAGS (lower 32 bit only) in R11. It then masks RFLAGS with an 
OS-defined value using the IA32_FMASK (MSR C000_0084). The actual mask value 
used by the OS is the complement of the value written to the IA32_FMASK MSR. 
None of the bits in RFLAGS are automatically cleared (except for RF). SYSRET 
restores RFLAGS from R11 (the lower 32 bits only).
Software should not alter the CS or SS descriptors in a manner that violates the 
following assumptions made by SYSCALL/SYSRET:
	 The CS and SS base and limit remain the same for all processes, including the 
operating system (the base is 0H and the limit is 0FFFFFFFFH).
	 The CS of the SYSCALL target has a privilege level of 0.
	 The CS of the SYSRET targethas a privilege level of 3.
SYSCALL/SYSRET do not check for violations of these assumptions.

将RIP保存在RCX中,将IA32_LSTAR寄存器的值载入RIP作为RING0将要执行的地址。具体的变化如下:


IF (CS.L != 1 ) or (IA32_EFER.LMA !=1) or (IA32_EFER.SCE !=1)
(* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *)
THEN #UD; FI;
RCX ←RIP;
RIP ←LSTAR_MSR;
R11 ←EFLAGS;
EFLAGS ←(EFLAGS MASKED BY IA32_FMASK);
CPL ←0;
CS(SEL) ←IA32_STAR_MSR[47:32];
CS(DPL) ←0;
CS(BASE) ←0;
CS(LIMIT) ←0xFFFFF;
CS(GRANULAR) ←1;
SS(SEL) ←IA32_STAR_MSR[47:32] + 8;
SS(DPL) ←0;
SS(BASE) ←0;
SS(LIMIT) ←0xFFFFF;
SS(GRANULAR) ←1;

涉及到几个寄存器:IA32_LSTAR,IA32_STAR_MSR,IA32_FMASK,它们的地址分别是0xC0000082、0xC0000081、0xC0000084,还有一个IA32_CSTAR用于兼容模式,地址是0xC0000083。


kd> rdmsr C0000081
msr[c0000081] = 00230010`00000000
kd> rdmsr C0000082
msr[c0000082] = fffff800`03e7bec0
kd> rdmsr C0000084
msr[c0000084] = 00000000`00004700
kd> u fffff80003e7bec0
nt!KiSystemCall64:
fffff800`03e7bec0 0f01f8          swapgs
fffff800`03e7bec3 654889242510000000 mov   qword ptr gs:[10h],rsp
fffff800`03e7becc 65488b2425a8010000 mov   rsp,qword ptr gs:[1A8h]
fffff800`03e7bed5 6a2b            push    2Bh
fffff800`03e7bed7 65ff342510000000 push    qword ptr gs:[10h]
fffff800`03e7bedf 4153            push    r11
fffff800`03e7bee1 6a33            push    33h
fffff800`03e7bee3 51              push    rcx
kd> rdmsr C0000083
msr[c0000083] = fffff800`03e7bc00
kd> u fffff80003e7bc00
nt!KiSystemCall32:
fffff800`03e7bc00 0f01f8          swapgs
fffff800`03e7bc03 654889242510000000 mov   qword ptr gs:[10h],rsp
fffff800`03e7bc0c 65488b2425a8010000 mov   rsp,qword ptr gs:[1A8h]
fffff800`03e7bc15 6a2b            push    2Bh
fffff800`03e7bc17 65ff342510000000 push    qword ptr gs:[10h]
fffff800`03e7bc1f 4153            push    r11
fffff800`03e7bc21 6a23            push    23h
fffff800`03e7bc23 51              push    rcx


KiSystemCall64或KiSystemCall32分发找到系统服务。

再看一下内核中调用ZwCreateFile会发生什么。


kd> uf nt!ZwCreateFile
Flow analysis was incomplete, some code may be missing
nt!ZwCreateFile:
fffff800`03e75f00 488bc4          mov     rax,rsp
fffff800`03e75f03 fa              cli
fffff800`03e75f04 4883ec10        sub     rsp,10h
fffff800`03e75f08 50              push    rax
fffff800`03e75f09 9c              pushfq
fffff800`03e75f0a 6a10            push    10h
fffff800`03e75f0c 488d05dd270000  lea     rax,[nt!KiServiceLinkage (fffff800`03e786f0)]
fffff800`03e75f13 50              push    rax
fffff800`03e75f14 b852000000      mov     eax,52h
fffff800`03e75f19 e9225f0000      jmp     nt!KiServiceInternal (fffff800`03e7be40)

nt!KiServiceInternal:
fffff800`03e7be40 4883ec08        sub     rsp,8
fffff800`03e7be44 55              push    rbp
fffff800`03e7be45 4881ec58010000  sub     rsp,158h
fffff800`03e7be4c 488dac2480000000 lea     rbp,[rsp+80h]
fffff800`03e7be54 48899dc0000000  mov     qword ptr [rbp+0C0h],rbx
fffff800`03e7be5b 4889bdc8000000  mov     qword ptr [rbp+0C8h],rdi
fffff800`03e7be62 4889b5d0000000  mov     qword ptr [rbp+0D0h],rsi
fffff800`03e7be69 fb              sti
fffff800`03e7be6a 65488b1c2588010000 mov   rbx,qword ptr gs:[188h]
fffff800`03e7be73 0f0d8bd8010000  prefetchw [rbx+1D8h]
fffff800`03e7be7a 0fb6bbf6010000  movzx   edi,byte ptr [rbx+1F6h]
fffff800`03e7be81 40887da8        mov     byte ptr [rbp-58h],dil
fffff800`03e7be85 c683f601000000  mov     byte ptr [rbx+1F6h],0
fffff800`03e7be8c 4c8b93d8010000  mov     r10,qword ptr [rbx+1D8h]
fffff800`03e7be93 4c8995b8000000  mov     qword ptr [rbp+0B8h],r10
fffff800`03e7be9a 4c8d1d3d010000  lea     r11,[nt!KiSystemServiceStart (fffff800`03e7bfde)]
fffff800`03e7bea1 41ffe3          jmp     r11

有三个函数非常显眼:KiServiceLinkage、KiServiceInternal、KiSystemServiceStart。KiServiceLinkage就是一句ret,KiServiceInternal做了一些简单处理后调用了KiSystemServiceStart。KiSystemServiceStart又调用了KiSystemCall64,说到底还是这哥们儿在发挥作用。

KiSystemCall64又调用了KiSystemServiceRepeat等发挥作用。KiSystemServiceRepeat这个函数很关键,是查找SSDT和Shadow SSDT的关键所在。