這是編譯ARM二進制文件和使用GDB進行基本調試的簡單介紹。在您按照教程進行操作時,您可能需要按照自己的習慣使用ARM程序集。在這種情況下,你要么需要一個備用的ARM設備,或者你只是按照在這短短的步驟建立自己的實驗室環境中虛擬機操作方法。
您可以使用第7部分 - 堆棧和函數中的以下代碼來熟悉GDB的基本調試。
.section .text .global _start _開始: 按{r11,lr} / *開始序幕。將幀指針和LR保存到堆棧* / 添加r11,sp,#0 / *設置堆棧框架的底部* / sub sp,sp,#16 / *序幕結束。在堆棧上分配一些緩沖區* / mov r0,#1 / *設置局部變量(a = 1)。這也可以作為設置最大功能的第一個參數* / mov r1,#2 / *設置局部變量(b = 2)。這也可以作為設置最大功能的第二個參數* / bl最大/ *呼叫/分支功能最大* / sub sp,r11,#0 / *結尾的開始。重新調整堆棧指針* / 流行{r11,pc} / *結語結尾。從堆棧中恢復幀指針,通過直接加載到PC,跳轉到先前保存的LR * / 最大: 按{r11} / *開始序幕。將幀指針保存到堆棧* / 添加r11,sp,#0 / *設置堆棧框架的底部* / sub sp,sp,#12 / *序幕結束。在堆棧上分配一些緩沖區* / cmp r0,r1 / *執行if(a <b)* / movlt r0,r1 / *如果r0小於r1,將r1存入r0 * / 添加sp,r11,#0 / *結尾的開始。重新調整堆棧指針* / 彈出{r11} / *恢復幀指針* / bx lr / *結語結尾。通過LR寄存器跳回主站* /
就個人而言,我更喜歡使用GEF作為GDB擴展。它給了我一個更好的概述和有用的功能。您可以在這里試用:GEF - GDB增強功能。
將上面的代碼保存在一個名為max.s的文件中,並使用以下命令進行編譯:
$ as max.s -o max.o $ ld max.o -o max
調試器是一個強大的工具,可以:
- 崩潰后加載內存轉儲(驗屍調試)
- 附加到正在運行的進程(用於服務器進程)
- 啟動一個程序並進行調試
針對二進制文件,核心文件或進程ID啟動GDB:
- 附加到進程:$ gdb -pid $(pidof <process>)
- 調試一個二進制文件:$ gdb ./file
- 檢查核心(崩潰)文件:$ gdb -c ./core.3243
$ gdb max
如果您安裝了GEF,則會釋放您的gef>提示符。
這是你如何獲得幫助:
- (gdb)h
- (gdb)apropos <search-term>
gef> apropos寄存器 收集 - 指定要在追蹤點收集的一個或多個數據項目 核心文件 - 使用FILE作為核心轉儲來檢查內存和寄存器 info all-registers - 所有寄存器及其內容的列表 info r - 整數寄存器及其內容的列表 信息寄存器 - 整數寄存器及其內容的列表 維護打印烹飪寄存器 - 打印包括烹飪值的內部寄存器配置 維護打印原始寄存器 - 打印內部寄存器配置,包括原始值 維護打印寄存器 - 打印內部寄存器配置 維護打印遠程寄存器 - 打印包括每個寄存器的內部寄存器配置 p - 打印EXP表達式的值 打印 - 表達EXP的打印值 寄存器 - 在一個顯示全部細節 設置may-write-registers - 設置寫入寄存器的權限 設置觀察者 - 設置gdb是否在觀察者模式下控制劣勢 顯示may-write-registers - 顯示寫入寄存器的權限 顯示觀察者 - 顯示gdb是否在觀察者模式下控制劣勢 tui reg float - 僅顯示浮點寄存器 tui reg general - 只顯示通用寄存器 tui reg system - 只顯示系統寄存器
斷點命令:
- break(或者只是b)<function-name>
- 打破<line-number>
- 打破文件名:功能
- 中斷文件名:行號
- 打破* <地址>
- break + <offset>
- 打破 - <偏移>
- tbreak(設置臨時斷點)
- del <number> (刪除斷點編號x)
- 刪除(刪除所有斷點)
- 刪除<range>(刪除斷點范圍)
- 禁用/啟用<斷點編號或范圍>(不刪除斷點,只是啟用/禁用它們)
- 繼續(或只是c) - (繼續執行,直到下一個斷點)
- 繼續<number>(繼續,但是忽略當前的斷點編號時間,對循環內的斷點很有用。
- 完成(繼續結束功能)
gef> break _start gef> info break Num Type Disp Enb Address什么 1個斷點保持y 0x00008054 <_start> 斷點已經達到了一次 gef> del 1 gef> break * 0x0000805c 斷點2在0x805c gef> break _start
這將刪除第一個斷點並在指定的內存地址設置一個斷點。當你運行這個程序時,它會在這個確切的位置中斷。如果不刪除第一個斷點,只是設置一個新斷點並運行,它將在第一個斷點處斷開。
開始和停止:
- 從程序開始處開始執行程序
- 跑
- [R
- 運行<command-line-argument>
- 停止程序執行
- 殺
- 退出GDB調試器
- 放棄
- q
gef>運行
現在我們的程序正好打破了我們想要的地方,現在是檢查內存的時候了。命令“x”以各種格式顯示存儲器內容。
語法:x / < count > < format > < unit > | |
---|---|
格式 | 單元 |
x - 十六進制 | b - 字節 |
d - 十進制 | h - 半字(2字節) |
我 - 指示 | w - 字(4字節) |
t - 二進制(二) | g - 巨詞(8字節) |
o - 八進制 | |
你 - 無符號 | |
s - 字符串 | |
c - 字符 |
gef> x / 10i $ pc => 0x8054 <_start>:push {r11,lr} 0x8058 <_start + 4>:添加r11,sp,#0 0x805c <_start + 8>:sub sp,sp,#16 0x8060 <_start + 12>:mov r0,#1 0x8064 <_start + 16>:mov r1,#2 0x8068 <_start + 20>:bl 0x8074 <max> 0x806c <_start + 24>:sub sp,r11,#0 0x8070 <_start + 28>:pop {r11,pc} 0x8074 <max>:push {r11} 0x8078 <max + 4>:加上r11,sp,#0 gef> x / 16xw $ pc 0x8068 <_start + 20>:0xeb000001 0xe24bd000 0xe8bd8800 0xe92d0800 0x8078 <max + 4>:0xe28db000 0xe24dd00c 0xe1500001 0xb1a00001 0x8088 <max + 20>:0xe28bd000 0xe8bd0800 0xe12fff1e 0x00001741 0x8098:0x61656100 0x01006962 0x0000000d 0x01080206
單步執行代碼的命令:
- 進入下一行代碼。將步入一個功能
- 步驟1
- 小號
- 步驟<步驟數量>
- 執行下一行代碼。不會輸入功能
- nexti
- ñ
- 下一個<number>
- 繼續處理,直到達到指定的行號,函數名稱,地址,文件名:函數或文件名:行號
- 直到
- 直到<line-number>
- 顯示當前行號和您所在的功能
- 哪里
gef> nexti 5 ... 0x8068 <_start + 20> bl 0x8074 <max> < - $ pc 0x806c <_start + 24> sub sp,r11,#0 0x8070 <_start + 28> pop {r11,pc} 0x8074 <max> push {r11} 0x8078 <max + 4>添加r11,sp,#0 0x807c <max + 8> sub sp,sp,#12 0x8080 <max + 12> cmp r0,r1 0x8084 <max + 16> movlt r0,r1 0x8088 <max + 20>添加sp,r11,#0
用信息寄存器或ir檢查寄存器
gef> info寄存器 r0 0x1 1 r1 0x2 2 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0xbefff7e8 3204446184 r12 0x0 0 sp 0xbefff7d8 0xbefff7d8 lr 0x0 0 pc 0x8068 0x8068 <_start + 20> cpsr 0x10 16
命令“信息寄存器”給你當前的寄存器狀態。我們可以看到通用寄存器r0-r12和專用寄存器SP,LR和PC,包括狀態寄存器CPSR。函數的前四個參數通常存儲在r0-r3中。在這種情況下,我們手動將值移到r0和r1。
顯示進程內存映射:
gef> info proc map 過程10225 映射地址空間: 開始地址結束地址大小偏移量objfile 0x8000 0x9000 0x1000 0 / home / pi / lab / max 0xb6fff000 0xb7000000 0x1000 0 [sigpage] 0xbefdf000 0xbf000000 0x21000 0 [stack] 0xffff0000 0xffff1000 0x1000 0 [矢量]
用“反匯編”命令查看max函數的反匯編輸出。
gef>反匯編max 匯編代碼功能最大的轉儲: 0x00008074 <+0>:push {r11} 0x00008078 <+4>:添加r11,sp,#0 0x0000807c <+8>:sub sp,sp,#12 0x00008080 <+12>:cmp r0,r1 0x00008084 <+16>:movlt r0,r1 0x00008088 <+20>:添加sp,r11,#0 0x0000808c <+24>:pop {r11} 0x00008090 <+28>:bx lr 匯編器轉儲結束。
GEF特定命令(更多命令可以使用命令“gef”查看):
- 將所有加載的ELF圖像的所有部分轉儲到進程內存中
- X檔案
- proc map的增強版本在映射頁面中包含RWX屬性
- 的VMMap
- 內存屬性在給定的地址
- 信佛
- 檢查內置於運行二進制文件中的編譯器級保護
- checksec
gef> xfiles 開始結束名稱文件 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max 0x00008054 0x00008094 .text / home / pi / lab / max gef> vmmap 開始結束偏移Perm路徑 0x00008000 0x00009000 0x00000000 rx / home / pi / lab / max 0xb6fff000 0xb7000000 0x00000000 rx [sigpage] 0xbefdf000 0xbf000000 0x00000000 rwx [stack] 0xffff0000 0xffff1000 0x00000000 rx [矢量] gef> xinfo 0xbefff7e8 ---------------------------------------- [xinfo:0xbefff7e8] ----- ----------------------------------- 找到0xbefff7e8 頁面:0xbefdf000 - > 0xbf000000(大小= 0x21000) 權限:rwx 路徑名稱:[stack] 偏移(從頁面):+ 0x207e8 Inode:0 gef> checksec [+] checksec for'/ home / pi / lab / max' 金絲雀:沒有 NX支持:是的 PIE支持:沒有 RPATH:沒有 RUNPATH:沒有 部分RelRO:沒有 完整的RelRO:沒有
為了使GDB的調試更高效,知道某些分支/跳轉將帶給我們的位置是非常有用的。某些(較新的)GDB版本解析分支指令的地址並向我們顯示目標函數的名稱。例如,GDB的以下輸出缺少這個功能:
... 0x000104f8 <+72>:bl 0x10334 0x000104fc <+76>:mov r0,#8 0x00010500 <+80>:bl 0x1034c 0x00010504 <+84>:mov r3,r0 ...
這是GDB(native,沒有gef)的輸出,它具有我正在談論的功能:
0x000104f8 <+72>:bl 0x10334 <free @ plt> 0x000104fc <+76>:mov r0,#8 0x00010500 <+80>:bl 0x1034c <malloc @ plt> 0x00010504 <+84>:mov r3,r0
如果你在GDB中沒有這個特性,你可以更新Linux源代碼(並且希望他們的代碼庫中已經有了一個更新的GDB)或者自己編譯一個更新的GDB。如果您選擇自行編譯GDB,則可以使用以下命令:
cd / tmp wget https://ftp.gnu.org/gnu/gdb/gdb-7.12.tar.gz tar vxzf gdb-7.12.tar.gz sudo apt-get更新 sudo apt-get install libreadline-dev python-dev texinfo -y cd gdb-7.12 ./configure --prefix = / usr --with-system-readline --with-python && make -j4 sudo make -j4 -C gdb / install gdb --version
我用上面提供的命令在Raspbian(jessie)上下載,編譯和運行GDB,沒有任何問題。這些命令也將取代以前的GDB版本。如果你不想要,那么跳過以install為結尾的命令。而且,我在QEMU中模擬Raspbian的時候做了這個,所以花了我很長時間(小時),因為仿真環境中的資源(CPU)有限。我使用GDB版本7.12,但是即使使用更新的版本,您也很有可能成功(請點擊這里查看其他版本)。