对XP上的KiFastSystemCall进行浅析
Windows API的系统调用过程通过KiFastSystemCall或int 2e进入内核,本文仅对XP上的KiFastSystemCall进行浅析。
以ntdll!ZwCreateProcessEx为例:
Eax中保存系统调用号,此处ZwCreateProcessEx的为30h;Edx是SharedUserData!SystemCallStub的地址,里面保存着KiFastSystemCall的地址。SharedUserData总是存放在0x7ffe0000处,其偏移0x300处正是SystemCall。
证实下0x7ffe0300处是否真保存着KiFastSystemCall的地址:
Sysenter时eax中保存着系统调用号,edx中保存着用户空间线程栈栈顶地址即保存着ZwCreateProcessEx中call KiFastSystemCall的返回地址,那么edx+4保存着call ZwCreateProcessEx的返回地址,edx+8为ZwCreateProcessEx的第一个参数,以此类推。
Sysenter将会从MSR寄存器组中加载cs:eip和ss:esp令cs=IA32_SYSENTER_CS、eip=IA32_SYSENTER_EIP、ss=IA32_SYSENTER_CS+8、esp=IA32_SYSENTER_ESP,这些值均在系统初始化时固定下来。
以ntdll!ZwCreateProcessEx为例:
Eax中保存系统调用号,此处ZwCreateProcessEx的为30h;Edx是SharedUserData!SystemCallStub的地址,里面保存着KiFastSystemCall的地址。SharedUserData总是存放在0x7ffe0000处,其偏移0x300处正是SystemCall。
证实下0x7ffe0300处是否真保存着KiFastSystemCall的地址:
Sysenter时eax中保存着系统调用号,edx中保存着用户空间线程栈栈顶地址即保存着ZwCreateProcessEx中call KiFastSystemCall的返回地址,那么edx+4保存着call ZwCreateProcessEx的返回地址,edx+8为ZwCreateProcessEx的第一个参数,以此类推。
Sysenter将会从MSR寄存器组中加载cs:eip和ss:esp令cs=IA32_SYSENTER_CS、eip=IA32_SYSENTER_EIP、ss=IA32_SYSENTER_CS+8、esp=IA32_SYSENTER_ESP,这些值均在系统初始化时固定下来。
代码:
0: kd> u 7c92e514 ntdll!KiFastSystemCallRet: 7c92e514 c3 ret ; 与call dword ptr [edx]相对应 lkd> rdmsr 174 msr[174] = 00000000`00000008 ; cs=0x0008 ss=0x0010 lkd> rdmsr 175 msr[175] = 00000000`f78b3000 ; esp=0xf78b3000 DPC Stack lkd> rdmsr 176 msr[176] = 00000000`804de89f ; eip=0x804de89f KiFastCallEntry sysenter切换堆栈时是从用户栈切换到DPC栈,这是因为MSR寄存器中的值是操作系统安排好的固定的值,它与具体的线程上下文无关,所以需要在DPC栈中再切换到线程的内核栈。 nt!KiFastCallEntry: 804de89f b923000000 mov ecx,23h ; KGDT_R3_DATA 0x23 = 0x20 + 011b(CPL Ring3) 804de8a4 6a30 push 30h ; KGDT_R0_PCR 0x30 = 0x30 + 000b(CPL Ring0), DPC Stack 804de8a6 0fa1 pop fs ; DPC Stack 804de8a8 8ed9 mov ds,cx 804de8aa 8ec1 mov es,cx 804de8ac 648b0d40000000 mov ecx,dword ptr fs:[40h] ; fs Base:ffdff000 Limit:1fff Data RW, KPCR 0x40处为TSS Ptr32 _KTSS 804de8b3 8b6104 mov esp,dword ptr [ecx+4] ; KTSS 0x4处为Esp0 Uint4B, Ring0下的esp 804de8b6 6a23 push 23h ; 此时已在内核堆栈, Ring3下的ss, KTRAP_FRAME.HardwareSegSs 804de8b8 52 push edx ; Ring3下的esp, KTRAP_FRAME.HardwareEsp 804de8b9 9c pushfd ; eflags ; KTRAP_FRAME.EFlags 804de8ba 6a02 push 2 804de8bc 83c208 add edx,8 ; Ring3堆栈的参数 804de8bf 9d popfd ; 初始eflags为2, 即各位清零 804de8c0 804c240102 or byte ptr [esp+1],2 ; 4字节eflags第二个字节的IF中断允许位置1 804de8c5 6a1b push 1Bh ; KGDT_R3_CODE 0x1B = 0x18 + 011b(CPL Ring3), KTRAP_FRAME.SegCs 804de8c7 ff350403dfff push dword ptr ds:[0FFDF0304h] ; KTRAP_FRAME.EIP, 0x7ffe0000与0xffdf000映射到同一块物理内存, 0xffdf0304处存放的是KiFastSystemCallRet 804de8cd 6a00 push 0 ; KTRAP_FRAME.ErrCode ; 以下四个寄存器从Ring3到Ring0没修改过, 直接保存 804de8cf 55 push ebp ; KTRAP_FRAME.Ebp 804de8d0 53 push ebx ; KTRAP_FRAME.Ebx 804de8d1 56 push esi ; KTRAP_FRAME.Esi 804de8d2 57 push edi ; KTRAP_FRAME.Edi 804de8d3 648b1d1c000000 mov ebx,dword ptr fs:[1Ch] ; 指向自己的指针, 头部即是TIB 804de8da 6a3b push 3Bh ; KTRAP_FRAME.SegFs, fs Base:00000000 Limit:fff Data RW, Ring3 804de8dc 8bb324010000 mov esi,dword ptr [ebx+124h] ; KPCR.SelfPcr->PrcbData.CurrentThead Ptr32 _KTHREAD 804de8e2 ff33 push dword ptr [ebx] ; KTRAP_FRAME.ExceptionList, TIB的头部即是ExceptionList 804de8e4 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh 804de8ea 8b6e18 mov ebp,dword ptr [esi+18h] ; KTHREAD.InitialStack Ptr32 Void 804de8ed 6a01 push 1 ; KTRAP_FRAME.PreviousPreviousMode 804de8ef 83ec48 sub esp,48h ; 预留KTRAP_FRAME中Eax到DbgEbp的空间 804de8f2 81ed9c020000 sub ebp,29Ch ; ... 804de8f8 c6864001000001 mov byte ptr [esi+140h],1 ; KTHREAD.PreviousMode 804de8ff 3bec cmp ebp,esp 804de901 0f8565ffffff jne nt!KiFastCallEntry2+0x25 (804de86c) 804de907 83652c00 and dword ptr [ebp+2Ch],0 ; KTRAP_FRAME第12个(0x2C/4+1)参数Dr7 804de90b f6462cff test byte ptr [esi+2Ch],0FFh ; KTHREAD.DebugActive 804de90f 89ae34010000 mov dword ptr [esi+134h],ebp ; KTHREAD.TrapFrame Ptr32 _KTRAP_FRAME 804de915 0f8535feffff jne nt!Dr_FastCallDrSave (804de750) ; if(KTHREAD.DebugActive != 0) ... 804de91b 8b5d60 mov ebx,dword ptr [ebp+60h] 804de91e 8b7d68 mov edi,dword ptr [ebp+68h] 804de921 89550c mov dword ptr [ebp+0Ch],edx ; 将Ring3参数地址赋给KTRAP_FRAME.DbgArgPointer 804de924 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h ; KTRAP_FRAME.DbgArgMark = 0xBADB0D00 804de92b 895d00 mov dword ptr [ebp],ebx ; KTRAP_FRAME.DbgEbp = KTRAP_FRAME.Ebp 804de92e 897d04 mov dword ptr [ebp+4],edi ; KTRAP_FRAME.DbgEip = KTRAP_FRAME.Eip 804de931 fb sti ; 当调用KeServiceDescriptorTableShadow中的系统服务时, eax是系统调用号再加0x1000 804de932 8bf8 mov edi,eax 804de934 c1ef08 shr edi,8 804de937 83e730 and edi,30h 804de93a 8bcf mov ecx,edi ; 经过以上4条指令后, 若ecx为0则为调用KeServiceDescriptorTable中的系统服务, 否则ecx为0x10调用KeServiceDescriptorTableShadow中的系统服务 804de93c 03bee0000000 add edi,dword ptr [esi+0E0h] ; edi = KTHREAD.ServiceTable + 0x00或0x10 804de942 8bd8 mov ebx,eax 804de944 25ff0f0000 and eax,0FFFh ; 系统调用号 804de949 3b4708 cmp eax,dword ptr [edi+8] ; 系统调用号必须小于系统服务数 804de94c 0f8330fdffff jae nt!KiBBTUnexpectedRange (804de682) ; ... 804de952 83f910 cmp ecx,10h 804de955 751b jne nt!KiFastCallEntry+0xcf (804de972) ; KeServiceDescriptorTable 804de957 648b0d18000000 mov ecx,dword ptr fs:[18h] ; _NT_TIB地址, 这里也是_TEB的地址了? 804de95e 33db xor ebx,ebx 804de960 0b99700f0000 or ebx,dword ptr [ecx+0F70h] ; TEB.GdiBatchCount 804de966 740a je nt!KiFastCallEntry+0xcf (804de972) ; KeServiceDescriptorTable 804de968 52 push edx ; Ring3参数地址 804de969 50 push eax ; 系统调用号 804de96a ff1568355680 call dword ptr [nt!KeGdiFlushUserBatch (80563568)] 804de970 58 pop eax 804de971 5a pop edx 804de972 64ff0538060000 inc dword ptr fs:[638h] ; KeServiceDescriptorTable直接跳转到这来, ++KPCR.KPRCB.KeSystemCalls 804de979 8bf2 mov esi,edx 804de97b 8b5f0c mov ebx,dword ptr [edi+0Ch] ; SSDT参数表的地址 804de97e 33c9 xor ecx,ecx 804de980 8a0c18 mov cl,byte ptr [eax+ebx] ; 该系统调用的参数个数 804de983 8b3f mov edi,dword ptr [edi] ; 系统服务表地址 804de985 8b1c87 mov ebx,dword ptr [edi+eax*4] ; 该系统服务地址 804de988 2be1 sub esp,ecx ; 在内核栈中分配参数空间 804de98a c1e902 shr ecx,2 ; 参数以DWORD大小拷贝 804de98d 8bfc mov edi,esp 804de98f 3b35d48e5680 cmp esi,dword ptr [nt!MmUserProbeAddress (80568ed4)] 804de995 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (804deb43) ; 若参数地址超过用户空间地址(0x7fff0000) 804de99b f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 804de99d ffd3 call ebx ; 调用! 804de99f 8be5 mov esp,ebp ; 恢复堆栈到只有KTRAP_FRAME时 804de9a1 648b0d24010000 mov ecx,dword ptr fs:[124h] 804de9a8 8b553c mov edx,dword ptr [ebp+3Ch] 804de9ab 899134010000 mov dword ptr [ecx+134h],edx ; KPCR.KPRCB.CurrentThread->TrapFrame = KTRAP_FRAME.Edx nt!KiServiceExit: 804de9b1 fa cli 804de9b2 f7457000000200 test dword ptr [ebp+70h],20000h ; eflags是否是Virtual-8086 Mode 804de9b9 7506 jne nt!KiServiceExit+0x10 (804de9c1) ; 非Virtual-8086 Mode则跳 804de9bb f6456c01 test byte ptr [ebp+6Ch],1 804de9bf 7456 je nt!KiServiceExit+0x66 (804dea17) ; KTRAP_FRAME.SegCs, CPL为Ring0则跳 804de9c1 648b1d24010000 mov ebx,dword ptr fs:[124h] ; 非Virtual-8086 Mode, 以及下面的交付APC, 跳到这来 804de9c8 c6432e00 mov byte ptr [ebx+2Eh],0 ; KPCR.KPRCB.CurrentThread->Alerted = 0 804de9cc 807b4a00 cmp byte ptr [ebx+4Ah],0 804de9d0 7445 je nt!KiServiceExit+0x66 (804dea17) ; if(KPCR.KPRCB.CurrentThread->ApcState.UserApcPending == 0)跳 804de9d2 8bdd mov ebx,ebp 804de9d4 894344 mov dword ptr [ebx+44h],eax ; KTRAP_FRAME.Eax, 系统调用号 804de9d7 c743503b000000 mov dword ptr [ebx+50h],3Bh ; KTRAP_FRAME.SegFs = 0x3B 804de9de c7433823000000 mov dword ptr [ebx+38h],23h ; KTRAP_FRAME.SegDs = KGDT_R3_DATA 804de9e5 c7433423000000 mov dword ptr [ebx+34h],23h ; KTRAP_FRAME.SegEs = KGDT_R3_DATA 804de9ec c7433000000000 mov dword ptr [ebx+30h],0 ; KTRAP_FRAME.SegGs = 0 804de9f3 b901000000 mov ecx,1 804de9f8 ff152c904d80 call dword ptr [nt!_imp_KfRaiseIrql (804d902c)] ; 提升IRQL 804de9fe 50 push eax 804de9ff fb sti 804dea00 53 push ebx ; PKTRAP_FRAME 804dea01 6a00 push 0 ; Reserved 804dea03 6a01 push 1 ; UserMode 804dea05 e8f7f3ffff call nt!KiDeliverApc (804dde01) ; 交付APC 804dea0a 59 pop ecx ; 原来的IRQL 804dea0b ff1530904d80 call dword ptr [nt!_imp_KfLowerIrql (804d9030)] ; 降回原来的IRQL 804dea11 8b4344 mov eax,dword ptr [ebx+44h] ; 赋回系统调用号 804dea14 fa cli 804dea15 ebaa jmp nt!KiServiceExit+0x10 (804de9c1) ;;;;;;;;;;;;;;;;;;;;;;;;;分割线;;;;;;;;;;;;;;;;;;;;;;;;; 804dea17 8b54244c mov edx,dword ptr [esp+4Ch] ; KTRAP_FRAME.SegCs CPL为Ring0, 以及无用户APC等待处理, 跳到这来 804dea1b 648b1d50000000 mov ebx,dword ptr fs:[50h] 804dea22 64891500000000 mov dword ptr fs:[0],edx ; KPCR.NtTib.Exception = KTRAP_FRAME.ExceptionList 804dea29 8b4c2448 mov ecx,dword ptr [esp+48h] 804dea2d 648b3524010000 mov esi,dword ptr fs:[124h] 804dea34 888e40010000 mov byte ptr [esi+140h],cl ; KPCR.KPRCB.CurrentThread->PreviousMode = KTRAP_FRAME.PreviousPreviousMode 804dea3a f7c3ff000000 test ebx,0FFh 804dea40 7579 jne nt!KiSystemCallExit2+0x17 (804deabb) ; if(KPCR.DebugActive != 0) ... 804dea42 f744247000000200 test dword ptr [esp+70h],20000h 804dea4a 0f8506090000 jne nt!Kei386EoiHelper+0x12c (804df356) ; 非Virtual-8086 Mode则跳 ;;;;;;;;;;;;;;;;;;;;;;;;;nt!Kei386EoiHelper+0x12c;;;;;;;;;;;;;;;;;;;;;;;;; nt!Kei386EoiHelper+0x12c 804df356 83c43c add esp,3Ch ; 指向KTRAP_FRAME.Edx 804df359 5a pop edx 804df35a 59 pop ecx 804df35b 58 pop eax 804df35c 8d6554 lea esp,[ebp+54h] ; 指向KTRAP_FRAME.Edi 804df35f 5f pop edi 804df360 5e pop esi 804df361 5b pop ebx 804df362 5d pop ebp 804df363 66817c24088000 cmp word ptr [esp+8],80h 804df36a 7706 ja nt!Kei386EoiHelper+0x148 (804df372) ; if(KTRAP_FRAME.SegCs > 0x80), 则跳. 0x80后的GDT不能用于cs 804df36c 83c404 add esp,4 ; 指向KTRAP_FRAME.Eip 804df36f cf iretd ; pop KTRAP_FRAME.Eip, KTRAP_FRAME.SegCs, KTRAP_FRAME.EFlags