1. 嵌入式軟件調試之軟件斷點

        共 6189字,需瀏覽 13分鐘

         ·

        2021-12-24 02:03

        ????關注、星標公眾號,直達精彩內容

        來源:CSDN |?maomao171314

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

        軟件斷點

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

        INT 3

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

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

        反匯編窗口如下:

        內存地址002719CE 處有INT 3 指令。

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

        INT 3屬于陷阱異常,當CPU產(chǎn)生異常時,EIP指向的是導致異常的下一條指令。但是EIP指向的是導致異常的指令——為什么會發(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語句的結束語句

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

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

        7?FI;//IF語句的結束語句

        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?*)

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

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

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

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

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

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

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

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

        恢復執(zhí)行

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

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

        INT 3指令的特殊用途

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

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

        斷點API

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

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

        1?lkd>?u?nt!DbgBreakPoint

        2?nt!DbgBreakPoint:

        3?804df8c4?cc?int?3

        4?804df8c5?c3?ret

        DbgBreakPointWithStatus()允許向調試器傳遞一個整型參數(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指令對應的機器碼是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í)行的過程中會中斷到調試器,但是繼續(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)]

        反匯編程序將0xCD03翻譯成了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總是指向將要執(zhí)行的指令,因此程序會嘗試訪問eax-0B7470h的內存地址,該地址為非法,因此會導致訪問失效錯誤。

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


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

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

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

        歡迎關注我的視頻號:

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

        瀏覽 78
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
          
          

            1. 张开腿插入 | 香蕉社区在线观看 | 成人免费乱码大片a毛片蜜芽 | 黑人一级婬片A片AAA毛片小说 | 亚洲女教师高潮毛茸茸 |