調試寄存器 原理與使用:DR0-DR7


調試寄存器 原理與使用:DR0-DR7

下面介紹的知識性信息來自intel IA-32手冊(可以在intel的開發手冊或者官方網站查到),提示和補充來自學習調試實現時的總結。

希望能給你帶去有用的信息。

(DRx對應任意的一個 調試 寄存 。LENn對應任意一個長度。Ln對應任意一個局部置位)

DR0-DR7可以直接被讀寫操作(MOV 指令之類的,DRx可以是源操作數也可以是目的操作數)

   但是,DRx的訪問是需要一定權限的。比如你用MOV操作的話,你需要在實地址模式,系統管理模式(smm)或者在保護模式(CPL設0).如果權限不夠,將會在訪問DRx的時候嘗產生#GP(general-protection)異常

現在來看看DRx可以干些什么? 

1.設置發生斷點的地址(線性地址)
2.設置斷點的長度(1,2,4個字節,但是執行斷點只能是1)
3.設置在 調試異常產生的地址執行的操作
4.設置斷點是否可用
5.在 調試異常產生時, 調試條件是否是可用

(以上直接翻譯自"Intel 64 and IA-32 Architectures Software Developer’s Manual" volume 3。
以下來自個人的總結。當然,也是參考intel官方資料得來的)

我們來看看 調試 寄存 的一些細節信息。
下圖很重要,后面的介紹都是針對這個圖說的。
(當然不是我畫的,是來自intel  ia-32系統結構開發手冊18章2節)。

點擊圖片以查看大圖圖片名稱: DR0_DR7.jpg查看次數: 2105文件大小: 63.6 KB文件 ID : 39226

調試 寄存  DR0-DR3
   這四個 寄存 是用來設置 斷點地址的。斷點的比對在物理地址轉換前(異常產生時,還沒有將線性地址轉換成物理地址)。由於只有0-3四個保存地址的 寄存 ,所以,硬件斷點,在物理上最多只能有4個。
調試 寄存 DR4-DR5 
   這兩個 調試 寄存 有CR4的DE標記控制。如果DE置位,那么對這兩個 寄存 的訪問會導致#UD異常。如果DE置0,那么他們就被化名為DR6-DR7(你一定會問原來的DR6-DR7怎么辦?這個…… 我也不知道。如果你搞明白了,一定記得告訴我)
調試 寄存 DR7(控制 寄存 )
   (先介紹DR7對DR6的理解有好處。)

