KiFastCallEntry KiServiceExit
从R3进入R0的代码很多人也有做过分析了. 这几天我也仔细看了下, 算是弄明白了. Windows不但有从R3进入R0的框架代码, 其实R0反调用R3的框架也是有的, 回头仔细分析下. 公司的电脑没有批下来, 有点蛋疼. 有很多好的想法想整一下都苦于没有电脑. 只能作罢.
本来吧, 公司不说配电脑, 那我就整个外星人. 一样很悠哉. 公司又说配电脑, 但是一直又批不下来, 卡在这里. 纠结.
Windows内核情景分析上面使用的是ReadFile这个函数来表述如何进入内核的, 讲得不错, 但是有些地方没有讲清楚. 看来书还是太薄了. 从ReadFile会调用到NtReadFile例程上面, 这是NtReadFile的代码.
kd> u ntdll!NtReadFile ntdll!NtReadFile: 7c92d9ce b8b7000000 mov eax,0B7h 7c92d9d3 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300) 7c92d9d8 ff12 call dword ptr [edx] 7c92d9da c22400 ret 24h
SystemCallStub里面存放了KiFastSystemCall的地址.
ntdll!KiFastSystemCall: 7c92e510 8bd4 mov edx,esp 7c92e512 0f34 sysenter
sysenter进入的地址可以观察MSR176, 然后进入nt!KiFastCallEntry:
KiFastCallEntry的主要功能有:
初始化系统堆栈
将上下文压入系统堆栈,压栈顺序见KTrap_Frame内核堆栈框架
将参数从用户模式堆栈复制到内核堆栈,以上步骤在关中断下进行的, 因为要用到 KUser_Shared_Data
确定服务例程的入口地址,包括使用那个服务表,是Shadow SSDT还是SSDT
转入服务例程入口
8053e600 b923000000 mov ecx,23h ; KGDT_R3_DATA OR RPL_MASK 8053e605 6a30 push 30h 8053e607 0fa1 pop fs ; 只要进入内核..fs->KPCR(Kernel's Processor Control Region,内核进程控制区域) 8053e609 8ed9 mov ds,cx ; 使用23h选择子 8053e60b 8ec1 mov es,cx 8053e60d 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h] ; _KPCR->_KTSS 8053e613 8b6104 mov esp,dword ptr [ecx+4] ; 取出_KTSS->esp0 ; 这里是模拟自陷框架,以形成和中断, 异常统一的框架 _KTRAP_FRAME ; _KTRAP_FRAME.HardwareSegSs 8053e616 6a23 push 23h ; KGDT_R3_DATA OR RPL_MASK ; _KTRAP_FRAME.HardwareEsp 8053e618 52 push edx ; R3 ss:esp ; _KTRAP_FRAME.EFags 8053e619 9c pushfd ; R3 Eflags 8053e61a 6a02 push 2 ; 8053e61c 83c208 add edx,8 ; edx -> args 用户态参数 8053e61f 9d popfd ; Eflags = 2 中断已关闭 8053e620 804c240102 or byte ptr [esp+1],2 ; 开启R3 Eflags的中断标记 ; _KTRAP_FRAME.SegCs 8053e625 6a1b push 1Bh ; R3 cs:eip ; _KTRAP_FRAME.Eip 0FFDF0304h->_KUser_Shared_Data 8053e627 ff350403dfff push dword ptr ds:[0FFDF0304h] ; ntdll!KiFastSystemCallRet ; _KTRAP_FRAME.ErrorCode 8053e62d 6a00 push 0 ; 为了和中断保持一致, 中断会有错误码, 同时用于返回值 ; _KTRAP_FRAME.Ebp 8053e62f 55 push ebp ; _KTRAP_FRAME.Ebx 8053e630 53 push ebx ; _KTRAP_FRAME.esi 8053e631 56 push esi ; _KTRAP_FRAME.edi 8053e632 57 push edi 8053e633 8b1d1cf0dfff mov ebx,dword ptr ds:[0FFDFF01Ch] ; ebx<-_KPCR.SelfPcr 这是pcr的指针 ; _KTRAP_FRAME.SegFs 8053e639 6a3b push 3Bh 8053e63b 8bb324010000 mov esi,dword ptr [ebx+124h] ; esi=_KPCR.PrcbData.CurrentThread _KTHREAD ;_KTRAP_FRAME.ExceptionList 8053e641 ff33 push dword ptr [ebx] ; 异常链表 8053e643 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh ; 初始化链表 ; _KTHREAD.InitialStack 8053e649 8b6e18 mov ebp,dword ptr [esi+18h] ; 获取线程堆栈 8053e64c 6a01 push 1 ; MODE_MASK = User Mode ; esp->_KTRAP_FRAME 8053e64e 83ec48 sub esp,48h ; 分配剩余 _KTRAP_FRAME 框架 ; ebp ->_KTRAP_FRAME 8053e651 81ed9c020000 sub ebp,29Ch ; (_FX_SAVE_AREA)NPX_FRAME_LENGTH=210h, (_KTRAP_FRAME)KTRAP_FRAME_LENGTH=8C ; _KTHREAD.PreviousMode 8053e657 c6864001000001 mov byte ptr [esi+140h],1 ; MODE_MASK = 1 设置线程模式 ; 现在_KTRAP_FRAME已经建立完成 ; 计算初始堆栈线程的初始堆栈指针,包含NPX和_KTRAP_FRAME ; 如果 ebp 和 esp 不相等, 那么这是一个V86模式的线程. 拒绝调用. 8053e65e 3bec cmp ebp,esp 8053e660 759a jne nt!KiFastCallEntry2+0x47 (8053e5fc) ; 处理V86模式的代码不看了. ; _KTRAP_FRAME.Dr7 8053e662 83652c00 and dword ptr [ebp+2Ch],0 ; 清空 Dr7 调试寄存器 ; _KTHREAD.DebugActive 8053e666 f6462cff test byte ptr [esi+2Ch],0FFh ; 线程是否被调试状态 ; _KTHREAD.TrapFrame 8053e66a 89ae34010000 mov dword ptr [esi+134h],ebp ; ebp = _KTRAP_FRAME 保存新的 TrapFrame 8053e670 0f854afeffff jne nt!Dr_FastCallDrSave (8053e4c0) ; 如果线程被调试, 那么还要做些处理, 这里先不管. 8053e676 8b5d60 mov ebx,dword ptr [ebp+60h] ; ebx = _KTRAP_FRAME->Ebp 8053e679 8b7d68 mov edi,dword ptr [ebp+68h] ; edi = _KTRAP_FRAME->Eip ; _KTRAP_FRAME.DbgArgPointer 8053e67c 89550c mov dword ptr [ebp+0Ch],edx ; edx = 参数指针 ; _KTRAP_FRAME.DbgArgMark 8053e67f c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h ; 0BADB0D00h 是什么? ; _KTRAP_FRAME.DbgEbp 8053e686 895d00 mov dword ptr [ebp],ebx ; _KTRAP_FRAME.DbgEbp = _KTRAP_FRAME->Ebp 8053e689 897d04 mov dword ptr [ebp+4],edi ; _KTRAP_FRAME.DbgEip = _KTRAP_FRAME->Eip 8053e68c fb sti ; 开中断 8053e68d 8bf8 mov edi,eax ; eax = 标号 8053e68f c1ef08 shr edi,8 ; Shadow ssdt的index 都在1000h以上 8053e692 83e730 and edi,30h 8053e695 8bcf mov ecx,edi ; 如果是shadow ecx = 10h,否则ecx =0h (bit11 bit12) ; _KTHREAD.ServiceTable 8053e697 03bee0000000 add edi,dword ptr [esi+0E0h] ; 确定是哪个表 8053e69d 8bd8 mov ebx,eax 8053e69f 25ff0f0000 and eax,0FFFh ; _SYSTEM_SERVICE_TABLE.NumberOfService 8053e6a4 3b4708 cmp eax,dword ptr [edi+8] ; 和 ssdt总项数比较,超出则跳 8053e6a7 0f8345fdffff jae nt!KiBBTUnexpectedRange (8053e3f2) ; 如果jae, 试图将其转换到GUI线程, 这个不分析了 8053e6ad 83f910 cmp ecx,10h ; 测试是否调用 Shadow Ssdt 8053e6b0 751a jne nt!KiFastCallEntry+0xcc (8053e6cc) ; 不跳则是shadow 8053e6b2 8b0d18f0dfff mov ecx,dword ptr ds:[0FFDFF018h] ; ecx = _KPCR->_NT_TIB->Self 指向 _TEB 8053e6b8 33db xor ebx,ebx 8053e6ba 0b99700f0000 or ebx,dword ptr [ecx+0F70h] ; _TEB.GdiBatchCount 8053e6c0 740a je nt!KiFastCallEntry+0xcc (8053e6cc) 8053e6c2 52 push edx ; edx = argc 8053e6c3 50 push eax ; eax = Index 8053e6c4 ff15e4405580 call dword ptr [nt!KeGdiFlushUserBatch (805540e4)] 8053e6ca 58 pop eax ; eax = Index 8053e6cb 5a pop edx ; edx = argc 8053e6cc ff0538f6dfff inc dword ptr ds:[0FFDFF638h] ; _KPRCB->KeSystemCalls++, 记录系统调用次数 8053e6d2 8bf2 mov esi,edx ; esi指向用户栈参数 8053e6d4 8b5f0c mov ebx,dword ptr [edi+0Ch] ; ebx = ssdt->ParamTableBase 8053e6d7 33c9 xor ecx,ecx 8053e6d9 8a0c18 mov cl,byte ptr [eax+ebx] ; cl = 参数总共占得字节大小 8053e6dc 8b3f mov edi,dword ptr [edi] ; edi=ssdt->ServiceTableBas 8053e6de 8b1c87 mov ebx,dword ptr [edi+eax*4] ; 找到对应函数地址 8053e6e1 2be1 sub esp,ecx ; 内核栈中为参数分配空间 8053e6e3 c1e902 shr ecx,2 ; 除4,参数个数 8053e6e6 8bfc mov edi,esp ; edi = 内核栈的参数位置 ; 检测参数是否在用户空间 8053e6e8 3b35d49a5580 cmp esi,dword ptr [nt!MmUserProbeAddress (80559ad4)] 8053e6ee 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (8053e89c) ; ecx是参数个数,从用户栈复制参数到内核栈,原来SSDT所有参数都是4个字节为单位的. 8053e6f4 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] ; 终于高潮了. 这里调用例程地址 8053e6f6 ffd3 call ebx 8053e6f8 8be5 mov esp,ebp ; 恢复栈顶,此时栈顶是KTRAP_FRAME 8053e6fa 8b0d24f1dfff mov ecx,dword ptr ds:[0FFDFF124h] ; ecx = _KTHREAD 8053e700 8b553c mov edx,dword ptr [ebp+3Ch] ; edx = KTRAP_FRAME->Edx 8053e703 899134010000 mov dword ptr [ecx+134h],edx ; KThread->TrapFrame = KTRAP_FRAME->Edx 恢复ring3 陷阱帧. ;========================================================================== ; 这是返回过程 ;========================================================================== nt!KiServiceExit: 8053e709 fa cli ; 关中断 8053e70a f7457000000200 test dword ptr [ebp+70h],20000h ; _KTRAP_FRAME->EFlags is this a V86 frame 8053e711 7506 jne nt!KiServiceExit+0x10 (8053e719) ; 跳则不是V86 8053e713 f6456c01 test byte ptr [ebp+6Ch],1 ; KTRAP_FRAME->SegCs 测试CS是否是R3选择子 8053e717 7457 je nt!KiServiceExit+0x67 (8053e770) ;如果CPL非0则跳. ;========================================================================== ; 处理APC ;========================================================================== 8053e719 8b1d24f1dfff mov ebx,dword ptr ds:[0FFDFF124h] ; ebx->_KTHREAD ; _KTHREAD.Alerted 8053e71f c6432e00 mov byte ptr [ebx+2Eh],0 ; 清除线程警觉位. APC有关. ; _KTHREAD._KAPC_STATE.UserApcPending 8053e723 807b4a00 cmp byte ptr [ebx+4Ah],0 ; 这里判断是否有APC挂起 8053e727 7447 je nt!KiServiceExit+0x67 (8053e770) ; 没有APC挂起 8053e729 8bdd mov ebx,ebp ; _KTRAP_FRAME.Eax 8053e72b 894344 mov dword ptr [ebx+44h],eax ; 保存调用例程的返回值 ; _KTRAP_FRAME.SegFs 8053e72e c743503b000000 mov dword ptr [ebx+50h],3Bh ; _KTRAP_FRAME.SegDs 8053e735 c7433823000000 mov dword ptr [ebx+38h],23h ; _KTRAP_FRAME.SegEs 8053e73c c7433423000000 mov dword ptr [ebx+34h],23h ; _KTRAP_FRAME.SegGs 8053e743 c7433000000000 mov dword ptr [ebx+30h],0 8053e74a b901000000 mov ecx,1 ; APC_LEVEL 将当前线程IRQL调整到APC_LEVEL 8053e74f ff15f4864d80 call dword ptr [nt!_imp_KfRaiseIrql (804d86f4)] 8053e755 50 push eax ; 保存旧的IRQL. 8053e756 fb sti ; 开中断以后, 有可能带来线程切换 8053e757 53 push ebx ; _KTRAP_FRAME 8053e758 6a00 push 0 ; Null exception frame 8053e75a 6a01 push 1 ; Previous mode = User Mode 8053e75c e8b702fcff call nt!KiDeliverApc (804fea18) ; 投递APC消息 8053e761 59 pop ecx 8053e762 ff151c874d80 call dword ptr [nt!_imp_KfLowerIrql (804d871c)]; 恢复中断级 ; _KTRAP_FRAME.Eax 8053e768 8b4344 mov eax,dword ptr [ebx+44h] ; 重新读出Eax 8053e76b fa cli 8053e76c ebab jmp nt!KiServiceExit+0x10 (8053e719) ; 这是一个循环, 循环的处理APC 8053e76e 8bff mov edi,edi ;========================================================================== ; _KTRAP_FRAME._EXCEPTION_REGISTRATION_RECORD 8053e770 8b54244c mov edx,dword ptr [esp+4Ch] ; ExceptionList ; _KPCR.DebugActive 8053e774 648b1d50000000 mov ebx,dword ptr fs:[50h] ; _KPCR._NT_TIB.ExceptionList 8053e77b 64891500000000 mov dword ptr fs:[0],edx ; 还原线程seh ; _KTRAP_FRAME.PreviousPreviousMode 8053e782 8b4c2448 mov ecx,dword ptr [esp+48h] 8053e786 648b3524010000 mov esi,dword ptr fs:[124h] ; esi-->_KTHREAD 8053e78d 888e40010000 mov byte ptr [esi+140h],cl ; _KTHREAD.PreviousMode = _KTRAP_FRAME.PreviousPreviousMode 8053e793 f7c3ff000000 test ebx,0FFh ; 当前线程是否在调试 8053e799 7579 jne nt!KiSystemCallExit2+0x17 (8053e814) ; 是被调试, 则跳走 ; _KTRAP_FRAME.EFlags = EFLAGS_V86_MASK 8053e79b f744247000000200 test dword ptr [esp+70h],20000h ; 判断当前是否是V86模式. 8053e7a3 0f85eb080000 jne nt!Kei386EoiHelper+0x12c (8053f094) ; 是, 则跳走 ; _KTRAP_FRAME.SegCs 8053e7a9 66f744246cf9ff test word ptr [esp+6Ch],0FFF9h ; FRAME_EDITED 8053e7b0 0f84b4000000 je nt!KiSystemCallExit2+0x6d (8053e86a) 8053e7b6 66837c246c1b cmp word ptr [esp+6Ch],1Bh ; set/clear ZF 8053e7bc 660fba64246c00 bt word ptr [esp+6Ch],0 ; test MODE_MASK set/clear CF 8053e7c3 f5 cmc ; (CF=1 and ZF=0) 8053e7c4 0f878e000000 ja nt!KiSystemCallExit2+0x5b (8053e858) ; jmp if CF=0 and ZF=0 ; KGDT_R0_CODE 8053e7ca 66837d6c08 cmp word ptr [ebp+6Ch],8 ; _KTRAP_FRAME.Cs 选择子的合法性 8053e7cf 7405 je nt!KiServiceExit+0xcd (8053e7d6) ; 如果CS是内核模式, 那么我们直接就可以跳到恢复通用寄存器的地方 ; _KTRAP_FRAME.TsSegFs 8053e7d1 8d6550 lea esp,[ebp+50h] ; 恢复FS 8053e7d4 0fa1 pop fs ; _KTRAP_FRAME.TsEdi 8053e7d6 8d6554 lea esp,[ebp+54h] ; 获取edi的值 8053e7d9 5f pop edi ; 8053e7da 5e pop esi 8053e7db 5b pop ebx 8053e7dc 5d pop ebp ; _KTRAP_FRAME.DbgArgMark 8053e7dd 66817c24088000 cmp word ptr [esp+8],80h ; 8053e7e4 0f87c6080000 ja nt!Kei386EoiHelper+0x148 (8053f0b0) 8053e7ea 83c404 add esp,4 8053e7ed f744240401000000 test dword ptr [esp+4],1 ; 是从用户空间发起的调用 nt!KiSystemCallExitBranch: 8053e7f5 7506 jne nt!KiSystemCallExit2 (8053e7fd) ; 测试是否是从内核种发起的调用 8053e7f7 5a pop edx 8053e7f8 59 pop ecx 8053e7f9 9d popfd 8053e7fa ffe2 jmp edx ; 从内核中发起的调用, 在这里返回 nt!KiSystemCallExit: 8053e7fc cf iretd nt!KiSystemCallExit2: 8053e7fd f644240901 test byte ptr [esp+9],1 ; 8053e802 75f8 jne nt!KiSystemCallExit (8053e7fc) ; 不为0是则是通过自陷指令进入内核的 8053e804 5a pop edx ; New R3 EIP 8053e805 83c404 add esp,4 ; Skip R3 DS 8053e808 80642401fd and byte ptr [esp+1],0FDh ; NOT EFLAGS_INTERRUPT_MASK ; 关闭中断标记位 8053e80d 9d popfd ; 还原eflag 8053e80e 59 pop ecx ; ecx = _KTRAP_FRAME.esp r3 的栈顶 8053e80f fb sti ; 开中断 8053e810 0f35 sysexit ; 退出内核模式. ;========================================================================== ; 调试有关的处理 ;========================================================================== nt!Dr_FastCallDrSave: 8053e4c0 f7457000000200 test dword ptr [ebp+70h],20000h 8053e4c7 750d jne nt!Dr_FastCallDrSave+0x16 (8053e4d6) 8053e4c9 f7456c01000000 test dword ptr [ebp+6Ch],1 8053e4d0 0f84a0010000 je nt!KiFastCallEntry+0x76 (8053e676) 8053e4d6 0f21c3 mov ebx,dr0 8053e4d9 0f21c9 mov ecx,dr1 8053e4dc 0f21d7 mov edi,dr2 8053e4df 895d18 mov dword ptr [ebp+18h],ebx 8053e4e2 894d1c mov dword ptr [ebp+1Ch],ecx 8053e4e5 897d20 mov dword ptr [ebp+20h],edi 8053e4e8 0f21db mov ebx,dr3 8053e4eb 0f21f1 mov ecx,dr6 8053e4ee 0f21ff mov edi,dr7 8053e4f1 895d24 mov dword ptr [ebp+24h],ebx 8053e4f4 894d28 mov dword ptr [ebp+28h],ecx 8053e4f7 33db xor ebx,ebx 8053e4f9 897d2c mov dword ptr [ebp+2Ch],edi 8053e4fc 0f23fb mov dr7,ebx 8053e4ff 648b3d20000000 mov edi,dword ptr fs:[20h] 8053e506 8b9ff8020000 mov ebx,dword ptr [edi+2F8h] 8053e50c 8b8ffc020000 mov ecx,dword ptr [edi+2FCh] 8053e512 0f23c3 mov dr0,ebx 8053e515 0f23c9 mov dr1,ecx 8053e518 8b9f00030000 mov ebx,dword ptr [edi+300h] 8053e51e 8b8f04030000 mov ecx,dword ptr [edi+304h] 8053e524 0f23d3 mov dr2,ebx 8053e527 0f23d9 mov dr3,ecx 8053e52a 8b9f08030000 mov ebx,dword ptr [edi+308h] 8053e530 8b8f0c030000 mov ecx,dword ptr [edi+30Ch] 8053e536 0f23f3 mov dr6,ebx 8053e539 0f23f9 mov dr7,ecx 8053e53c e935010000 jmp nt!KiFastCallEntry+0x76 (8053e676)
发表评论
Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/x-agan/comments.php on line 66
您必须登录 才能进行评论。