gdb 的組成架構

gdb 基本工作原理
gdb 通過系統(tǒng)調用 ptrace 來接管一個進程的執(zhí)行。ptrace 系統(tǒng)調用提供了一種方法使得父進程可以觀察和控制其它進程的執(zhí)行,檢查和改變其核心映像以及寄存器。它主要用來實現斷點調試和系統(tǒng)調用跟蹤。ptrace 系統(tǒng)調用的原型如下:
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
- pid_t pid:指示 ptrace 要跟蹤的進程。 - void *addr:指示要監(jiān)控的內存地址。 - void *data:存放讀取出的或者要寫入的數據。 - enum __ptrace_request request:決定了系統(tǒng)調用的功能,幾個主要的選項: - PTRACE_TRACEME:表示此進程將被父進程跟蹤,任何信號(除了 SIGKILL)都會暫停子進程,接著阻塞于 wait() 等待的父進程被喚醒。子進程內部對 exec() 的調用將發(fā)出 SIGTRAP 信號,這可以讓父進程在子進程新程序開始運行之前就完全控制它。 - PTRACE_ATTACH:attach 到一個指定的進程,使其成為當前進程跟蹤的子進程,而子進程的行為等同于它進行了一次 PTRACE_TRACEME 操作。但需要注意的是,雖然當前進程成為被跟蹤進程的父進程,但是子進程使用 getppid() 的到的仍將是其原始父進程的 pid。 - PTRACE_CONT:繼續(xù)運行之前停止的子進程。可同時向子進程交付指定的信號。
gdb 的三種調試方式
- 運行并調試一個新進程
- 運行 gdb,通過命令行或
file命令指定目標程序。 - 輸入
run命令, gdb 執(zhí)行下面的操作:- 通過
fork()系統(tǒng)調用創(chuàng)建一個新進程 - 在新創(chuàng)建的子進程中執(zhí)行操作:
ptrace(PTRACE_TRACEME, 0, 0, 0) - 在子進程中通過
execv()系統(tǒng)調用加載用戶指定的可執(zhí)行文件
- 通過
- attach 并調試一個已經運行的進程
- 用戶確定需要進行調試的進程 PID
- 運行 gdb,輸入
attach <pid>,gdb 將對指定進程執(zhí)行操作:ptrace(PTRACE_ATTACH, pid, 0, 0) - 遠程調試目標機上新創(chuàng)建的進程
- gdb 運行在調試機上,gdbserver 運行在目標機上,兩者之間的通信數據格式由 gdb 遠程串行協(xié)議(Remote Serial Protocol)定義
- RSP 協(xié)議數據的基本格式為:
$..........#xx - gdbserver 的啟動方式相當于運行并調試一個新創(chuàng)建的進程
注意,在你將 gdb attach 到一個進程時,可能會出現這樣的問題:
gdb-peda$ attach 9091
Attaching to process 9091
ptrace: Operation not permitted.
這是因為開啟了內核參數 ptrace_scope:
$ cat /proc/sys/kernel/yama/ptrace_scope
1
1 表示 True,此時普通用戶進程是不能對其他進程進行 attach 操作的,當然你可以用 root 權限啟動 gdb,但最好的辦法還是關掉它:
# echo 0 > /proc/sys/kernel/yama/ptrace_scope
斷點的實現
斷點的功能是通過內核信號實現的,在 x86 架構上,內核向某個地址打入斷點,實際上就是往該地址寫入斷點指令 INT 3,即 0xCC。目標程序運行到這條指令之后會觸發(fā) SIGTRAP 信號,gdb 捕獲這個信號,并根據目標程序當前停止的位置查詢 gdb 維護的斷點鏈表,若發(fā)現在該地址確實存在斷點,則可判定為斷點命中。
gdb 基本操作
使用 -tui 選項可以將代碼顯示在一個漂亮的交互式窗口中。
break -- b
-
break當不帶參數時,在所選棧幀中執(zhí)行的下一條指令處設置斷點。 -
break <function>在函數體入口處打斷點。 -
break <line>在當前源碼文件指定行的開始處打斷點。 -
break -Nbreak +N在當前源碼行前面或后面的N行開始處打斷點,N為正整數。 -
break <filename:line>在源碼文件filename的line行處打斷點。 -
break <filename:function>在源碼文件filename的function函數入口處打斷點。 -
break <address>在程序指令的地址處打斷點。 -
break ... if <cond>設置條件斷點,...代表上述參數之一(或無參數),cond為條件表達式,僅在cond值非零時停住程序。
info breakpoints -- i b
查看斷點,觀察點和捕獲點的列表。用法: - info breakpoints [list…] - info break [list…]
list… 用來指定若干個斷點的編號(可省略),可以是 2, 1-3, 2 5 等。
disable -- dis
禁用斷點,參數使用空格分隔。不帶參數時禁用所有斷點。 - disable [breakpoints] [list…] breakpoints 是 disable 的子命令(可省略),list… 同 info breakpoints 中的描述。
enable
啟用斷點,參數使用空格分隔。不帶參數時啟用所有斷點。 - enable [breakpoints] [list…] 啟用指定的斷點(或所有定義的斷點)。 - enable [breakpoints] once list… 臨時啟用指定的斷點。GDB 在停止您的程序后立即禁用這些斷點。 - enable [breakpoints] delete list… 使指定的斷點啟用一次,然后刪除。一旦您的程序停止,GDB 就會刪除這些斷點。等效于用 tbreak 設置的斷點。
breakpoints 同 disable 中的描述。
clear
在指定行或函數處清除斷點。參數可以是行號,函數名稱或 * 跟一個地址。 - clear 當不帶參數時,清除所選棧幀在執(zhí)行的源碼行中的所有斷點。 - clear <function>, clear <filename:function> 刪除在命名函數的入口處設置的任何斷點。 - clear <line>, clear <filename:line> 刪除在指定的文件指定的行號的代碼中設置的任何斷點。 - clear <address> 清除指定程序指令的地址處的斷點。
delete -- d
刪除斷點。參數使用空格分隔。不帶參數時刪除所有斷點。 - delete [breakpoints] [list…]
tbreak
設置臨時斷點。參數形式同 break 一樣。當第一次命中時被刪除。
watch
為表達式設置觀察點。每當一個表達式的值改變時,觀察點就會停止執(zhí)行您的程序。 - watch [-l|-location] <expr> 如果給出了 -l 或者 -location,則它會對 expr 求值并觀察它所指向的內存。
step -- s
單步執(zhí)行程序,直到到達不同的源碼行。 - step [N] 參數 N 表示執(zhí)行 N 次(或由于另一個原因直到程序停止)。
reverse-step
反向步進程序,直到到達另一個源碼行的開頭。 - reverse-step [N] 參數 N 表示執(zhí)行 N 次(或由于另一個原因直到程序停止)。
next -- n
單步執(zhí)行程序,執(zhí)行完子程序調用。 - next [N]
與 step 不同,如果當前的源代碼行調用子程序,則此命令不會進入子程序,而是繼續(xù)執(zhí)行,將其視為單個源代碼行。
reverse-next
反向步進程序,執(zhí)行完子程序調用。 - reverse-next [N]
如果要執(zhí)行的源代碼行調用子程序,則此命令不會進入子程序,調用被視為一個指令。
return
您可以使用 return 命令取消函數調用的執(zhí)行。如果你給出一個表達式參數,它的值被用作函數的返回值。 - return <expression> 將 expression 的值作為函數的返回值并使函數直接返回。
finish -- fin
執(zhí)行直到選定的棧幀返回。 - finish
until -- u
執(zhí)行程序直到大于當前棧幀或當前棧幀中的指定位置(與 break 命令相同的參數)的源碼行。此命令常用于通過一個循環(huán),以避免單步執(zhí)行。 - until <location> 繼續(xù)運行程序,直到達到指定的位置,或者當前棧幀返回。
continue -- c
在信號或斷點之后,繼續(xù)運行被調試的程序。 - continue [N]
如果從斷點開始,可以使用數字 N 作為參數,這意味著將該斷點的忽略計數設置為 N - 1(以便斷點在第 N 次到達之前不會中斷)。
print -- p
求表達式 expr 的值并打印。可訪問的變量是所選棧幀的詞法環(huán)境,以及范圍為全局或整個文件的所有變量。 - print [expr] - print /f [expr] 通過指定 /f 來選擇不同的打印格式,其中 f 是一個指定格式的字母
x
檢查內存。 - x/nfu <addr> - x <addr>
n, f, 和 u 都是可選參數,用于指定要顯示的內存以及如何格式化。 addr 是要開始顯示內存的地址的表達式。 n 重復次數(默認值是 1),指定要顯示多少個單位(由 u 指定)的內存值。 f 顯示格式(初始默認值是 x),顯示格式是 print('x','d','u','o','t','a','c','f','s') 使用的格式之一,再加 i(機器指令)。 u 單位大小,b 表示單字節(jié),h 表示雙字節(jié),w 表示四字節(jié),g 表示八字節(jié)。
display
每次程序停止時打印表達式 expr 的值。 - display <expr> - display/fmt <expr> - display/fmt <addr>
fmt 用于指定顯示格式。對于格式 i 或 s,或者包括單位大小或單位數量,將表達式 addr 添加為每次程序停止時要檢查的內存地址。
info display
打印自動顯示的表達式列表,每個表達式都帶有項目編號,但不顯示其值。
undisplay
取消某些表達式在程序停止時自動顯示。參數是表達式的編號(使用 info display 查詢編號)。不帶參數表示取消所有自動顯示表達式。
disable display
禁用某些表達式在程序停止時自動顯示。禁用的顯示項目被再次啟用。參數是表達式的編號(使用 info display 查詢編號)。不帶參數表示禁用所有自動顯示表達式。
enable display
啟用某些表達式在程序停止時自動顯示。參數是重新顯示的表達式的編號(使用 info display 查詢編號)。不帶參數表示啟用所有自動顯示表達式。
help -- h
打印命令列表。 - help <class> 您可以獲取該類中各個命令的列表。 - help <command> 顯示如何使用該命令的簡述。
attach
掛接到 GDB 之外的進程或文件。將進程 ID 或設備文件作為參數。 - attach <process-id>
run -- r
啟動被調試的程序??梢灾苯又付▍?,也可以用 set args 設置(啟動所需的)參數。還允許使用 >, <, 或 >> 進行輸入和輸出重定向。
甚至可以運行一個腳本,如:
run `python2 -c 'print "A"*100'`
backtrace -- bt
打印整個棧的回溯。
-
bt打印整個棧的回溯,每個棧幀一行。 -
bt n類似于上,但只打印最內層的 n 個棧幀。 -
bt -n類似于上,但只打印最外層的 n 個棧幀。 -
bt full n類似于bt n,還打印局部變量的值。
注意:使用 gdb 調試時,會自動關閉 ASLR,所以可能每次看到的棧地址都不變。
ptype
打印類型 TYPE 的定義。 - ptype[/FLAGS] TYPE-NAME | EXPRESSION
參數可以是由 typedef 定義的類型名, 或者 struct STRUCT-TAG 或者 class CLASS-NAME 或者 union UNION-TAG 或者 enum ENUM-TAG。
set follow-fork-mode
當程序 fork 出一個子進程的時候,gdb 默認會追蹤父進程(set follow-fork-mode parent),但也可以使用命令 set follow-fork-mode child 讓其追蹤子進程。
另外,如果想要同時追蹤父進程和子進程,可以使用命令 set detach-on-fork off(默認為on),這樣就可以同時調試父子進程,在調試其中一個進程時,另一個進程被掛起。如果想讓父子進程同時運行,可以使用 set schedule-multiple on(默認為off)。
但如果程序是使用 exec 來啟動了一個新的程序,可以使用 set follow-exec-mode new(默認為same) 來新建一個 inferior 給新程序,而父進程的 inferior 仍然保留。
info threads
打印出所有線程的信息,包含 Thread ID、Target ID 和 Frame。
thread apply all bt
打印出所有線程的堆棧信息。
gdb-peda
當 gdb 啟動時,它會在當前用戶的主目錄中尋找一個名為 .gdbinit 的文件;如果該文件存在,則 gdb 就執(zhí)行該文件中的所有命令。通常,該文件用于簡單的配置命令。但是 .gdbinit 的配置十分繁瑣,因此對 gdb 的擴展通常用插件的方式來實現,通過 python 的腳本可以很方便的實現需要的功能。
PEDA(Python Exploit Development Assistance for GDB)是一個強大的 gdb 插件。它提供了高亮顯示反匯編代碼、寄存器、內存信息等人性化的功能。同時,PEDA 還有一些實用的新命令,比如 checksec 可以查看程序開啟了哪些安全機制等等。
安裝
安裝 peda 需要的軟件包:
$ sudo apt-get install nasm micro-inetd
$ sudo apt-get install libc6-dbg vim ssh
安裝 peda:
$ git clone https://github.com/longld/peda.git ~/peda
$ echo "source ~/peda/peda.py" >> ~/.gdbinit
$ echo "DONE! debug your program with gdb and enjoy"
如果系統(tǒng)為 Arch Linux,則可以直接安裝:
$ yaourt -S peda
peda命令?
-
aslr-- 顯示/設置 gdb 的 ASLR -
asmsearch-- Search for ASM instructions in memory asmsearch "int 0x80"asmsearch "add esp, ?" libc-
assemble-- On the fly assemble and execute instructions using NASM assembleassemble $pc > mov al, 0xb > int 0x80 > end-
checksec-- 檢查二進制文件的安全選項 -
cmpmem-- Compare content of a memory region with a file cmpmem 0x08049000 0x0804a000 data.mem-
context-- Display various information of current execution context -
context_code-- Display nearby disassembly at $PC of current execution context -
context_register-- Display register information of current execution context -
context_stack-- Display stack of current execution contextcontext regcontext codecontext stack
-
crashdump-- Display crashdump info and save to file -
deactive-- Bypass a function by ignoring its execution (eg sleep/alarm) deactive setresuiddeactive chdir-
distance-- Calculate distance between two addresses -
dumpargs-- 在調用指令停止時顯示傳遞給函數的參數 -
dumpmem-- Dump content of a memory region to raw binary file dumpmem libc.mem libc-
dumprop-- 在特定的內存范圍顯示 ROP gadgets dumpropdumprop binary "pop"-
eflags-- Display/set/clear/toggle value of eflags register -
elfheader-- 獲取正在調試的 ELF 文件的頭信息 elfheaderelfheader .got-
elfsymbol-- 從 ELF 文件中獲取沒有調試信息的符號信息 elfsymbolelfsymbol printf-
gennop-- Generate abitrary length NOP sled using given characters gennop 500gennop 500 "\x90"-
getfile-- Get exec filename of current debugged process -
getpid-- Get PID of current debugged process -
goto-- Continue execution at an address -
help-- Print the usage manual for PEDA commands -
hexdump-- Display hex/ascii dump of data in memory hexdump $sp 64hexdump $sp /20-
hexprint-- Display hexified of data in memory hexprint $sp 64hexprint $sp /20-
jmpcall-- Search for JMP/CALL instructions in memory jmpcalljmpcall eaxjmpcall esp libc-
loadmem-- Load contents of a raw binary file to memory loadmem stack.mem 0xbffdf000-
lookup-- 搜索屬于內存范圍的地址的所有地址/引用 lookup address stack libclookup pointer stack ld-2-
nearpc-- Disassemble instructions nearby current PC or given address nearpc 20nearpc 0x08048484-
nextcall-- Step until next 'call' instruction in specific memory range nextcall cpy-
nextjmp-- Step until next 'j*' instruction in specific memory range nextjmp-
nxtest-- Perform real NX test to see if it is enabled/supported by OS -
patch-- 使用字符串/十六進制字符串/整形數 patch $esp 0xdeadbeefpatch $eax "the long string"patch (multiple lines)-
pattern-- 生成,搜索或寫入循環(huán) pattern 到內存 -
pattern_arg-- Set argument list with cyclic pattern -
pattern_create-- Generate a cyclic pattern -
pattern_env-- Set environment variable with a cyclic pattern -
pattern_offset-- Search for offset of a value in cyclic pattern -
pattern_patch-- Write a cyclic pattern to memory -
pattern_search-- Search a cyclic pattern in registers and memorypattern create 2000pattern create 2000 inputpattern offset $pcpattern searchpattern patch 0xdeadbeef 100
-
payload-- Generate various type of ROP payload using ret2plt payload copybytespayload copybytes target "/bin/sh"payload copybytes 0x0804a010 offset-
pdisass-- Format output of gdb disassemble command with colors pdisass $pc /20-
pltbreak-- Set breakpoint at PLT functions match name regex pltbreak cpy-
procinfo-- 顯示調試進程的 /proc/pid/ procinfoprocinfo fd-
profile-- Simple profiling to count executed instructions in the program -
pyhelp-- Wrapper for python built-in help pyhelp pedapyhelp hex2str-
pshow-- 顯示各種 PEDA 選項和其他設置 pshowpshow option context-
pset-- 設置各種 PEDA 選項和其他設置 pset arg '"A"*200'pset arg 'cyclic_pattern(200)'pset env EGG 'cyclic_pattern(200)'pset option context "code,stack"pset option badchars "\r\n"-
readelf-- 獲取 ELF 的文件頭信息 readelf libc .text-
refsearch-- Search for all references to a value in memory ranges refsearch "/bin/sh"refsearch 0xdeadbeef-
reload-- Reload PEDA sources, keep current options untouch -
ropgadget-- 獲取二進制或庫的常見 ROP gadgets ropgadgetropgadget libc-
ropsearch-- 搜索內存中的 ROP gadgets ropsearch "pop eax"ropsearch "xchg eax, esp" libc-
searchmem|find-- 搜索內存中的 pattern; 支持正則表達式搜索 find "/bin/sh" libcfind 0xdeadbeef allfind "..\x04\x08" 0x08048000 0x08049000-
searchmem-- Search for a pattern in memory; support regex search -
session-- Save/restore a working gdb session to file as a script -
set-- Set various PEDA options and other settings set exec-wrapper ./exploit.py-
sgrep-- Search for full strings contain the given pattern -
shellcode-- 生成或下載常見的 shellcode shellcode x86/linux exec-
show-- Show various PEDA options and other settings -
skeleton-- 生成 python exploit 代碼模板 skeleton argv exploit.py-
skipi-- Skip execution of next count instructions -
snapshot-- Save/restore process's snapshot to/from file snapshot savesnapshot restore-
start-- Start debugged program and stop at most convenient entry -
stepuntil-- Step until a desired instruction in specific memory range stepuntil cmpstepuntil xor-
strings-- Display printable strings in memory stringsstrings binary 4-
substr-- Search for substrings of a given string/number in memory -
telescope-- Display memory content at an address with smart dereferences telescope 40telescope 0xb7d88000 40-
tracecall-- Trace function calls made by the program tracecalltracecall "cpy,printf"tracecall "-puts,fflush"-
traceinst-- Trace specific instructions executed by the program traceinst 20traceinst "cmp,xor"-
unptrace-- Disable anti-ptrace detection unptrace-
utils-- Miscelaneous utilities from utils module -
vmmap-- 在調試過程中獲取段的虛擬映射地址范圍 cmmapvmmap binary / libcvmmap 0xb7d88000-
waitfor-- Try to attach to new forked process; mimic "attach -waitfor" waitforwaitfor myprog -c-
xinfo-- Display detail information of address/registers xinfo register eaxxinfo 0xb7d88000-
xormem-- 用一個 key 來對一個內存區(qū)域執(zhí)行 XOR 操作 xormem 0x08049000 0x0804a000 “thekey”-
xprint-- Extra support to GDB's print command -
xrefs-- Search for all call/data access references to a function/variable -
xuntil-- Continue execution until an address or function
使用 PEDA 和 Python 編寫 gdb 腳本
- 全局類
-
pedacmd:- 交互式命令
- 沒有返回值
- 例如:
pedacmd.context_register()
-
peda:- 與 gdb 交互的后端功能
- 有返回值
- 例如:
peda.getreg("eax")
- 小工具
- 例如:
to_int()、format_address() - 獲得幫助
pyhelp pedapyhelp hex2str
- 例如:
- 單行/交互式使用
gdb-peda$ python print peda.get_vmmap()gdb-peda$ python > status = peda.get_status() > while status == "BREAKPOINT": > peda.execute("continue") > end- 外部腳本
# myscript.py def myrun(size): argv = cyclic_pattern(size) peda.execute("set arg %s" % argv) peda.execute("run") ?wzxhzdk:8? gdb-peda$ source myscript.py gdb-peda$ python myrun(100)