使用gdb进行写操作
Posted veli
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用gdb进行写操作相关的知识,希望对你有一定的参考价值。
使用gdb调试程序,读写操作是很普遍的事情。其中,读操作包括:
- 读取某个变量的值
- 读取某个内存地址里的内容
- 读取某个寄存器的值
对应地,写操作包括:
- 修改某个变量的值
- 修改某个内存地址里的内容
- 修改某个寄存器的值
本文将首先简单介绍一下读操作,然后重点介绍一下写操作。
1. 读操作
- 读取某个变量的值: p <var>
- 读取某个内存地址里的内容: x /NFU <memaddr>
- 读取某个寄存器的值: info r <register>
2. 写操作
先上个例子// foo.c
1 #include <stdio.h> 2 3 typedef enum {false, true} bool; 4 5 static bool g_verbose = 0; 6 7 static void 8 dump(int a[], int n) 9 { 10 for (int i = 0; i < n; i++) 11 printf("%-2d ", a[i]); 12 printf("\n"); 13 } 14 15 int main(int argc, char *argv[]) 16 { 17 int a[] = {0x1, 0x2, 0x3, 0x4, 0x5}; 18 int n = sizeof(a) / sizeof(int); 19 20 if (g_verbose) { 21 printf("Now dump a[] ...\n"); 22 dump(a, n); 23 } 24 25 int m = 0; 26 for (int i = 0; i < n; i++) 27 m += a[i]; 28 29 return m; 30 }
在上面的代码中,第5行我们定义了一个变量g_verbose, 其值为false。现在,我们将通过gdb在第20行将其修改为true,给出三种方法,贯穿了修改变量的值,修改内存地址内容和修改寄存器。
2.1 修改某个变量的值
set var <name> = <value>
e.g.
$ 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"); 13 } 14 15 int main(int argc, char *argv[]) 16 { 17 int a[] = {0x1, 0x2, 0x3, 0x4, 0x5}; 18 int n = sizeof(a) / sizeof(int); 19 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 or 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
e.g.
$ 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"); 13 } 14 15 int main(int argc, char *argv[]) 16 { 17 int a[] = {0x1, 0x2, 0x3, 0x4, 0x5}; 18 int n = sizeof(a) / sizeof(int); 19 20 if (g_verbose) { (gdb) 21 printf("Now dump a[] ...\n"); 22 dump(a, n); 23 } 24 25 int m = 0; 26 for (int i = 0; i < n; i++) 27 m += a[i]; 28 29 return m; 30 } (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>
e.g.
$ 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: 16 { 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 19 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 <[email protected]> 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> 23 } 24 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 28 29 return m; 0x08048556 <+143>: mov eax,DWORD PTR [esp+0x10] 30 } 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很好使,你值得拥有!
以上是关于使用gdb进行写操作的主要内容,如果未能解决你的问题,请参考以下文章
Linux C/C++代码 使用gdb进行coredump调试