windows 7 x64 下的 System Call


很自然地 windows 7 x64 版本会使用 processor 提供的 syscall/sysret 指令来构造一个快速的调用系统服务例程机制。

ntdll!NtCreateDebugObject:
00000000`76d70680 4c8bd1          mov     r10,rcx
00000000`76d70683 b890000000      mov     eax,90h
00000000`76d70688 0f05            syscall
00000000`76d7068a c3              ret

上面是 ntdll 模式中的 NtCreateDebugObject() 实现,它使用 syscall 指令切换到 kernel,调用内核提供的例程。

关于对 syscall/sysret 指令的详解,请见:http://www.mouseos.com/arch/syscall_sysret.html

我们要想看 syscall 指令进入哪里,可以查看 MSR_LSTAR 寄存器的值,在 windbg 的内核调试模式下,使用 rdmsr 命令观察 MSR_LSTAR 寄存器值:

kd> rdmsr c0000082
msr[c0000082] = fffff800`03cc4ec0

上面的结果显示 fffff800`03cc4ec0 就是 MSR_LSTAR 里的值,它是 syscall 指令的进入点。

我们看看它是什么代码,下面截取了一部分:

nt!KiSystemCall64:
fffff800`03cc4ec0 0f01f8          swapgs
fffff800`03cc4ec3 654889242510000000 mov   qword ptr gs:[10h],rsp
fffff800`03cc4ecc 65488b2425a8010000 mov   rsp,qword ptr gs:[1A8h]
fffff800`03cc4ed5 6a2b            push    2Bh
fffff800`03cc4ed7 65ff342510000000 push    qword ptr gs:[10h]
fffff800`03cc4edf 4153            push    r11
fffff800`03cc4ee1 6a33            push    33h
fffff800`03cc4ee3 51              push    rcx
fffff800`03cc4ee4 498bca          mov     rcx,r10
fffff800`03cc4ee7 4883ec08        sub     rsp,8
fffff800`03cc4eeb 55              push    rbp
fffff800`03cc4eec 4881ec58010000  sub     rsp,158h
fffff800`03cc4ef3 488dac2480000000 lea     rbp,[rsp+80h]
fffff800`03cc4efb 48899dc0000000  mov     qword ptr [rbp+0C0h],rbx
fffff800`03cc4f02 4889bdc8000000  mov     qword ptr [rbp+0C8h],rdi
fffff800`03cc4f09 4889b5d0000000  mov     qword ptr [rbp+0D0h],rsi
fffff800`03cc4f10 c645ab02        mov     byte ptr [rbp-55h],2
fffff800`03cc4f14 65488b1c2588010000 mov   rbx,qword ptr gs:[188h]
fffff800`03cc4f1d 0f0d8bd8010000  prefetchw [rbx+1D8h]
fffff800`03cc4f24 0fae5dac        stmxcsr dword ptr [rbp-54h]
fffff800`03cc4f28 650fae142580010000 ldmxcsr dword ptr gs:[180h]
fffff800`03cc4f31 807b0300        cmp     byte ptr [rbx+3],0
fffff800`03cc4f35 66c785800000000000 mov   word ptr [rbp+80h],0
fffff800`03cc4f3e 0f848c000000    je      nt!KiSystemCall64+0x110 (fffff800`03cc4fd0)

syscall 的入口点是 KiSystemCall64() ,系统在 KiInitializeBootStructures() 里对 syscall/sysret 执行环境进行了设置:

nt!KiInitializeBootStructures+0x233:
fffff800`03f12f63 498b442408      mov     rax,qword ptr [r12+8]
fffff800`03f12f68 b968000000      mov     ecx,68h
fffff800`03f12f6d 66894866        mov     word ptr [rax+66h],cx
fffff800`03f12f71 48b80000000010002300 mov rax,23001000000000h
fffff800`03f12f7b b9810000c0      mov     ecx,0C0000081h                         ; MSR_STAR
fffff800`03f12f80 488bd0          mov     rdx,rax
fffff800`03f12f83 48c1ea20        shr     rdx,20h
fffff800`03f12f87 0f30            wrmsr
fffff800`03f12f89 488d05701cdbff  lea     rax,[nt!KiSystemCall32 (fffff800`03cc4c00)]
fffff800`03f12f90 b9830000c0      mov     ecx,0C0000083h                         ; MSR_CSTAR
fffff800`03f12f95 488bd0          mov     rdx,rax
fffff800`03f12f98 48c1ea20        shr     rdx,20h
fffff800`03f12f9c 0f30            wrmsr
fffff800`03f12f9e 488d051b1fdbff  lea     rax,[nt!KiSystemCall64 (fffff800`03cc4ec0)]
fffff800`03f12fa5 b9820000c0      mov     ecx,0C0000082h                         ; MSR_LSTAR
fffff800`03f12faa 488bd0          mov     rdx,rax
fffff800`03f12fad 48c1ea20        shr     rdx,20h
fffff800`03f12fb1 0f30            wrmsr
fffff800`03f12fb3 b800470000      mov     eax,4700h
fffff800`03f12fb8 b9840000c0      mov     ecx,0C0000084h                         ; MSR_SFMASK
fffff800`03f12fbd 488bd0          mov     rdx,rax
fffff800`03f12fc0 48c1ea20        shr     rdx,20h
fffff800`03f12fc4 0f30            wrmsr
fffff800`03f12fc6 85ed            test    ebp,ebp
fffff800`03f12fc8 750a            jne     nt!KiInitializeBootStructures+0x2a4 (fffff800`03f12fd4)

MSR_STAR 寄存器里的值被设为 23001000000000h,它意味着:

在 SYSRET_CS 中,SYSRET_CS.RPL = 3 返回的权限级别是 3 级(用户代码)。

MSR_CSTAR 寄存器被设为 nt!KiSystemCall32 (fffff800`03cc4c00) 地址值,这是为了 compaitibility 模式代码调用而设置的。

MSR_LSTAR 寄存器被设为 nt!KiSystemCall64 (fffff800`03cc4ec0) 地址值,是为 64-bit 模式而准备的。

MSR_SFMASK 寄存器设为 4700h,意味着:

这些 rflags 寄存器中的标志位在进入 KiSystemCall64() 后会被清 0

那么,对于要进入系统调用的代码来说:

ntdll!NtCreateDebugObject:
00000000`76d70680 4c8bd1          mov     r10,rcx        ; 保存原来的 rcx 的值,rcx 将被置为 rip(返回值)
00000000`76d70683 b890000000      mov     eax,90h        ; 系统调用号
00000000`76d70688 0f05            syscall
00000000`76d7068a c3              ret

执行 syscall 后,rcx 会保存返回值,因此应该要保存 rcx 原来的值。


版权 mik 所有,转载请注明出处