<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          嵌入式軟件調(diào)試之軟件斷點

          共 6189字,需瀏覽 13分鐘

           ·

          2021-12-24 02:03

          ????關(guān)注、星標公眾號,直達精彩內(nèi)容

          來源:CSDN |?maomao171314

          整理:技術(shù)讓夢想更偉大?|?李肖遙

          軟件斷點

          INT 3 指令,即通常所說的“軟件斷點”,一條X86系列處理器專門用于支持調(diào)試的指令。該指令目的是使CPU中斷(break)到調(diào)試器,供調(diào)試器對執(zhí)行現(xiàn)場進行各種分析。

          INT 3

          Visual C++ 嵌入內(nèi)聯(lián)匯編指令,示例如下:

          VS沒有下斷點,程序會自動斷在INT 3 指令所在的位置。這正是通過注入代碼手工設(shè)置斷點的方法。

          反匯編窗口如下:

          內(nèi)存地址002719CE 處有INT 3 指令。

          打開寄存器窗口,EIP=002719CE

          INT 3屬于陷阱異常,當CPU產(chǎn)生異常時,EIP指向的是導(dǎo)致異常的下一條指令。但是EIP指向的是導(dǎo)致異常的指令——為什么會發(fā)生回跳?

          斷點命中

          當CPU執(zhí)行INT 3指令時,在執(zhí)行異常處理例程之前,CPU會保存當前的執(zhí)行上下文。

          實模式下INT 3 指令的執(zhí)行過程:

          1?REAL-ADDRESS-MODE:

          2?IF?((vector_number???4)?+?3)?is?not?within?IDT?limit??//檢查根據(jù)向量號計算出向量地址是否超出了邊界

          3?THEN?#GP;//發(fā)生保護性錯誤異常

          4?FI;//IF語句的結(jié)束語句

          5?IF?stack?not?large?enough?for?a?6-byte?return?information?//檢查棧是否有空間保存寄存器

          6?THEN?#SS;//堆棧不足以保存要壓入的6字節(jié)內(nèi)容(CS、IP和EFLAGS的低16位),產(chǎn)生堆棧異常

          7?FI;//IF語句的結(jié)束語句

          8?Push?(EFLAGS[15:0]);

          9?IF?←?0;?(*?Clear?interrupt?flag?*)?//清除IF

          10?TF?←?0;?(*?Clear?trap?flag?*)?//清除TF

          11?AC?←?0;?(*?Clear?AC?flag?*)?//清除AC

          12?Push(CS);?//保存當前段寄存器

          13?Push(IP);?//保存程序指針寄存器

          14?(*?No?error?codes?are?pushed?*)

          15?CS?←?IDT(Descriptor?(vector_number???4),?selector));??//將異常處理例程入口地址加載到CS和IP寄存器

          16?EIP?←?IDT(Descriptor?(vector_number???4),?offset));?(*?16?bit?offset?AND?17?0000FFFFH?*)

          在實模式的單任務(wù)操作系統(tǒng),CPU直接執(zhí)行調(diào)試器注冊的斷點異常處理例程。然后執(zhí)行中斷返回指令(IRET),恢復(fù)被調(diào)試程序,從斷點位置繼續(xù)執(zhí)行。

          保護模式下的INT 3指令的執(zhí)行流程原理上與實模式一致。

          Windows保護模式下的多任務(wù)操作系統(tǒng),INT 3 異常的處理函數(shù)是內(nèi)核函數(shù)KiTrap03。斷點指令在用戶模式下的應(yīng)用程序代碼中,CPU會從用戶模式轉(zhuǎn)入內(nèi)核模式。經(jīng)過幾個內(nèi)核函數(shù)分發(fā)和處理。由于這個異常是來自用戶模式,且該異常的擁有進程正在被調(diào)試(進程的Debug Port不為0),所以內(nèi)核例程會把這個異常通過調(diào)試子系統(tǒng)以調(diào)試事件的形式分發(fā)給用戶模式的調(diào)試器,內(nèi)核的調(diào)試子系統(tǒng)會等待調(diào)試器的回復(fù),收到調(diào)試器的回復(fù)后,調(diào)試子系統(tǒng)會返回到異常處理例程,異常處理例程執(zhí)行IRET指令使被調(diào)試程序回復(fù)執(zhí)行。

          在調(diào)試器收到調(diào)式事件后,會在內(nèi)部尋找與其匹配的斷點記錄。如果能找到,則允許用戶進行交互式調(diào)試。如果找不到,則說明該斷點是程序內(nèi)置的斷點,會彈出異常。

          在Windows中,操作系統(tǒng)的斷點異常處理函數(shù)對于x86 CPU的斷點異常會有一個特殊的處理:將EIP的值減1。出于這個原因,我們在調(diào)試器看到的程序指針指向的仍然是INT 3指令的位置,而不是它的下一條指令。這樣處理的目的是:

          1. 調(diào)試器在落實斷點時只替換一個字節(jié),如果程序指針發(fā)生改變指向了下一條指令的位置,指向的可能是原來多字節(jié)指令的第二個字節(jié),不是一條完整的指令,造成程序的錯誤。

          2. 由于斷點的存在,被調(diào)試程序于斷點位置的指令在斷點觸發(fā)時還未被執(zhí)行,按照“程序指針總是指向?qū)⒁獔?zhí)行的那條指令”的原則,應(yīng)該讓其指向原指令,即倒退一個字節(jié),指向原指令起始位置。

          至此,回跳的問題得到了解答。

          恢復(fù)執(zhí)行

          當用戶結(jié)束分析希望恢復(fù)被調(diào)試程序執(zhí)行時,調(diào)試器通過調(diào)試API通知調(diào)試子系統(tǒng),這會使系統(tǒng)內(nèi)核的異常分發(fā)函數(shù)返回到異常處理例程,然后異常處理例程通過IRET/IRETD指令觸發(fā)一個異常返回動作,使CPU恢復(fù)執(zhí)行上下文,從發(fā)生異常的位置繼續(xù)執(zhí)行。

          當斷點命中中斷到調(diào)試器時,調(diào)試器會把所有斷點處的INT 3替換成原本的內(nèi)容,因此當用戶發(fā)出恢復(fù)執(zhí)行的命令后,調(diào)試器在通知系統(tǒng)真正恢復(fù)程序的執(zhí)行前需要將斷點列表所有斷點全部落實一遍,但是對于命中的斷點需要特殊處理——如果落實了命中斷點,那么程序一恢復(fù)執(zhí)行便會再次觸發(fā)斷點;如果沒有落實,程序下次執(zhí)行到該部分便不會中斷。對于這種情況,大多數(shù)調(diào)試器的做法都是先單步執(zhí)行一次,設(shè)置單步執(zhí)行標志,然后恢復(fù)執(zhí)行,將斷點所在位置的指令執(zhí)行完。由于設(shè)置了單步標志,CPU執(zhí)行完斷點位置的這條指令后會再次中斷到調(diào)試器中,這次調(diào)試器不會通知用戶,而是做一些內(nèi)部操作后恢復(fù)程序的執(zhí)行,而且將所有斷點落實,這一過程一般稱為“單步走出斷點”,如果用戶在恢復(fù)程序執(zhí)行前取消了該斷點,就不需要單步執(zhí)行一次。

          INT 3指令的特殊用途

          由于INT 3 指令的特殊性,對應(yīng)的機器碼是0xCC,對應(yīng)的漢字是“燙”。編譯器在編譯調(diào)試版本時會用0xCC填充剛剛分配的緩沖區(qū),就是下圖經(jīng)常見到的情形:

          編譯器還用INT 3 指令來填充函數(shù)或代碼段末尾的空閑區(qū)域,即用它來做內(nèi)存對齊。

          斷點API

          用戶模式,使用DebugBreak() API ,內(nèi)核模式下使用DbgBreakPoint() 或DbgBreakPointWithStatus() API 主動插入斷點。

          DebugBreak() 反匯編如下,只是對INT 3指令的簡單包裝:

          1?lkd>?u?nt!DbgBreakPoint

          2?nt!DbgBreakPoint:

          3?804df8c4?cc?int?3

          4?804df8c5?c3?ret

          DbgBreakPointWithStatus()允許向調(diào)試器傳遞一個整型參數(shù):

          lkd>?u?nt!DbgBreakPointWithStatus

          804df8d1?8b442404?mov?eax,[esp+0x4]

          804df8d5?cc?int?3

          其中[esp+0x4]代表DbgBreakPointWithStatus函數(shù)的第一個參數(shù)。

          0xCD03

          INT 3指令與當n=3時的INT n指令不同,INT n指令對應(yīng)的機器碼是0xCD后跟1字節(jié)的n值,比如INT 23H會被編譯為0xCD23。與此不同的是,INT 3指令具有獨特的單字節(jié)機器碼0xCC。用戶可以通過_EMIT偽指令來直接嵌入機器碼。

          #include

          int?main()

          {
          ????//?手工斷點

          ????_asm?INT?3;

          ????printf("Hello?INT?3!\n");

          ????_asm

          ????{

          ????????mov?eax,?eax

          ????????__asm?_emit?0xcd?__asm?_emit?0x03

          ????????nop

          ????????nop

          ????}

          ????//或者使用Windows?API

          ????DebugBreak();

          ???return?0;

          }

          C++程序在執(zhí)行的過程中會中斷到調(diào)試器,但是繼續(xù)執(zhí)行會報訪問沖突錯誤。使用windbg打開可執(zhí)行文件,在反編譯窗口中發(fā)現(xiàn)了0xCD03指令

          00421a9e?cc??????????????????????int?????3

          00421a9f?68c47b4200??????push???offset?test!`string'?(00427bc4)

          00421aa4?e89ef9ffff??????????call????test!ILT+1090(_printf)?(00421447)

          00421aa9?83c404?????????????add????esp,4

          00421aac?8bc0?????????????????mov????eax,eax

          00421aae?cd03????????????????int??????3

          00421ab0?90???????????????????nop

          00421ab1?90???????????????????nop

          00421ab2?8bf4????????????????mov?????esi,esp

          00421ab4?ff155cb04200????call??????dword?ptr?[test!_imp__DebugBreak?(0042b05c)]

          反匯編程序?qū)?xCD03翻譯成了INT 3指令,繼續(xù)執(zhí)行,windbg會報以下錯誤

          (c84.1e34):?Break?instruction?exception?-?code?80000003?(first?chance)

          eax=0000000d?ebx=01058000?ecx=23648826?edx=79dc4a6c?esi=004213e3?edi=00fef828

          eip=00421aaf?esp=00fef75c?ebp=00fef828?iopl=0?????????nv?up?ei?pl?nz?na?pe?nc

          cs=0023??ss=002b??ds=002b??es=002b??fs=0053??gs=002b?????????????efl=00000206

          test!main+0x2f:

          00421aaf?0390908bf4ff????add?????edx,dword?ptr?[eax-0B7470h]?ds:002b:fff48b9d=????????

          其中80000003是系統(tǒng)定義的斷點異常代碼,此時程序的EIP=0X00421aaf,這指向的是位于0x00421aae的0xCD03指令的第二個字節(jié)。由于EIP指向的是一條指令的中間而不是起始處,后面的指令都錯位了。以下為對比

          #中斷前的反匯編

          00421a9e?cc?????????????????????int?????3

          00421a9f?68c47b4200??????push???offset?test!`string'?(00427bc4)

          00421aa4?e89ef9ffff??????????call????test!ILT+1090(_printf)?(00421447)

          00421aa9?83c404?????????????add????esp,4

          00421aac?8bc0?????????????????mov????eax,eax

          00421aae?cd03????????????????int??????3

          00421ab0?90????????????????????nop

          00421ab1?90???????????????????nop

          00421ab2?8bf4????????????????mov?????esi,esp

          00421ab4?ff155cb04200????call??????dword?ptr?[test!_imp__DebugBreak?(0042b05c)]
          #中斷后的反匯編

          (c84.1e34):?Access?violation?-?code?c0000005?(!!!?second?chance?!!!)

          eax=0000000d?ebx=01058000?ecx=23648826?edx=79dc4a6c?esi=004213e3?edi=00fef828

          eip=00421aaf?esp=00fef75c?ebp=00fef828?iopl=0?????????nv?up?ei?pl?nz?na?pe?nc

          cs=0023??ss=002b??ds=002b??es=002b??fs=0053??gs=002b?????????????efl=00010206

          test!main+0x2f:

          00421aaf?0390908bf4ff????add?????edx,dword?ptr?[eax-0B7470h]?ds:002b:fff48b9d=????????

          可以看到,中斷后余下的指令都已變得面目全非。由于EIP總是指向?qū)⒁獔?zhí)行的指令,因此程序會嘗試訪問eax-0B7470h的內(nèi)存地址,該地址為非法,因此會導(dǎo)致訪問失效錯誤。

          導(dǎo)致該EIP錯位的原因是KiTrap03在分發(fā)這個異常前總是會將EIP減1,對于單字節(jié)的INT 3指令,這樣的減法過后剛好指向INT 3指令或原來指令的起始地址。但是對于雙字節(jié)的0xCD03指令,執(zhí)行后EIP指向的是該指令的第二個字節(jié)處。解決方法為在斷點命中后手動修改EIP,重定向至原本的下一指令處,調(diào)整后程序可以繼續(xù)執(zhí)行。

          0:000>?r?eip?=?eip+1

          #?修改eip后的反匯編

          KERNELBASE!DebugBreak+0x2:

          76aff092?cc??????????????????int?????3

          76aff093?c3??????????????????ret

          76aff094?8bff????????????????mov?????edi,edi

          76aff096?55??????????????????push????ebp

          76aff097?8bec??????????????mov?????ebp,esp

          76aff099?68ffff0080??????push????8000FFFFh

          76aff09e?6a03??????????????push????3

          76aff0a0?ff7504????????????push????dword?ptr?[ebp+4]

          歸納與提示

          軟件斷點具有以下局限性:

          • 屬于代碼類斷點,適用于代碼段,不使用于數(shù)據(jù)段和I/O空間

          • 對在ROM中執(zhí)行的程序(如BIOS)無法動態(tài)加載軟件斷點

          • 在VDT或IDT還未準備就緒或被破壞的情況下,軟件斷點無法正常工作

          原文鏈接:https://blog.csdn.net/maomao171314/article/details/109749352


          版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除。

          ???????????????? ?END ?????????????????

          關(guān)注我的微信公眾號,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。

          歡迎關(guān)注我的視頻號:

          點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。

          瀏覽 78
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人精品导航 | 色老板在线观看 | 久久午夜精品 | 久久一区二区三区在线 | 亚洲欧美精品久久久久 |