http://blog.youkuaiyun.com/cosmoslife/article/details/14151245
很自然地 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,它意味着:
- SYSCALL_EIP 为 0
- SYSCALL_CS 为 0x10
- SYSRET_CS 为 0x23
在 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,意味着:
- NT
- DF
- IF
- TF
这些 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 原来的值。