1、内核堆栈
2、内核堆栈结构
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
3、API进0环
普通调用:通过TSS.ESP0得到0环堆栈
快速调用:从MSR得到一个0环的堆栈,代码执行后仍然通过TSS.ESP0的到当前线程0环堆栈
4、TSS
Intel设计TSS是为了任务切换(线程切换),但Windows和Linux并没有使用,而是使用堆栈来保存寄存器的值
text:0046A9F0 SwapContext proc near ; CODE XREF: KiUnlockDispatcherDatabase(x)+72↑p
.text:0046A9F0 ; KiSwapContext(x)+29↑p ...
.text:0046A9F0 or cl, cl
.text:0046A9F2 mov byte ptr es:[esi+2Dh], 2 ; 目标KTHREAD( +0x02d State : UChar)改为2
.text:0046A9F7 pushf ; 存储当前的EFLAGS寄存器
.text:0046A9F8
.text:0046A9F8 loc_46A9F8: ; CODE XREF: KiIdleLoop()+5A↓j
.text:0046A9F8 mov ecx, [ebx] ; EBX存储的是KPCR的地址,此处是读取异常链表
.text:0046A9FA cmp dword ptr [ebx+994h], 0 ; +0x874 DpcRoutineActive : Uint4B是否有dpc有就蓝屏
.text:0046AA01 push ecx
.text:0046AA02 jnz loc_46AB3D
.text:0046AA08 cmp ds:_PPerfGlobalGroupMask, 0 ; LOG用的 Windows自己调试用的,别的地方没有用
.text:0046AA0F jnz loc_46AB14
.text:0046AA15
.text:0046AA15 loc_46AA15: ; CODE XREF: SwapContext+12C↓j
.text:0046AA15 ; SwapContext+13D↓j ...
.text:0046AA15 mov ebp, cr0 ; 1.cr0的保护控制位
.text:0046AA15 ; 2.协处理器控制位
.text:0046AA18 mov edx, ebp
.text:0046AA1A mov cl, [esi+2Ch] ; 当前cpu所跑的线程正在被调试
.text:0046AA1A ; 调试器在3环调用API设置的(Atach就有)
.text:0046AA1A ; 这个位置0不支持硬件断点了
.text:0046AA1D mov [ebx+50h], cl
.text:0046AA20 cli ; 清CPU,表示这个CPU不在被时钟中断打扰
.text:0046AA20 ; 时钟 键盘 鼠标等有时间,CPU默认指向IAT,关闭中段则忽略这些硬件时间
.text:0046AA21 mov [edi+28h], esp ; 将当前的ESP存储到线程结构体中
.text:0046AA24 mov eax, [esi+18h] ; 目标线程栈底
.text:0046AA27 mov ecx, [esi+1Ch]
.text:0046AA2A sub eax, 210h ; -210h浮点寄存器
.text:0046AA2F mov [ebx+8], ecx
.text:0046AA32 mov [ebx+4], eax
.text:0046AA35 xor ecx, ecx
.text:0046AA37 mov cl, [esi+31h] ; +0x031 NpxState : UChar
.text:0046AA37 ; 浮点寄存器 运行浮点用这个,没有不用
.text:0046AA3A and edx, 0FFFFFFF1h ; 判断NpxState有没有浮点支持
.text:0046AA3A ; 如果上一个线程跟这一个线程浮点支持是一样的,就不用替换Cr0
.text:0046AA3D or ecx, edx
.text:0046AA3F or ecx, [eax+20Ch]
.text:0046AA45 cmp ebp, ecx
.text:0046AA47 jnz loc_46AB0C
.text:0046AA4D lea ecx, [ecx+0]
.text:0046AA50
.text:0046AA50 loc_46AA50: ; CODE XREF: SwapContext+11F↓j
.text:0046AA50 test dword ptr [eax-1Ch], 20000h ; 检查是否是虚拟8086模式,如果是eax-10h,也就是减掉
.text:0046AA50 ; _ktrap_frame中的四个成员
.text:0046AA50 ; +0x07c V86Es : Uint4B
.text:0046AA50 ; +0x080 V86Ds : Uint4B
.text:0046AA50 ; +0x084 V86Fs : Uint4B
.text:0046AA50 ; +0x088 V86Gs : Uint4B
.text:0046AA57 jnz short loc_46AA5C
.text:0046AA59 sub eax, 10h ; 减去4个成员,在虚拟8086模式下使用
.text:0046AA5C
.text:0046AA5C loc_46AA5C: ; CODE XREF: SwapContext+67↑j
.text:0046AA5C mov ecx, [ebx+40h] ; 通过KPCR取出TSS的值
.text:0046AA5F mov [ecx+4], eax ; 将修正过的栈底存到TSS中
.text:0046AA62 mov esp, [esi+28h] ; 将目标栈的ESP存储到ESP中
.text:0046AA65 mov eax, [esi+20h] ; 当前线程中有很多状态,一个在ETHREAD中,一个在FS中
.text:0046AA65 ; 这样的好处就是可以在3环通过FS获取当前线程的状态
.text:0046AA68 mov [ebx+18h], eax ; 临时存储目标线程的TEB
.text:0046AA6B sti
.text:0046AA6C mov eax, [edi+44h]
.text:0046AA6F cmp eax, [esi+44h]
.text:0046AA72 mov byte ptr [edi+50h], 0
.text:0046AA76 jz short loc_46AAA4 ; 如果是一个进程中的线程切换跳转
.text:0046AA78 mov edi, [esi+44h] ; 如果不是一个进程,取出线程的KPROCESS
.text:0046AA7B test word ptr [edi+20h], 0FFFFh ; 判断LdtDescriptor是否为-1
.text:0046AA7B ; win95之前用Ldt,之后不用
.text:0046AA81 jnz short loc_46AADE
.text:0046AA83 xor eax, eax
.text:0046AA85
.text:0046AA85 loc_46AA85: ; CODE XREF: SwapContext+117↓j
.text:0046AA85 lldt ax
.text:0046AA88 xor eax, eax
.text:0046AA8A mov gs, eax ; gs段寄存器清0,这就能解释为什么3环gs为0
.text:0046AA8C assume gs:GAP
.text:0046AA8C mov eax, [edi+18h] ; 得到目标进程的Cr3
.text:0046AA8F mov ebp, [ebx+40h] ; TSS寄存器
.text:0046AA92 mov ecx, [edi+30h]
.text:0046AA95 mov [ebp+1Ch], eax ; 将当前Tss中的Cr3修改为目标进程的CR3
.text:0046AA98 mov cr3, eax ; 切换Cr3
.text:0046AA9B mov [ebp+66h], cx ; 存储IO权限位图到TSS 当前线程的IO权限位图 windows2000以后没用了
.text:0046AA9F jmp short loc_46AAA4
.text:0046AA9F ; ---------------------------------------------------------------------------
.text:0046AAA1 align 4