DR7是 調試控制 寄存 。控制方式嘛!繼續看:
1.  L0-L3(由第0,2,4,6位控制):對應DR0-DR3,設置斷點作用范圍,如果被置位,那么將只對當前任務有效。每次異常后,Lx都被清零。
2.  G0-G3(由第1,3,5,7位控制):對應DR0-DR3,如果置位,那么所有的任務都有效。每次異常后不會被清零。以確保對所有任務有效。但是,不知道為什么,我在測試時:
設置Gn后,不能返回 調試異常給 調試 (如果你知道為什么,記得告訴我)
3.  LE,GE(由第8,9位控制):這個在P6以下系列CPU上不被支持,在升級版的系列里面:如果被置位,那么cpu將會追蹤精確的數據斷點。LE是局部的,GE是全局的。(到底什么算精確的,我也不清楚,但是,我知道如果設置了這兩個,cpu的速度會降低。我在測試中,都沒有置位。)
4.  GD(由第13位控制):如果置位,追蹤下一條指令是否會訪問 調試 寄存 。如果是,產生異常。在下面的DR6里面,你會知道他還和另外一個標志位有點關系。
5.  R/W0-R/W3:(由第16,17,20,21,24,25,28,29位控制):這個東西的處理有兩種情況。
如果CR4的DE被置位,那么,他們按照下面的規則處理問題:
00:執行斷點
01:數據寫入斷點
10:I/0讀寫斷點
11:讀寫斷點,讀取指令不算
如果DE置0,那么問題會這樣處理:
00:執行斷點
01:數據寫入斷點
10:未定義
11:數據讀寫斷點,讀取指令不算
6.  LEN0-LEN3:(由第18.19.22.23.26.27.30位控制):指定內存操作的大小。
00:1字節(執行斷點只能是1字節長)
01:2字節
10:未定義或者是8字節(和cpu的系列有關系)
11:4字節
調試 寄存 DR6( 調試狀態 寄存
   這個 寄存 主要是在 調試異常產生后,報告產生 調試異常的相關信息
1.  B0-B3(DR0-DR3):DRx指定的斷點在滿足DR7指定的條件下,產生異常。那么Bx就置位。但是,有時,即使Ln和Gn置0,也可能產生Bx被置位。這種現象可能這樣出現(提示:在p6系列處理 ,REP MOVS在不斷循環中產生的 調試異常需要執行完了才能准確返回給 調試進程):DR0的L0,G0都置0(DR0就是一個不能產生異常的斷點了),然后在DR0指定的地址是一個REP指令的循環,這樣,DR0就可能在這個循環之后的REP指令產生的 調試異常中將B0置位
2.  BD:BD需要DR7的GD置位,才有效。BD是在下一條指令要訪問到某一個 調試 寄存 的時候,被置位的。
3.  BS:單步執行模式時,被置位。單步執行是最高權限的 調試異常。
4.  BT:在任務切換的時候,被置位。但是必須在被切換去的任務的TSS段里面的T標記被置位的情況下才有效。在控制權被切換過去后,在執行指令前,返回 調試異常。但是,需要注意,如果 調試程序是一個任務,那么T標記的設置肯定就沖突了。然后,導致了死循環(BT的這些信息都是按照官方資料翻譯而來,由於沒有實際的操作,肯定會有理解上的出入。如果要深入的話,建議看官方資料)
  
有些 調試異常會將B0-B3清零。但是其他的DR6的位是不能被產生異常的進程清零的。每次 調試異常返回后, 調試進程都會先將DR6清零,再按照情況設置。以免產生不必要的錯誤。

對齊問題和64位處理
對齊問題:
這個問題是來源於LENn的設置,如果設置4字節,那么必須4字節對齊。例如:我們下4字節的斷點,那么DRx需要是A0000/A0004/A0008這樣的地址上。I/O地址是零擴展的(這個……也許意味着必須完全對齊)。因為,intel在比對地址時:用LENn的值去覆蓋DRx里面保存的地址的低位。你可以想到,不對齊會有什么后果了吧。注意:執行斷點只能是1字節。

再用圖片解釋下(當然,圖片來自intel官方資料):

點擊圖片以查看大圖圖片名稱: Align1.jpg查看次數: 2083文件大小: 68.6 KB文件 ID : 39224
點擊圖片以查看大圖圖片名稱: Align2.jpg查看次數: 2073文件大小: 33.1 KB文件 ID : 39225

在64位處理 下:
調試 寄存 當然也是64位的。在操作過程中,寫入,前面32位被置零。讀取:只返回后32位。MOV DRx操作,前32位被忽略。
DR6-DR7的高32位被保留。置零。如果置位,會產生#GP異常。8字節的讀寫斷點完全被支持。

最后,還是給個圖片(64位處理 的布局):

點擊圖片以查看大圖圖片名稱: 64.jpg查看次數: 2074文件大小: 41.9 KB文件 ID : 39223

最后需要提醒一個小問題:數據寫入斷點設置后。是在原數據被修改后,才產生調試異常。所以,返回異常時,原有數據已經被修改。如果想保留原有數據,需要自己提前保存對應地址的數據。

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

VC利用調試寄存器實現硬件斷點,處理斷點異常

 

  1.  
    /************************************************************************
  2.  
    SetHardWareBP:
  3.  
    設置線程硬件斷點
  4.  
    hThread: 線程句柄
  5.  
    dwAddr: 斷點地址
  6.  
    dwDrIndex: 硬件寄存器(0~3)
  7.  
    nType: 斷點類型(0:執行,1:讀取,2:寫入)
  8.  
    nLen: 讀寫斷點數據長度(1,2,4)
  9.  
    /************************************************************************/
  10.  
     
  11.  
    BOOL SetHardWareBP(HANDLE hThread,DWORD dwAddr,DWORD dwDrIndex=0,UINT nType=0,UINT nLen=1)
  12.  
    {
  13.  
    BOOL bResult=FALSE;
  14.  
     
  15.  
    CONTEXT context = { 0};
  16.  
    context.ContextFlags=CONTEXT_DEBUG_REGISTERS;
  17.  
    if(::GetThreadContext(hThread,&context))
  18.  
    {
  19.  
    DWORD dwDrFlags=context.Dr7;
  20.  
     
  21.  
     
  22.  
    //將斷點地址復制進入對應Dr寄存器(參考CONTEXT結構)
  23.  
    memcpy(((BYTE *)&context)+ 4+dwDrIndex* 4,&dwAddr, 4);
  24.  
     
  25.  
    //決定使用哪個寄存器
  26.  
    dwDrFlags|=(DWORD) 0x1<<( 2*dwDrIndex);
  27.  
     
  28.  
    //見OD讀寫斷點時 這個置位了,執行沒有(置位也正常-_-)
  29.  
    dwDrFlags|= 0x100;
  30.  
     
  31.  
     
  32.  
    //先將對應寄存器對應4個控制位清零(先或,再異或,還有其它好方法嗎) =.= 悲催的小學生
  33.  
    dwDrFlags|=(DWORD) 0xF<<( 16+ 4*dwDrIndex);
  34.  
    dwDrFlags^=(DWORD) 0xF<<( 16+ 4*dwDrIndex);
  35.  
     
  36.  
     
  37.  
    //設置斷點類型,執行:00 讀取:11 寫入:01
  38.  
    //(不知何故,測試時發現不論是11還是01,讀寫數據時均會斷下來)
  39.  
    if (nType== 1)
  40.  
    dwDrFlags|=(DWORD) 0x3<<( 16+ 4*dwDrIndex); //讀取
  41.  
    else if(nType== 2)
  42.  
    dwDrFlags|=(DWORD) 0x1<<( 16+ 4*dwDrIndex); //寫入
  43.  
    //else if(nType==0)
  44.  
    //dwDrFlags=dwDrFlags //執行
  45.  
     
  46.  
     
  47.  
    //設置讀寫斷點時數據長度
  48.  
    if (nType!= 0)
  49.  
    {
  50.  
    if(nLen== 2 && dwAddr% 2== 0)
  51.  
    dwDrFlags|=(DWORD) 0x1<<( 18+ 4*dwDrIndex); //2字節
  52.  
    else if(nLen== 4 && dwAddr% 4== 0)
  53.  
    dwDrFlags|=(DWORD) 0x3<<( 18+ 4*dwDrIndex); //4字節
  54.  
    }
  55.  
     
  56.  
    context.Dr7=dwDrFlags;
  57.  
    if (::SetThreadContext(hThread,&context)) bResult=TRUE;
  58.  
    }
  59.  
    return bResult;
  60.  
    }

  1.  
    //異常處理
  2.  
    //直接從工程中拷出來的
  3.  
    typedef ULONG (WINAPI *pfnRtlDispatchException)(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext);
  4.  
    static pfnRtlDispatchException m_fnRtlDispatchException= NULL;
  5.  
     
  6.  
    BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext);
  7.  
     
  8.  
    ULONG WINAPI CSysHook::_RtlDispatchException( PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext )
  9.  
    {
  10.  
    if(RtlDispatchException(pExcptRec,pContext)) return 1;
  11.  
    return m_fnRtlDispatchException(pExcptRec,pContext);
  12.  
    }
  13.  
     
  14.  
    //Hook程序異常處理,當程序發生異常時,由ring0轉回ring3時調用的第一個函數:KiUserExceptionDispatcher
  15.  
    BOOL CSysHook::HookSystemSEH()
  16.  
    {
  17.  
    BOOL bResult=FALSE;
  18.  
    BYTE *pAddr=(BYTE *)::GetProcAddress(::GetModuleHandleA( "ntdll"), "KiUserExceptionDispatcher");
  19.  
    if (pAddr)
  20.  
    {
  21.  
    while (*pAddr!= 0xE8)pAddr++; //XP~Win7正常,Win8尚無緣得見
  22.  
    m_fnRtlDispatchException=(pfnRtlDispatchException)((*(DWORD *)(pAddr+ 1))+ 5+(DWORD)pAddr); //得到原函數地址
  23.  
    DWORD dwNewAddr=(DWORD)_RtlDispatchException-(DWORD)pAddr -5; //計算新地址
  24.  
    CMemory::WriteMemory((DWORD)pAddr+ 1,(BYTE *)&dwNewAddr, 4); //這個寫內存的自己改造吧
  25.  
    bResult=TRUE;
  26.  
    }
  27.  
    return bResult;
  28.  
    }
  29.  
     
  30.  
    //異常處理函數
  31.  
    BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext)
  32.  
    {
  33.  
    返回TRUE,這個異常我已經處理好了,繼續運行程序
  34.  
    返回FALSE,這個異常不是我的,找別人處理去
  35.  
    }
詳情jpg 轉 rar

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM