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

相关文章


回應 (Leave a comment)