2015
Oct
01

網頁好讀版

GDB 全名為 Global Project DeBug,它可以用來檢視系統執行檔所執行的語法,以及記憶體地址所存的資料,可以用來 Debug ,反組譯,Hack 執行檔等等,Linux 與 Windows 系統皆可以使用這套軟體。

GDB 有以下數種功用

  • Debug
  • 反組譯
  • Hack 執行檔

對於一個已經編譯好的執行檔,因為我們沒有他的原始程式碼,無法直接參透程式的內容,而 GDB 能夠將執行檔的程式轉成組語 (assemble) ,只要你看得懂組語,就能夠猜出原始的程式碼大約會是什麼。

如何開始使用 GDB

首先,我們先寫一段簡單的 c 語言,並正確的編譯 g++ -g main.cc,請注意一定要加 -g 這個參數, -g 代表 debug 模式,使用 GDB 時會更加好用。

g++ main.cc
  1. #include "stdio.h"
  2. int main () {
  3. char const *message = "How are you?";
  4. printf("%s", message);
  5.  
  6. return 0;
  7. }

編譯完成後,會產生一個叫 a.out 的檔案,接著我們用 gdb a.out 就可以開始使用 GDB 囉,下面有執行的範例。

範例
  1. # gdb a.out
  2. GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-64.el7
  3. Copyright (C) 2013 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "x86_64-redhat-linux-gnu".
  9. For bug reporting instructions, please see:
  10. <http://www.gnu.org/software/gdb/bugs/>...
  11. Reading symbols from a.out...(no debugging symbols found)...done.
  12. (gdb)

GDB 基本指令

[disass] display assemble 列印出組語程式碼

  • disass main
  • disass /r main
  • disass 0x400530,0x400550 (disass start,end)

disass 後面可以接 function 的名稱,當然你要知道有什麼程式裡面有什麼 function 名稱,一般來說 C 語言裡面都會有個 main function ,所以我們可以輸入 disass main 來檢視 main 底下的組語內容。

disass -r,加上 -r 這個參數,可以把組語的內容用 hex 的方式印出來。

disass -m,加上 -m 這個參數,可以印出程式原始碼內容,如果你想要這個功能,就得在編譯時先加上 debug 的參數。

disass 0x400530,0x400550,如果你看某一個區間的組語內容,可以用 disass start address,end address , GDB 就會自動印出兩段 memory address 之間的所有組語內容。

來看一下 disass 執行的結果:

disass example
  1. (gdb) disass main
  2. Dump of assembler code for function main():
  3. 0x00000000004005f0 <+0>: push %rbp
  4. 0x00000000004005f1 <+1>: mov %rsp,%rbp
  5. 0x00000000004005f4 <+4>: sub $0x10,%rsp
  6. 0x00000000004005f8 <+8>: movq $0x4006b0,-0x8(%rbp)
  7. 0x0000000000400600 <+16>: mov -0x8(%rbp),%rax
  8. 0x0000000000400604 <+20>: mov %rax,%rsi
  9. 0x0000000000400607 <+23>: mov $0x4006bd,%edi
  10. 0x000000000040060c <+28>: mov $0x0,%eax
  11. 0x0000000000400611 <+33>: callq 0x4004d0 <printf@plt>
  12. 0x0000000000400616 <+38>: mov $0x0,%eax
  13. 0x000000000040061b <+43>: leaveq
  14. 0x000000000040061c <+44>: retq
  15. End of assembler dump.

[b] breakpoint 設定程式暫停點

breakpoint 可以任何設定暫停點,通常我們會在文字比較 (strncmp) 的時候,暫停程式,檢查兩個文字的內容,分別是什麼,breakpoint 有兩種縮寫可以使用, [break] & [b]。

b main , 當程式執行到 main 這個 function 時,程式會暫停

b *0x00000000004005f0 , 當程式執行到這個記憶體 address 時,程式會暫停

info b , 列出所有設定過的 breakpoint

delete 1 , 移除第一個 breakpoint ,你必需先用 info b 來看看目前有哪些 breakpoint ,然後再用 delete 1 2 3 來移除 breakpoint 。

來看看 breakpoint 的執行過程:

breakpoint example
  1. (gdb) b main
  2. Breakpoint 5 at 0x4005f8: file main.cc, line 5.
  3. (gdb) b *0x00000000004005f0
  4. Breakpoint 6 at 0x4005f0: file main.cc, line 4.
  5. (gdb) info b
  6. Num Type Disp Enb Address What
  7. 5 breakpoint keep y 0x00000000004005f8 in main() at main.cc:5
  8. 6 breakpoint keep y 0x00000000004005f0 in main() at main.cc:4
  9. (gdb) delete 5 6

[si] stepi 執行一行組語

stepi 的縮寫是 si , stepi 代表只執行一行組語指令,每執行一行就會自動暫停。

