m;
在上面的代码中,第5行我们定义了一个变量g_verbose, 其值为false。现在,我们将通过gdb在第20行将其修改为true,给出三种方法,贯穿了修改变量的值,修改内存地址内容和修改寄存器。
2.1 修改某个变量的值
set var <name> = <value>
$ gcc -g -Wall -std=c99 -o foo foo.c
$ gdb foo
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...<snip>.......................................................................
(gdb) l main
11 printf("%-2d ", a[i]);
12 printf("\n");
15 int main(int argc, char *argv[])
17 int a[] = {0x1, 0x2, 0x3, 0x4, 0x5};
18 int n = sizeof(a) / sizeof(int);
20 if (g_verbose) {
(gdb) b 20
Breakpoint 1 at 0x8048500: file foo.c, line 20.
(gdb) r
Starting program: /tmp/foo
Breakpoint 1, main (argc=1, argv=0xbffff054) at foo.c:20
20 if (g_verbose) {
(gdb) p g_verbose
$1 = false
(gdb) set var g_verbose = true
(gdb) p g_verbose
$2 = true
(gdb) c
Continuing.
Now dump a[] ...
1 2 3 4 5
[Inferior 1 (process 5544) exited with code 017]
(gdb)
2.2 修改某个内存地址里的内容
set *(unsigned char *)<memaddr> = <value> ; write 1 byte
set *(unsigned short *)<memaddr> = <value> ; write 2 bytes
set *(unsigned int *)<memaddr> = <value> ; write 4 bytes
set *(unsigned long long *)<memaddr> = <value> ; write 8 bytes
set *(char *)<memaddr> = <value> ; write 1 byte
set *(short *)<memaddr> = <value> ; write 2 bytes
set *(int *)<memaddr> = <value> ; write 4 bytes
set *(long long *)<memaddr> = <value> ; write 8 bytes
$ gdb foo
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...<snip>.......................................................................
(gdb) l main
11 printf("%-2d ", a[i]);
12 printf("\n");
15 int main(int argc, char *argv[])
17 int a[] = {0x1, 0x2, 0x3, 0x4, 0x5};
18 int n = sizeof(a) / sizeof(int);
20 if (g_verbose) {
(gdb)
21 printf("Now dump a[] ...\n");
22 dump(a, n);
25 int m = 0;
26 for (int i = 0; i < n; i++)
27 m += a[i];
29 return m;
(gdb) b 20
Breakpoint 1 at 0x8048500: file foo.c, line 20.
(gdb) r
Starting program: /tmp/foo
Breakpoint 1, main (argc=1, argv=0xbffff054) at foo.c:20
20 if (g_verbose) {
(gdb) #
(gdb)
(gdb) p &g_verbose
$1 = (bool *) 0x804a02c <g_verbose>
(gdb) #
(gdb)
(gdb) x /x 0x804a02c
0x804a02c <g_verbose>: 0x00000000
(gdb) #
(gdb)
(gdb) set *(unsigned int *)0x804a02c = 0x1
(gdb) #
(gdb)
(gdb) x /x 0x804a02c
0x804a02c <g_verbose>: 0x00000001
(gdb) #
(gdb)
(gdb) c
Continuing.
Now dump a[] ...
1 2 3 4 5
[Inferior 1 (process 5731) exited with code 017]
(gdb) q
2.3 修改某个寄存器的值
set $<register> = <value>
$ gdb foo
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...<snip>.......................................................................
(gdb) display /i $eip
(gdb) set disassembly-flavor intel
(gdb) disas /m main
Dump of assembler code for function main:
0x080484c7 <+0>: push ebp
0x080484c8 <+1>: mov ebp,esp
0x080484ca <+3>: and esp,0xfffffff0
0x080484cd <+6>: sub esp,0x30
17 int a[] = {0x1, 0x2, 0x3, 0x4, 0x5};
0x080484d0 <+9>: mov DWORD PTR [esp+0x1c],0x1
0x080484d8 <+17>: mov DWORD PTR [esp+0x20],0x2
0x080484e0 <+25>: mov DWORD PTR [esp+0x24],0x3
0x080484e8 <+33>: mov DWORD PTR [esp+0x28],0x4
0x080484f0 <+41>: mov DWORD PTR [esp+0x2c],0x5
18 int n = sizeof(a) / sizeof(int);
0x080484f8 <+49>: mov DWORD PTR [esp+0x18],0x5
20 if (g_verbose) {
0x08048500 <+57>: mov eax,ds:0x804a02c
0x08048505 <+62>: test eax,eax
0x08048507 <+64>: je 0x8048529 <main+98>
21 printf("Now dump a[] ...\n");
0x08048509 <+66>: mov DWORD PTR [esp],0x80485f6
0x08048510 <+73>: call 0x8048340 <puts@plt>
22 dump(a, n);
0x08048515 <+78>: mov eax,DWORD PTR [esp+0x18]
0x08048519 <+82>: mov DWORD PTR [esp+0x4],eax
0x0804851d <+86>: lea eax,[esp+0x1c]
0x08048521 <+90>: mov DWORD PTR [esp],eax
0x08048524 <+93>: call 0x804847d <dump>
25 int m = 0;
0x08048529 <+98>: mov DWORD PTR [esp+0x10],0x0
26 for (int i = 0; i < n; i++)
0x08048531 <+106>: mov DWORD PTR [esp+0x14],0x0
0x08048539 <+114>: jmp 0x804854c <main+133>
0x08048547 <+128>: add DWORD PTR [esp+0x14],0x1
0x0804854c <+133>: mov eax,DWORD PTR [esp+0x14]
0x08048550 <+137>: cmp eax,DWORD PTR [esp+0x18]
0x08048554 <+141>: jl 0x804853b <main+116>
27 m += a[i];
0x0804853b <+116>: mov eax,DWORD PTR [esp+0x14]
0x0804853f <+120>: mov eax,DWORD PTR [esp+eax*4+0x1c]
0x08048543 <+124>: add DWORD PTR [esp+0x10],eax
29 return m;
0x08048556 <+143>: mov eax,DWORD PTR [esp+0x10]
0x0804855a <+147>: leave
0x0804855b <+148>: ret
End of assembler dump.
(gdb) b 20
Breakpoint 1 at 0x8048500: file foo.c, line 20.
(gdb) r
Starting program: /tmp/foo
Breakpoint 1, main (argc=1, argv=0xbffff054) at foo.c:20
20 if (g_verbose) {
1: x/i $eip
=> 0x8048500 <main+57>: mov eax,ds:0x804a02c
(gdb) #
(gdb)
(gdb) info r eax
eax 0x1 1
(gdb) ni
0x08048505 20 if (g_verbose) {
1: x/i $eip
=> 0x8048505 <main+62>: test eax,eax
(gdb) info r eax
eax 0x0 0
(gdb) #
(gdb) set $eax = 0x1
(gdb) #
(gdb)
(gdb) info r eax
eax 0x1 1
(gdb) c
Continuing.
Now dump a[] ...
1 2 3 4 5
[Inferior 1 (process 25323) exited with code 017]
(gdb) q
总结:
熟练掌握了gdb的读写操作,对于调试那种release版本的程序bug是相当有帮助的。最近两天,我在一个测试环境中调试一个跟子网掩码有关的bug。TNND可执行程序的符号表已经被删除,而且测试环境中不能clone源代码进行修改后编译(因为源代码保护)。幸运地是,还可以自己安装gdb。 有了gdb, 通过修改某个寄存器的值快速地把debug信息打印了出来,从而有效地缩小了bug存在的代码范围。一句话,gdb很好使,你值得拥有!