一 硬件斷點介紹
硬件斷點,顧名思義是由硬件提供給我們的調試寄存器組,我們可以對這些硬件寄存器設置相應的值,然后讓硬件幫我們斷在需要下斷點的地址。
硬件斷點是CPU提供的功能,所以要怎么做就得聽CPU的硬件寄存器的了。先來看看關於硬件寄存器的說明。Intel 80386以上的CPU 提供了調試寄存器以用於軟件調試。386和486擁有6個(另外兩個保留)調試寄存器:Dr0,Dr1,Dr2,Dr3,Dr6和Dr7。這些寄存器均是 32位的,如下圖所示(該圖來源於看雪文章《調試寄存器(DRx)理論與實踐》(http://www.pediy.com/bbshtml/BBS6 /pediy6751.htm),在此對文章作者Hume/冷雨飄心表示感謝):
|---------------|----------------|
Dr0| 用於一般斷點的線性地址
|---------------|----------------|
Dr1| 用於一般斷點的線性地址
|---------------|----------------|
Dr2| 用於一般斷點的線性地址
|---------------|----------------|
Dr3| 用於一般斷點的線性地址
|---------------|----------------|
Dr4| 保留
|---------------|----------------|
Dr5| 保留
|---------------|----------------|
Dr6| |BBB BBB B |
| |TSD 3 2 1 0 |
|---------------|----------------|
Dr7|RWE LEN ... RWE LEN | G GLGLGLGLGL |
| 3 3 ... 0 0 | D E E 3 3 2 21 100 |
|---------------|----------------|
31 15 0
Dr0~3用於設置硬件斷點,由於只有4個斷點寄存器,所以最多只能設置4個硬件調試斷點,產生的異常是 STATUS_SINGLE_STEP(單步異常) 。Dr7是一些控制位,用於控制斷點的方式,Dr6用於顯示是哪個硬件調試寄存器引起的斷點,如果是 Dr0~3或單步(EFLAGS的TF)的話,則相對應的位會置一。
即如果是Dr0引起的斷點,則Dr6的第0位被置1,如果是Dr1引起的斷點,則Dr6的第1位被置1,依此類推。因為硬件斷點同時只會觸發一個,所以 Dr6的低4位最多只有一個位被置1,所以在進入單步后,我們可以通過檢測Dr6的低4位是否有值為1的位,來判斷當前進入單步的原因是否是因為硬件斷點 被斷下。如果是因為硬件斷點被斷下,也可以通過判斷Dr6的低4位中哪一位是1,來進一步判斷是被Dr0到dr3中的哪一個斷點所斷下。
調試控制寄存器Dr7比較重要,其32位結構如下:

1. 位0 L0和位1 G0:用於控制Dr0是全局斷點還是局部斷點,如果G0置位則是全局斷點,L0置位則是局部斷點。G1L1~G3L3用於控制D1~Dr3,其功能同上。
2. LE和GE:P6 family和之后的IA32處理器都不支持這兩位。當設置時,使得處理器會檢測觸發數據斷點的精確的指令。當其中一個被設置的 時候,處理器會放慢執行速度,這樣當命令執行的時候可以通知這些數據斷點。建議在設置數據斷點時需要設置其中一個。切換任務時LE會被清除而GE不會被清 除。為了兼容性,Intel建議使用精確斷點時把LE和GE都設置為1。
3. LEN0到LEN3:指定調試地址寄存器DR0到DR3對應斷點所下斷的長度。如果R/Wx位為0(表示執行斷點),則LENx位也必須為0(表示1字節),否則會產生不確定的行為。LEN0到LEN3其可能的取值如下:
(1)00 1字節
(2)01 2字節
(3)10 保留
(4)11 4字節
4. R/W0到R/W3:指定各個斷點的觸發條件。它們對應於DR0到DR3中的地址以及DR6中的4個斷點條件標志。可能的取值如下:
(1) 00 只執行
(2) 01 寫入數據斷點
(3) 10 I/O端口斷點(只用於pentium+,需設置CR4的DE位,DE是CR4的第3位 )
(4) 11 讀或寫數據斷點
5. GD位:用於保護DRx,如果GD位為1,則對Drx的任何訪問都會導致進入1號調試陷阱(int 1)。即IDT的對應入口,這樣可以保證調試器在必要的時候完全控制Drx。
二 設置硬件斷點
通過上面的介紹,我們知道設置一個硬件斷點一般只需要以下幾個步驟。
(1) 在Dr0到Dr3中找一個可用的寄存器,將其值填寫為要斷下的地址。
(2) 設置Dr7對應的GX或LX位為1。(例如斷點設置在Dr0上則設置Dr7的G0或L0位為1)。
(3) 設置Dr7對應的斷點類型位(R/W0到R/W3其中之一)為執行、寫入或訪問類型。
(4) 設置Dr7對應的斷點長度位(LEN0到LEN3其中之一)為1、2或4字節。
三 處理硬件斷點
在硬件斷點的介紹中已經說過,硬件斷點被斷下后將觸發單步異常,因此在進入單步異常后,我們檢測Dr6的低4位是否有值為1的位,就可以判斷是否是因為硬件斷點被斷下,以及進一步判斷是被Dr0到Dr3中的哪一個斷點所斷下。
硬件斷點有三種類型,硬件執行斷點、硬件訪問斷點和硬件寫入斷點。硬件斷點被斷下后,所斷下的位置(也就是程序的EIP值)會因為斷點的類型不同而有差 異。對於硬件執行斷點,會斷在所下斷點地址指令處,也就是EIP的值和斷點設置的值一樣,下斷點的指令還沒有被執行。而對於硬件訪問、寫入斷點,會斷在所 下斷點地址指令的下一條指令處,也就是EIP的值已經是斷點指令后的下一條指令的地址了,下斷點地址處的指令已經被執行了。
究其原因是因為硬件執行斷點只需要查看EIP的值就可以判斷是否命中硬件執行斷點了,所以在指令執行前就可以斷下,而硬件訪問、寫入斷點是需要 在CPU拿到完整指令代碼並譯碼完畢之后才能判斷是否命中了硬件訪問、寫入斷點的。此時EIP已經指向了下一條指令,又因為intel的cpu指令是變長 指令集,所以不易倒推實際觸發硬件訪問、寫入斷點的指令地址,所以intel對硬件訪問、寫入斷點的處理是停在觸發異常指令后的下一條指令處(這一段是我 本人的理解,如有不對之處,請讀者多多指教)。
由於不同類型的硬件斷點觸發異常的情況不同,所以要區別對待。對於硬件執行斷點,觸發異常斷點被斷下后,要先暫時取消硬件執行斷點,然后設置單步,到下一 次的單步中進行硬件斷點的恢復工作。對於硬件訪問、寫入斷點則不需要做多余的處理,斷下后顯示一下斷點信息,並等待用戶操作就可以了。
因為硬件斷點的設計比較死板,照着intel手冊的說明一步步來就可以了。對Dr7的操作也就是一些位操作。我的代碼里面是一個大大的SWITCH里面套了4個小SWITCH來做的,顯得拖堂冗長、很不好看,所以就不放上來了。
硬件斷點設計需要注意的幾點如下:
1. 設置硬件斷點的時候也要檢查是否重復設置了。
2. 硬件執行斷點被斷下后,此時需要暫時取消掉該硬件執行斷點(否則程序一直被斷在這里,跑不下去)。並設置單步,在下一次單步中重設該硬件執行斷點。
3. 如果硬件執行斷點被斷下之后,此時用戶執行了取消該斷點的操作,則不需要在下一次的單步中恢復該斷點的設置了(這一點同樣適用於INT3斷點和內存斷點)。
本系列文章參考書目、資料如下:
1.《加密與解密3》 編著:段鋼
2.《調試寄存器(DRx)理論與實踐》 作者:Hume/冷雨飄心
3.《數據結構》 作者:嚴蔚敏