Example
  1. (gdb) b main
  2. (gdb) run
  3. .
  4. .
  5. (gdb) si
  6. 0x000000000040060c in main ()
  7. (gdb) si
  8. 0x0000000000400611 in main ()
  9. (gdb) si
  10. 0x00000000004004d0 in printf@plt ()

[ni] nexti 執行一行組語

nexti 的縮寫是 [ni] , next 跟 stepi 很像,也是執行一行組語,但不同的是 ,如果 nexti 遇到要 call 另外一個 function ,那麼它會直接執行到該 function 結束,也就是說 nexti 不會暫停在其它的 function 。

[n] next 執行到下一行 source code

next 的縮寫為 n ,程式碼中的每一行都可以對應到組語中的其中一段程式,有可能一行程式碼對到十行組語,如果你使用 [si] 或 [ni] 都只能一行執行一行組語,而若是我們想直接執行一個程式碼,就可以使用 next ,有一點要特別注意的是,當你 compile 程式碼的時候要使用 g++ -g ,開始 debug mode 才能用這個功能 。

next example
  1. (gdb) b main
  2. (gdb) run
  3. .
  4. .
  5. (gdb) n
  6. 5 char const *message = "How are you?";
  7. (gdb) n
  8. 6 printf("%s", message);

[c] continue 執行到下一行 breakpoint

continue 的縮寫是 c ,功能很容易懂,就會直接執行直到下一個 breakpoint ,當然你要記得先設定 breakpoint ,例如 b *0x00000450400

watch 觀察特定變數

watch 可以用來偵測那個變數的值有被修改,當指定的變數被更改時,程式會暫停,並印出更改前後的數值,這個功能一樣要打開 debug 模式才能使用 (g++ -g)。

watch str , 觀察變數 str

watch (t > 10) , 觀察變數 t 是否大於 10

來看看 watch 的執行過程:

watch example
  1. (gdb) b main
  2. (gdb) run
  3. .
  4. .
  5. (gdb) watch message
  6. Hardware watchpoint 2: message
  7. (gdb) c
  8. Continuing.
  9. Hardware watchpoint 2: message
  10.  
  11. Old value = 0x0
  12. New value = 0x4006f4 "How are you?"
  13. main () at main2.cc:13
  14. 13 printf("%s", message);

frame

所有的程式,每一個 function 都會被分配到一個 frame ,每個 frame 都是一個 組語 stack ,存放所有組語指令,然後再一行一行的執行,例如當程式執行到 printf 這個 function 的時候,就會進入該 printf frame 。

frame 1, 進入 frame 1

up , 進入上一個 frame

down , 進入下一個 frame

bt , backtrace , 列出目前所有的 frame

來看看 frame 的執行過程:

frame example
  1. (gdb) bt
  2. #0 0x00000000004004d0 in printf@plt ()
  3. #1 0x0000000000400616 in showPassword () at main2.cc:5
  4. #2 0x0000000000400625 in main () at main2.cc:11
  5. (gdb) frame 1
  6. #1 0x0000000000400616 in showPassword () at main2.cc:5
  7. (gdb) down
  8. #0 0x00000000004004d0 in printf@plt ()
  9. (gdb) up
  10. #1 0x0000000000400616 in showPassword () at main2.cc:5

print : 印出某個變數或 memory address 的數值

print example
  1. (gdb) print x
  2. $1 = 0

printf : 一次印出兩個以上的變數

printf example
  1. (gdb) print "%d,%d\n",x,y
  2. 5,2

list 顯示目前程式執行到那一行

要使用這個功能的前提是你必須在編譯的時候,帶 -g 這個參數,

list example
  1. (gdb) list
  2. 1 #include "stdio.h"
  3. 2 int main () {
  4. 3 char const *message = "How are you?";
  5. 4 printf("%s", message);
  6. 5
  7. 6 return 0;
  8. 7 }

display : 當碰到 breakpoint 就印出某些個變數或 memory address 的數值

until : 執行完當前的迴圈

finish : 執行完當前的 function

GDB test specific process

Example
  1. sudo gdb -p 21611(process id)
  2.  
  3. b file.cc:277 (break point)
  4. c (continue)
  5. bt
  6.  
  7. ///usr/bin/strace -Ttt -s 1000 -p 6

dump void *

x/20c (*r->useragent_addr)->ipaddr_ptr

使用 nm 查詢 function 在哪一個 shared object 裡:

nm -C -A *.so | grep xxx_function

其它

  • x/i $pc 用 hex 印出目前位置
  • x/20c $pc
  • set $pc=0x0804852b : 修改當前的 memory address ,可以在 call xxx 時使用。
  • info frame
  • info stack
  • info functions 列出所有 function
  • set {int}0x8048667=32 : 修改 memory value

相關文章

網頁好讀版