class="markdown_views prism-dracula">
_KTrap_Frame 结构
kd> dt _KTrap_Frame
ntdll!_KTRAP_FRAME
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint4B
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
无论是通过中断门进入的0环,还是快速调用,3环所有的寄存器都会保存在这个结构体里。
通过
快速调用68~78是没有的
权限发生切换时堆栈也会改变,这里听不懂的话去学习保护模式
内核层的ESP = TSS+4
Windows操作系统在每个处理器初始化时,会在GDT中为它构造一个TSS段,然后利用ltr指令设置处理器的任务环境段。(参考_KiSystemStartup函数)
另外,Windows每次切换线程时,总是会设置好TSS中Ring0的esp,使指向当前线程的内核栈。(参考SwapContext函数)
保存3环寄存器值到_KTrap_Frame的0x50~0x64
fs :0030 拆分
0000 0000 0011 0 0 00,RPL为0 ,查gdt,第六组
ffc093
df`
f0000001
ffdff000这个地址刚好指向的就是CUP的KPCR结构,fs在3环指向的是PEB结构,0环就是kpcr。
继续上面
Exceptionlist是异常链表。
把老的Exceptionlist保存到_KTRAP_FRAME的+0x4c位置,然后把它置为空白。
将kpcr+0x124放到esi中,也就是CurrentThread。
将[esi+140]保存先前模式到_KTRAP_FRAME的+048位置,_KTHREAD.PreviousMode 。
提升堆栈0x48
取出3环的CS放到ebx,然后and 上1结果放到esi+0x140,也就是新的先前模式。
先前模式:当你调用这段代码的时候,如果是0环的程序先前模式就存0,3环就存1。操作系统想知道是哪一环的程序调用我只需要查看调用者的先前模式即可
提升栈底ebp与esp指向同一个位置_KTRAP_FRAME最开始的位置
取出esi+0x134的位置_KTHREAD.TrapFrame (+0x134 当前线程的信息),放到ebp+3c_KTHREAD.Edx 。
_KTRAP_FRAME放到_KTHREAD.TrapFrame
把3环的ebp放到ebx
把3环的eip放到edi
把3环的参数指针放到_KTRAP_FRAME.DbgArgPointer
0x0dadb0d0(操作系统用到的一个标志)放到_KTRAP_FRAME.DbgArgMark
ebp+0 = _KTRAP_FRAME.DbgEbp
ebp+04 = _KTRAP_FRAME.DbgEip
比较_KTHREAD.DebugActive(当前线程是否处于调试状态如果处于调试状态这个值就不是0ff),如果你处于调试状态他就会跳转到下面的代码。
就是将dr0~dr7存到_KTRAP_FRAME的+0x18到+0x2c。
如果不处于调试状态跳转到以下代码
无论是从KiFastCallEntry还是kisystenService都会最终都会调用一下的代码
两空进来最中执行的代码是一样的,中断进来和快速调用的他们填表(_KTrap_Frame)的方式不一样但是最终调用的代码都是一样的
- kiSystenService:
进入0环后原来3环的寄存器保存在_KTrap_Frame的0x50~0x64
把3环的参数指针放到_KTRAP_FRAME.DbgArgPointer
以上都kiSystenService从0环进入3环的填表过程
KiFastCallEntry的填表过程
KPCR+40就是TSS,TSS+4就是esp0
push 到_KTrap_Frame
+0x078 HardwareSegSs : Uint4B
+0x074 HardwareEsp : Uint4B
+0x070 EFlags : Uint4B
直接贴ida分析代码了
mov ecx, 23h
push 30h
pop fs ; 修改fs寄存器为30
mov ds, ecx
mov es, ecx
mov ecx, ds:0FFDFF040h ; 获取当前TSS
mov esp, [ecx+4] ; TSS中得到ESP
push 23h ; 原ss压栈
push edx ; 原esp压栈
pushf ; EFLAGS压栈
loc_40770A: ; CODE XREF: _KiFastCallEntry2+22j
push 2
add edx, 8 ; 当前保存着sysentet进入前的ESP的值
; +8 = 参数指针
popf
or [esp+0Ch+var_B], 2 ; KtrapFrame->Eflags.if = 1
push 1Bh ; KtrapFrame->CS=0x1b 保存r3的cs
push dword ptr ds:0FFDF0304h ; KtrapFrame->EIP =返回地址
push 0 ; KtrapFrame->Error = 0
push ebp ; KtrapFrame->ebp = ebp
push ebx ; KtrapFrame->ebx = ebx
push esi ; KtrapFrame->esi = esi
push edi ; KtrapFrame->edi = edi
mov ebx, ds:0FFDFF01Ch
push 3Bh ; KtrapFrame->SegFs = 0x3b 保存r3的fs
mov esi, [ebx+124h] ; 得到当前线程结构
push dword ptr [ebx] ; KtrapFrame->0x4c 保存原异常链
mov dword ptr [ebx], 0FFFFFFFFh ; 设置为空的异常链
mov ebp, [esi+18h] ; 得到初始堆栈KtrapFrame->0x48
push 1 ; KtrapFrame->PreviousPreviousMode = 1
sub esp, 48h ; 提升栈顶指针到_Ktrap_Frame
sub ebp, 29Ch ; Ktrap_Frame
mov byte ptr [esi+140h], 1 ; 先前模式
cmp ebp, esp
jnz loc_4076C8
and dword ptr [ebp+2Ch], 0 ; 清零dr7
test byte ptr [esi+2Ch], 0FFh ; 检查是否处于调试状态
mov [esi+134h], ebp ; ;
jnz Dr_FastCallDrSave
loc_40776A: ; CODE XREF: Dr_FastCallDrSave+10j
; Dr_FastCallDrSave+7Cj
mov ebx, [ebp+60h] ; ebp = KtrapFrame->ebp
mov edi, [ebp+68h] ; edi = KtrapFrame->eip
mov [ebp+0Ch], edx ; KtrapFrame->DbgArgPointer = 参数指针
mov dword ptr [ebp+8], 0BADB0D00h
mov [ebp+0], ebx ; KtrapFrame->DbgEbp = ebx
mov [ebp+4], edi ; KtrapFrame->DbgEip = edi
sti