从R3进入R0的代码很多人也有做过分析了. 这几天我也仔细看了下, 算是弄明白了. Windows不但有从R3进入R0的框架代码, 其实R0反调用R3的框架也是有的, 回头仔细分析下. 公司的电脑没有批下来, 有点蛋疼. 有很多好的想法想整一下都苦于没有电脑. 只能作罢.

本来吧, 公司不说配电脑, 那我就整个外星人. 一样很悠哉. 公司又说配电脑, 但是一直又批不下来, 卡在这里. 纠结.

Windows内核情景分析上面使用的是ReadFile这个函数来表述如何进入内核的, 讲得不错, 但是有些地方没有讲清楚. 看来书还是太薄了. 从ReadFile会调用到NtReadFile例程上面, 这是NtReadFile的代码.

1
2
3
4
5
6
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的地址.

1
2
3
ntdll!KiFastSystemCall:
7c92e510 8bd4            mov     edx,esp
7c92e512 0f34            sysenter

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)