1. 程式人生 > >使用gdb進行寫操作

使用gdb進行寫操作

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進行寫操作