軟件調試(3) - 調試異常與斷點異常

保護模式下的INT 3

INT 3 對應的異常處理函數是內核函數: nt!KiTrap03

斷點命中
---------------------
由於是R3程序調試
=>會因斷點指令由R3轉入R0
=>由內核函數分發(11章詳解)
=>由於異常來自r3, 而產生異常的進程正被調試(debugport非0),所以內核例程會把這個異常通過調試子系統,以調試事件的形式分發給r3的調試器,例如(vc6),在通知調試器後, 內核等待回復=>收到回復後調試子系統的函數會層層返回,最後返回到異常處理例程


KiTrap03會把程序指針寄存器-1 => 因而調試器看到的仍然是int3的位置 看似未被執行=>但其實已經執行

1. 保持原有指令完整性
2. 由於斷點存在, 原本指令其實未被執行=>因應該指向原有指令位置(改為INT 3 的地址)


恢復執行
-----------------------
當用戶結束分析希望恢復時
=>調試器通過調試api通知調試子系統(內核), 這會導致系統內核的異常分發函數返回到異常處理例程,然後異常處理例程通過IRET/IRETD指令觸發一個異常返回動作
=>使CPU恢復上下文,從發生異常的位置繼續執行
=>注意:EIP指向斷點的位置,最後恢復指令
=>CPU繼續執行

特別用途
------------------------
0xCC塊的作用: 為調試版本=>分配緩沖區=>因為緩沖區或堆棧溢出時,程序指針意外指向了這些區域便因為INT3 而馬上中斷到調試器
還有就是填充函數或代碼段未尾的空閑空間,也就是用來做內存對齊=>截獲內存溢出


斷點API
------------------------
R0=>DbgBreakPoint / DbgBreakPointWithStatus
R3=>DebugBreak

調試寄存器
------------------------
DR0~DR3 : 指定斷點地址, 是虛擬地址不是物理地址, 因為CPU是在虛擬地址被翻譯之前做斷點工作, 意味不能透過它對物理內存作斷點行為
DR7 : 24位被分成四組 分別與四個調試寄存器對應,用於設置讀/寫/執等中斷條件

DR7用法 : 讀/寫內存時中斷 注意: 長度域應該為00, 代碼1字節長度=>應指向指令起始字節
執行內存中的代碼時中斷 : 一. 只要斷點區域中任一字節在被訪問範圍內,都會觸發該斷點
讀寫I/O端口時中斷 二. 邊界對齊要求=> CPU會在檢查斷點時自動去除相應長度的低位
例如地址:0xA004 , 長度:4字節, 會被CPU認為要檢測0XA000~0XA003
因為會自動去除做到對齊, 因此想檢測0xA004~0xA008應設長度為0x8


DR7詳細設置(p.85)


指令斷點
-------------------------
如果指令斷點被設置在近於mov ss,eax的下一行 那麼該斷點永遠不會被觸發!
因為是為了保證ss和esp的一致性, CPU 執行Mov ss指令時,會禁止全部中斷和異常直到執行下一條指令
或者pop ss, pop esp 也是會禁止

當有多個指令一起時:
mov ss,edx <=斷點觸發 ,開始保護 mov ss,eax <=斷點 ,仍然保護直到他執行完 mov esp,ebp <=斷點觸發 CPU只會保證第一條指令禁止全部中斷及異常 使用LSS指令可同時加載SS和ESP兩個寄存器 調試異常 ------------------------- INT 3=> 斷點異常(#BP)
INT 1=> 調試異常(#DB)

硬斷產生的是調試異常,因此CPU會執行INT 1

調試狀態寄存器(DR6):
當CPU檢測到相應斷點條件的斷點或有其他調試事件發生時,用來向調試器的斷點異常處理程序傳送異常的詳細信息,方便調試器知道是什麼調試事件

DR6各個標志位含義(P.89)

設置硬斷透過:SetThreadContext / GetThreadContext => 可以產生異常後透過VEH捕獲 再形成斷點


除了斷點外,中斷到調試器的方法
---------------------------------
陷阱標志:
8086 => EFLAGS[TF]
80386=> TSS[T]
當TF為1 每執行完一次指令也會產生一次調試異常(#DB) => int 1

cpu 在即將完成一條指令時,檢查TSS[T]

TF和BTF標志同時為1時, 可以為分支下斷點(如jne) 會跳到分支後的第一句語句, 因為eip總是指向下一條指令,
而且是在jne執行後才檢查TF和BTF標志位
CPU認為的分支:
JMP(無條件跳轉)
JCC(各種條件跳轉指令,如JA,JAE,JNE等)
LOOP
CALL
由於只有內核代碼可以訪問MSR寄存器,
在WriteMSR()函數中,使用了一個未公開的apiW


參考:Windows軟件調試

Comments

Popular posts from this blog

Android Kernel Development - Kernel compilation and Hello World

How does Nested-Virtualization works?

Understanding ACPI and Device Tree