关于GDB调试C程序的常用命令与手段就不多说了,这里主要介绍一下如何对C程序做到汇编指令级别的调试。
首先是获取汇编代码,这可以通过disassemble命令或x命令或类似的命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
(gdb) list 1 #include<stdio.h> 2 #include<malloc.h> 3 4 int callee(int a, int b, int c, int d, int e) 5 { 6 return 1; 7 } 8 9 int main(){ 10 callee(1,2,3,4,5); (gdb) disassemble main Dump of assembler code for function main: 0x0000000000400463 <main+0>: push %rbp 0x0000000000400464 <main+1>: mov %rsp,%rbp 0x0000000000400467 <main+4>: mov $0x5,%r8d 0x000000000040046d <main+10>: mov $0x4,%ecx 0x0000000000400472 <main+15>: mov $0x3,%edx 0x0000000000400477 <main+20>: mov $0x2,%esi 0x000000000040047c <main+25>: mov $0x1,%edi 0x0000000000400481 <main+30>: callq 0x400448 <callee> 0x0000000000400486 <main+35>: mov $0x2,%eax 0x000000000040048b <main+40>: leaveq 0x000000000040048c <main+41>: retq End of assembler dump. (gdb) x/10i main 0x400463 <main>: push %rbp 0x400464 <main+1>: mov %rsp,%rbp 0x400467 <main+4>: mov $0x5,%r8d 0x40046d <main+10>: mov $0x4,%ecx 0x400472 <main+15>: mov $0x3,%edx 0x400477 <main+20>: mov $0x2,%esi 0x40047c <main+25>: mov $0x1,%edi 0x400481 <main+30>: callq 0x400448 <callee> 0x400486 <main+35>: mov $0x2,%eax 0x40048b <main+40>: leaveq (gdb)
|
接着,利用display命令自动显示当前正要执行的汇编指令,display命令可以在每次程序暂停时自动打印指定变量的值。而我们要显示的汇编指令在ip寄存器内(当然,ip寄存器内存储的是机器码),我们可以看看(先得把程序执行起来):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
Breakpoint 1 at 0x400467: file t3.5.c, line 10. (gdb) r Starting program: /root/test/a.out Breakpoint 1, main () at t3.5.c:10 10 callee(1,2,3,4,5); (gdb) info reg rax 0x3cd2153a60 261222644320 rbx 0x3cd101bbc0 261204589504 rcx 0x4004a0 4195488 rdx 0x7fffc5f6fa38 140736514685496 rsi 0x7fffc5f6fa28 140736514685480 rdi 0x1 1 rbp 0x7fffc5f6f940 0x7fffc5f6f940 rsp 0x7fffc5f6f940 0x7fffc5f6f940 r8 0x3cd21522d0 261222638288 r9 0x3cd0e0d620 261202433568 r10 0x0 0 r11 0x3cd1e1d8a0 261219276960 r12 0x0 0 r13 0x7fffc5f6fa20 140736514685472 r14 0x0 0 r15 0x0 0 rip 0x400467 0x400467 <main+4> eflags 0x246 [ PF ZF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 fctrl 0x37f 895 fstat 0x0 0 ftag 0xffff 65535 fiseg 0x0 0 fioff 0x0 0 foseg 0x0 0 fooff 0x0 0 fop 0x0 0 mxcsr 0x1f80 [ IM DM ZM OM UM PM ] (gdb)
|
看汇编指令:
1
2
3
4
5
|
$2 = (void(*)()) 0x400467 <main+4> (gdb) x/i $rip 0x400467 <main+4>: mov $0x5,%r8d (gdb)
|
我们还可以利用一个名为pc的gdb内部变量:
1
2
3
4
5
|
$3 = (void(*)()) 0x400467 <main+4> (gdb) x/i $pc 0x400467 <main+4>: mov $0x5,%r8d (gdb)
|
结合display命令和寄存器或pc内部变量,我们做如下设置:
1
2
3
4
|
1: x/i $pc 0x400467 <main+4>: mov $0x5,%r8d (gdb)
|
或同时显示多条汇编,比如3条:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
(gdb) b main Breakpoint 1 at 0x400467: file t3.5.c, line 10. (gdb) r Starting program: /root/test/a.out Breakpoint 1, main () at t3.5.c:10 10 callee(1,2,3,4,5); 1: x/3i $pc 0x400467 <main+4>: mov $0x5,%r8d 0x40046d <main+10>: mov $0x4,%ecx 0x400472 <main+15>: mov $0x3,%edx (gdb)
|
接下来,利用ni(nexti)或si(stepi)命令进行汇编指令级的调试,如下所示可以看到参数是如何传递的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
1: x/i $pc 0x400467 <main+4>: mov $0x5,%r8d (gdb) ni 0x000000000040046d 10 callee(1,2,3,4,5); 1: x/i $pc 0x40046d <main+10>: mov $0x4,%ecx (gdb) ni 0x0000000000400472 10 callee(1,2,3,4,5); 1: x/i $pc 0x400472 <main+15>: mov $0x3,%edx (gdb) ni 0x0000000000400477 10 callee(1,2,3,4,5); 1: x/i $pc 0x400477 <main+20>: mov $0x2,%esi (gdb) ni 0x000000000040047c 10 callee(1,2,3,4,5); 1: x/i $pc 0x40047c <main+25>: mov $0x1,%edi (gdb)
|
更简单直接的方法是利用layout显示汇编代码窗口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
Change the layout of windows. Usage: layout prev | next | <layout_name> Layout names are: src : Displays source and command windows. asm : Displays disassembly and command windows. split : Displays source, disassembly and command windows. regs : Displays registerwindow. If existing layout is source/command or assembly/command, the registerwindow is displayed. If the source/assembly/command (split) is displayed, theregisterwindow is displayed with the window that has current logical focus. (gdb) layout asm lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk x0x400463 <main> push %rbp x x0x400464 <main+1> mov %rsp,%rbp x x0x400467 <main+4> mov $0x5,%r8d x x0x40046d <main+10> mov $0x4,%ecx x x0x400472 <main+15> mov $0x3,%edx x x0x400477 <main+20> mov $0x2,%esi x x0x40047c <main+25> mov $0x1,%edi x x0x400481 <main+30> callq 0x400448 <callee> x x0x400486 <main+35> mov $0x2,%eax x x0x40048b <main+40> leaveq x x0x40048c <main+41> retq x x0x40048d nop x x0x40048e nop x x0x40048f nop x x0x400490 <__libc_csu_fini> repz retq x x0x400492 nopl 0x0(%rax) x x0x400499 nopl 0x0(%rax) x x0x4004a0 <__libc_csu_init> mov %r12,-0x20(%rsp) x x0x4004a5 <__libc_csu_init+5> mov %r13,-0x18(%rsp) x x0x4004aa <__libc_csu_init+10> lea 0x2001bb(%rip),%r12 # 0x60066c x x0x4004b1 <__libc_csu_init+17> mov %r14,-0x10(%rsp) x x0x4004b6 <__libc_csu_init+22> mov %r15,-0x8(%rsp) x x0x4004bb <__libc_csu_init+27> mov %rsi,%r14 x x0x4004be <__libc_csu_init+30> mov %rbx,-0x30(%rsp) x x0x4004c3 <__libc_csu_init+35> mov %rbp,-0x28(%rsp) x x0x4004c8 <__libc_csu_init+40> sub $0x38,%rsp x mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj exec No process In: Line: ?? PC: 0x0 (gdb)
|
如果是7.0版本以上的gdb,那么还有一个方法显示汇编:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
[root@localhost gdb-7.4.1]# ./gdb/gdb -version | grep
"(GDB)"
GNU gdb (GDB) 7.4.1
[root@localhost gdb-7.4.1]# ./gdb/gdb ~/test/a.out
GNU gdb (GDB) 7.4.1
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http:
//gnu.org/licenses/gpl.html>
This is
free
software: you are
free
to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type
"show copying"
and
"show warranty"
for
details.
This GDB was configured as
"x86_64-unknown-linux-gnu"
.
For bug reporting instructions, please see:
<http:
//www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/test/a.out...done.
(gdb) set disassemble-next-line on
(gdb) b main
Breakpoint 1 at 0x400467: file t3.5.c, line 10.
(gdb) r
Starting program: /root/test/a.out
Breakpoint 1, main () at t3.5.c:10
10 callee(1,2,3,4,5);
=> 0x0000000000400467 <main+4>: 41 b8 05 00 00 00 mov $0x5,%r8d
0x000000000040046d <main+10>: b9 04 00 00 00 mov $0x4,%ecx
0x0000000000400472 <main+15>: ba 03 00 00 00 mov $0x3,%edx
0x0000000000400477 <main+20>: be 02 00 00 00 mov $0x2,%esi
0x000000000040047c <main+25>: bf 01 00 00 00 mov $0x1,%edi
0x0000000000400481 <main+30>: e8 c2 ff ff ff callq 0x400448 <callee>
(gdb) ni
0x000000000040046d 10 callee(1,2,3,4,5);
0x0000000000400467 <main+4>: 41 b8 05 00 00 00 mov $0x5,%r8d
=> 0x000000000040046d <main+10>: b9 04 00 00 00 mov $0x4,%ecx
0x0000000000400472 <main+15>: ba 03 00 00 00 mov $0x3,%edx
0x0000000000400477 <main+20>: be 02 00 00 00 mov $0x2,%esi
0x000000000040047c <main+25>: bf 01 00 00 00 mov $0x1,%edi
0x0000000000400481 <main+30>: e8 c2 ff ff ff callq 0x400448 <callee>
(gdb)
|
另外,7.0版本以上gdb的disas命令可以携带/m参数,让汇编与c源码同时显示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
(gdb) disas /m main
Dump of assembler code
for
function main:
9
int
main(){
0x0000000000400463 <+0>: push %rbp
0x0000000000400464 <+1>: mov %rsp,%rbp
10 callee(1,2,3,4,5);
0x0000000000400467 <+4>: mov $0x5,%r8d
0x000000000040046d <+10>: mov $0x4,%ecx
0x0000000000400472 <+15>: mov $0x3,%edx
0x0000000000400477 <+20>: mov $0x2,%esi
0x000000000040047c <+25>: mov $0x1,%edi
0x0000000000400481 <+30>: callq 0x400448 <callee>
11
return
2;
0x0000000000400486 <+35>: mov $0x2,%eax
12 }
0x000000000040048b <+40>: leaveq
0x000000000040048c <+41>: retq
End of assembler dump.
(gdb)
|
转载请保留地址:http://lenky.info/2012/05/30/%e5%88%a9%e7%94%a8gdb%e5%9c%a8%e6%b1%87%e7%bc%96%e6%8c%87%e4%bb%a4%e7%ba%a7%e8%b0%83%e8%af%95c%e7%a8%8b%e5%ba%8f/或 http://lenky.info/?p=1694