KiFastCallEntry KiServiceExit
文章目录
从R3进入R0的代码很多人也有做过分析了. 这几天我也仔细看了下, 算是弄明白了. Windows不但有从R3进入R0的框架代码, 其实R0反调用R3的框架也是有的, 回头仔细分析下. 公司的电脑没有批下来, 有点蛋疼. 有很多好的想法想整一下都苦于没有电脑. 只能作罢.
本来吧, 公司不说配电脑, 那我就整个外星人. 一样很悠哉. 公司又说配电脑, 但是一直又批不下来, 卡在这里. 纠结.
Windows内核情景分析上面使用的是ReadFile这个函数来表述如何进入内核的, 讲得不错, 但是有些地方没有讲清楚. 看来书还是太薄了. 从ReadFile会调用到NtReadFile例程上面, 这是NtReadFile的代码.
|
|
SystemCallStub里面存放了KiFastSystemCall的地址.
|
|
sysenter进入的地址可以观察MSR176, 然后进入nt!KiFastCallEntry:
KiFastCallEntry的主要功能有: 初始化系统堆栈 将上下文压入系统堆栈,压栈顺序见KTrap_Frame内核堆栈框架 将参数从用户模式堆栈复制到内核堆栈,以上步骤在关中断下进行的, 因为要用到 KUser_Shared_Data 确定服务例程的入口地址,包括使用那个服务表,是Shadow SSDT还是SSDT 转入服务例程入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
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) |
文章作者 忆杰
上次更新 2012-12-10