使用gdb進行寫操作
阿新 • • 發佈:2017-08-04
line and exit eof 自己 bug eas inf spa
使用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進行寫操作