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