利用gdb在汇编指令级调试C程序

http://lenky.info/tag/%E8%B0%83%E8%AF%95/

利用gdb在汇编指令级调试C程序

2012年5月30日 没有评论 181 次浏览

关于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
[root@localhost test]# gdb ./a.out -q
(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
(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);
(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
(gdb) p $rip
$2 = ( void (*)()) 0x400467 <main+4>
(gdb) x/i $rip
0x400467 <main+4>:    mov    $0x5,%r8d
(gdb)

我们还可以利用一个名为pc的gdb内部变量:

1
2
3
4
5
(gdb) p $pc
$3 = ( void (*)()) 0x400467 <main+4>
(gdb) x/i $pc
0x400467 <main+4>:    mov    $0x5,%r8d
(gdb)

结合display命令和寄存器或pc内部变量,我们做如下设置:

1
2
3
4
(gdb) display /i $pc
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) display /3i $pc
(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
(gdb) display /i $pc
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
(gdb) help layout
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 register window. If existing layout
            is source/command or assembly/command, the
            register window is displayed. If the
            source/assembly/command (split) is displayed,
            the register window 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)
分类: *nix技术跟踪调试 标签:

利用KVM调试内核

2012年5月12日 没有评论 224 次浏览

虽然kvm运行的虚拟机也是host的一个进程,但是却不能像UML那样直接gdb attach到对应的进程进行调试,毕竟kvm和uml完全不同,如果那样做的话,你会发现你attach的只是qemu-system-x86进程:

1
2
3
4
5
(gdb) bt
#0  0x00007f8dba022ed2 in select () from /lib64/libc.so.6
#1  0x00007f8dbdd2118a in ?? () from /usr/local/bin/qemu-system-x86_64
#2  0x00007f8dbdd1a798 in main () from /usr/local/bin/qemu-system-x86_64
(gdb)

要用gdb调试kvm虚拟机内核,需要借助qemu-system-x86的两个选项:

1
2
-s              shorthand for -gdb tcp::1234
-S              freeze CPU at startup (use 'c' to start execution)

选项-s使得可以通过gdb远程连接qemu进行调试,而-S将让kvm虚拟机停止在执行第一条内核镜像代码的地方,等待gdb连接,如果没有-S选项,那么kvm不等待:

1
[root@localhost kvm]# qemu- system -x86_64 -hda vdisk.img -net none -m 1024 -daemonize -cpu host -smp 2 -vnc :1 -s

gdb可以通过127.0.0.1:1234或:1234(gdb在本机执行)或192.168.1.1:1234(gdb在另外的机器执行,而kvm host机器ip为192.168.1.1),假设在本host执行gdb命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost kvm]# gdb
GNU gdb Fedora (6.8-37.el5)
Copyright (C) 2008 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-redhat-linux-gnu" .
(gdb) target remote :1234
Remote debugging using :1234
[New Thread 1]
Remote 'g' packet reply is too long : d85f8780ffffffff88f58680ffffffff00000000000000000000000000000000180000000000000020fb7c80ffffffff40318880ffffffff205f8780ffffffff000000000000000063c3dd712e00000072feff00000000004bb52180ffffffffb76ddbb66ddbb66d20748b80ffffffffc09c8b80ffffffff0000000000000000241c2280ffffffff4602000010000000180000001800000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f03000000000000000000000000000000000000000000000000000000000000000000000000e03f00000000000000007b14ae47e17a843f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a01f0000
(gdb)

如果出现上面这种情况,需要先执行:set architecture i386:x86-64:intel,我的kvm客户机是x86-64:

1
2
[root@localhost ~]# uname -a
Linux localhost.localdomain 2.6.30-gentoo-r8 #55 SMP Thu May 10 20:05:44 CST 2012 x86_64 x86_64 x86_64 GNU/Linux

,使得gdb知道远程系统的架构:

1
2
3
4
5
6
7
(gdb) set architecture i386:x86-64:intel
The target architecture is assumed to be i386:x86-64:intel
(gdb) target remote :1234
Remote debugging using :1234
[New Thread 1]
0xffffffff80221c24 in ?? ()
(gdb)

加载对应的kvm客户机内核镜像,当然是未压缩的(务必选中内核选项:[*] Compile the kernel with debug info和[*] Compile the kernel with frame pointers):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(gdb) file /tmp/vmlinux
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /tmp/vmlinux...done.
(gdb) bt
#0  native_safe_halt () at /usr/src/linux-2.6.37.2/arch/x86/include/asm/irqflags.h:51
#1  0xffffffff80211e41 in default_idle ()
     at /usr/src/linux-2.6.37.2/arch/x86/include/asm/paravirt.h:802
#2  0xffffffff8020ab67 in cpu_idle ()
     at /usr/src/linux-2.6.37.2/arch/x86/kernel/process_64.c:149
#3  0xffffffff8061ab0d in rest_init () at /usr/src/linux-2.6.37.2/init/main.c:474
#4  0xffffffff808adcda in start_kernel () at /usr/src/linux-2.6.37.2/init/main.c:701
#5  0xffffffff808ad2a7 in x86_64_start_reservations (
     real_mode_data=<value optimized out>)
     at /usr/src/linux-2.6.37.2/arch/x86/kernel/head64.c:123
#6  0xffffffff808ad39f in x86_64_start_kernel (
     real_mode_data=0x93050 <Address 0x93050 out of bounds>)
     at /usr/src/linux-2.6.37.2/arch/x86/kernel/head64.c:94
#7  0x0000000000000000 in ?? ()
(gdb)

加个__schedule断点:

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
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
native_safe_halt () at /usr/src/linux-2.6.37.2/arch/x86/include/asm/irqflags.h:51
51  /usr/src/linux-2.6.37.2/arch/x86/include/asm/irqflags.h: No such file or directory.
     in /usr/src/linux-2.6.37.2/arch/x86/include/asm/irqflags.h
(gdb) b __schedule
Breakpoint 1 at 0xffffffff80636792: file /usr/src/linux-2.6.37.2/kernel/sched.c, line 5022.
(gdb) c
Continuing.
[New Thread 4]
[Switching to Thread 4]
 
Breakpoint 1, __schedule () at /usr/src/linux-2.6.37.2/kernel/sched.c:5022
5022    /usr/src/linux-2.6.37.2/kernel/sched.c: No such file or directory.
     in /usr/src/linux-2.6.37.2/kernel/sched.c
(gdb) bt
#0  __schedule () at /usr/src/linux-2.6.37.2/kernel/sched.c:5022
#1  0xffffffff80636f51 in schedule () at /usr/src/linux-2.6.37.2/kernel/sched.c:5084
#2  0xffffffff8020ab88 in cpu_idle ()
     at /usr/src/linux-2.6.37.2/arch/x86/kernel/process_64.c:159
#3  0xffffffff80632a4e in start_secondary (unused=<value optimized out>)
     at /usr/src/linux-2.6.37.2/arch/x86/kernel/smpboot.c:329
#4  0x0000000000000000 in ?? ()
(gdb)

利用命令q退出gdb时,如果导致kvm虚拟机终止,此时需先执行detach命令,后再退出gdb:

1
2
3
4
5
6
7
(gdb) q
The program is running.  Exit anyway? (y or n) n
Not confirmed.
(gdb) detach
Ending remote debugging.
(gdb) q
[root@localhost kvm]#

对于内核调试的一个十分有利帮助是串口的使用,kvm虚拟机的串口可以这样添加:

1
[root@localhost kvm]# qemu- system -x86_64 -hda vdisk.img -net none -m 1024 -daemonize -cpu host -smp 2 -vnc :1 -s -serial file:serial. log

这将在当前目录下生成一个serial.log的文件,kvm虚拟机的串口输出将重定向到这个文件内,比如给kvm虚拟机的内核加上串口输出选项(console=ttyS0,115200)后,kvm虚拟机的内核信息将输出到这个文件:

1
2
3
[root@localhost kvm]# ls serial. log -lh
-rw-r----- 1 root root 21K May 11 16:56 serial. log
[root@localhost kvm]#

还可以将kvm虚拟机的串口重定向到一个tcp监听口:

1
2
[root@localhost kvm]# qemu- system -x86_64 -hda vdisk.img -net none -m 1024 -daemonize -cpu host -smp 2 -vnc :1 -s -serial tcp::1235,server
QEMU waiting for connection on: tcp:0.0.0.0:1235,server

执行qemu-system-x86_64后进行等待链接状态,在本机可以执行(当然,你需要另开一个终端):

1
[root@localhost ~]# telnet 127.0.0.1 1235

在另外的机器,那么可执行(前面已交代这里kvm host机器的ip为192.168.1.1):

1
[root@localhost ~]# telnet 192.168.1.1 1235

之后,kvm虚拟机的串口输出将都打印在telnet上,并且此时可通过这个串口通道登陆kvm虚拟机。
另外,发现一个问题就是通过windows上的VNC Viewer 4远程连接到kvm虚拟机,进入grub后键盘就无响应,任何对内核选项的上下选择、编辑或启动都失效,此时无法做任何操作,只能在host机器内kill qemu-system-x86_64。如果在升级内核,这非常不方便,但值得庆幸的是qemu-system-x86_64支持直接在外部指定内核镜像(具体可以参考qemu-system-x86_64 –help):

1
2
[root@localhost kvm]# qemu- system -x86_64 -hda vdisk.img -net none -m 1024 -daemonize -cpu host -smp 2 -vnc :1 -kernel vmlinuz-2.6.18-194.el5 -initrd initrd-2.6.18-194.el5.img
[root@localhost kvm]#

所以,在装好最初的kvm虚拟机后立马把这两个文件备份到host机器来,这样如果后续捣鼓其它内核出了问题还能通过这种方法进入kvm虚拟机内进行修复(也许还可以利用其它工具,比如http://libguestfs.org/来进行,不过毕竟不是直接手段而比较麻烦)。
kvm虚拟机模块的调试要麻烦一点,首先需要在gdb里主动加载对应模块的符号,并且要加载到正确的位置。模块的代码位置可以在kvm虚拟机使用如下命令查看:

1
2
3
4
[root@localhost ~]# cat /proc/modules
igb 84012 0 - Live 0xffffffffa0007000
dca 6468 1 igb, Live 0xffffffffa0000000
[root@localhost ~]#

只加载了两个模块,以igb模块为例,在host机内的gdb内执行add-symbol-file,其中/tmp/igb.ko是kvm虚拟机的igb模块文件,拷贝到host机器内的,而0xffffffffa0007000是从上面/proc/modules文件内看到的:

1
2
3
4
5
6
7
(gdb) add-symbol-file /tmp/igb.ko 0xffffffffa0007000
add symbol table from file "/tmp/igb.ko" at
     .text_addr = 0xffffffffa0007000
(y or n) y
Reading symbols from /tmp/igb.ko...done.
(gdb) c
Continuing.

设置一个igb模块内的igb_clean_tx_irq函数断点试试,马上断下来了(因为我这里使用了igb ssh远程连接),看来没什么问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(gdb) b igb_clean_tx_irq
Breakpoint 2 at 0xffffffffa000a5a8
(gdb) c
Continuing.
[New Thread 2]
[Switching to Thread 2]
 
Breakpoint 2, 0xffffffffa000a5a8 in igb_clean_tx_irq ()
(gdb) bt
#0  0xffffffffa000a5a8 in igb_clean_tx_irq ()
#1  0xffffffffa000c19e in igb_msix_tx ()
#2  0xffffffff8027cb92 in handle_IRQ_event (irq=27, action=0xffff88003e18bf40)
     at /usr/src/linux-2.6.37.2/kernel/irq/handle.c:371
#3  0xffffffff8027e9f0 in handle_edge_irq (irq=27, desc=0xffff88003e6a85c0)
     at /usr/src/linux-2.6.37.2/kernel/irq/chip.c:514
#4  0xffffffff8020de43 in handle_irq (irq=27, regs=<value optimized out>)
     at /usr/src/linux-2.6.37.2/include/linux/irq.h:312
#5  0xffffffff8020d6a1 in do_IRQ (regs=0xffff88003f89de18) at /usr/src/linux-2.6.37.2/arch/x86/kernel/irq.c:215
#6  0xffffffff8020c453 in common_interrupt ()
#7  0xffff88003f89de40 in ?? ()
#8  0x0000000000000000 in ?? ()
(gdb) c
Continuing.

如果不执行对应的add-symbol-file命令,那么将会这样:

1
2
3
(gdb) b igb_clean_tx_irq
Function "igb_clean_tx_irq" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n

就算选择y,后续也不能捕获到该断点。

Linux下逆向工具

2012年1月12日 没有评论 366 次浏览

Windows下有很多逆向工具Linux下也有,当然,和Windows平台有所不同的是,Linux下的逆向工具主要是用来定位和分析程序的问题,比如当应用程序崩溃之后,如何从core文件里给出的信息查出原因所在。
曾经用过的一些命令如下(只列出主要功能):
addr2line:根据一个代码地址,定位到对应的源文件与代码行。
nm:列出程序里的符号。
objdump:反汇编。
readelf:显示任何ELF格式目标文件里的相关符号信息。
等等。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
[root@localhost db]# addr2line -e xfs_db -f 44c794
xfs_attr_leaf_add
/root/xfs.code/xfsprogs/libxfs/xfs_attr_leaf.c:785
[root@localhost db]# nm ./xfs_db
000000000047caa0 r C.135.10410
000000000046c608 t INIT_HLIST_NODE
0000000000692da4 b Rflag
0000000000692028 d _DYNAMIC
00000000006921f0 d _GLOBAL_OFFSET_TABLE_
000000000046e0c0 R _IO_stdin_used
                  w _Jv_RegisterClasses
0000000000692008 d __CTOR_END__
0000000000692000 d __CTOR_LIST__
0000000000692018 D __DTOR_END__
0000000000692010 d __DTOR_LIST__
0000000000491660 r __FRAME_END__
00000000004806e0 r __FUNCTION__.10009
...
[root@localhost db]# objdump --source xfs_db
 
xfs_db:     file format elf64-x86-64
 
Disassembly of section .init:
 
0000000000401e68 <_init>:
   401e68:   48 83 ec 08             sub    $0x8,%rsp
   401e6c:   e8 1b 07 00 00          callq  40258c <call_gmon_start>
   401e71:   e8 9a 07 00 00          callq  402610 <frame_dummy>
   401e76:   e8 e5 c1 06 00          callq  46e060 <__do_global_ctors_aux>
   401e7b:   48 83 c4 08             add    $0x8,%rsp
   401e7f:   c3                      retq
Disassembly of section .plt:
 
0000000000401e80 < ctime @plt-0x10>:
   401e80:   ff 35 72 03 29 00       pushq  2687858(%rip)        # 6921f8 <_GLOBAL_OFFSET_TABLE_+0x8>
   401e86:   ff 25 74 03 29 00       jmpq   *2687860(%rip)        # 692200 <_GLOBAL_OFFSET_TABLE_+0x10>
   401e8c:   0f 1f 40 00             nopl   0x0(%rax)
...
[root@localhost db]# readelf -S xfs_db
There are 37 section headers, starting at offset 0x1a8fb8:
 
Section Headers:
   [Nr] Name              Type             Address           Offset
        Size              EntSize          Flags  Link  Info  Align
   [ 0]                   NULL             0000000000000000  00000000
        0000000000000000  0000000000000000           0     0     0
   [ 1] .interp           PROGBITS         0000000000400200  00000200
        000000000000001c  0000000000000000   A       0     0     1
...
   [34] .shstrtab         STRTAB           0000000000000000  001a8e65
        0000000000000153  0000000000000000           0     0     1
   [35] .symtab           SYMTAB           0000000000000000  001a98f8
        000000000000d5d8  0000000000000018          36   1513     8
   [36] .strtab           STRTAB           0000000000000000  001b6ed0
        0000000000008402  0000000000000000           0     0     1
Key to Flags:
   W (write), A (alloc), X (execute), M (merge), S (strings)
   I (info), L (link order), G (group), x (unknown)
   O (extra OS processing required) o (OS specific), p (processor specific)
[root@localhost db]#

更多相关命令请参考这里:
http://sourceware.org/binutils/
http://sourceforge.net/projects/elftoolchain/
http://sourceforge.net/apps/trac/elftoolchain/

踩内存检测

2011年12月1日 没有评论 568 次浏览

众所周知,踩内存是一个非常麻烦的问题,不管是在应用层或是内核层,关于踩内存的检测也有各种各样的工具,比如应用层的优秀开源valgrind,内核内置的kmemcheck等。关于这些工具的具体信息就不在这里做进一步描述了,本文主要简单介绍一下几种踩内存工具的工作原理。
一种最为简单的踩内存工具为通过给分配的内存前后多加上一小块内存,用于存放我们预先设定的值,比如Crc和Magic:

不管是应用程序(App)或是内核模块(Module),它们分配内存都是利用我们重载过的接口(malloc或kmalloc或new或其它等),这些接口分配比实际请求要大一点的内存,以便在前和后能空留出一个或几个字节存放检测值(也就是前面提到的Crc和Magic)。Magic可以是我们预先指定的特定值,比如0xC0,而Crc可以是某些条件(比如当前请求内存分配的进程的id、名、代码行等)的crc值。应用程序或是内核模块释放内存同样也是利用我们重载过的接口(free或kfree或delete或其它等),在这些接口里就可以做踩内存检测,如果发现Magic不再是我们预先指定的特定值,比如0xC0,则表示该块内存被踩了,而Crc呢?虽然没有固定的值,但也可以通过做同样的计算后做对比检测,或者我们还可以提前在分配内存时把这些值记录下来,比如可以设置一个hash表,把App id/name, Code line,Module id/name, Code line …和对应的Crc存起来,通过扫描Crc是否存在来做判断,也可以通过这个hash表做Crc反查到对应的进程和模块。

可以看到这种方法的缺点就是发现踩内存比较慢,要等在内存释放时才能检测获知。所以,可以提供一些proc接口以便随时通过命令显示内存当前使用情况。
另一种可以实时检测到踩内存的做法很巧妙,在codeproject上有详细的英文介绍,下面也再介绍一下。应用程序(App)或是内核模块(Module)对内存的分配释放接口仍然由我们重载,这无需多说,它的主要思路就是不管应用程序(App)或是内核模块(Module)申请多么小的内存,我们都直接给它分配2个页面(这里先讨论实际申请的内存块小于1页的情况),而这块实际内存块在这2个页面里怎么放呢?有两种放法:

第一种(左)是把实际请求内存块放在第一页的末尾,而把第二页设置为只读,此时如果往后越界必定踩到第二页,而由于该页为只读属性,于是立即被OS弹出异常。
第二种(右)是把实际请求内存块放在第二页的开始,而把第一页设置为只读,此时如果往前越界必定踩到第一页,而由于该页为只读属性,于是立即被OS弹出异常。
可以看到,这两种情况都只能检测前向和后向中的任何一种越界,而对于相应的另一种就无能为力。如果实际申请的内存块大于1页的时也是如此(如下图,大于1页,小于2页的情况,其它类似),除非实际申请的内存块恰好为内存页整数倍(包括其它额外开销所占的内存),也许可以在前后都设置只读页。但是,我们有办法解决这个问题,那就是提供参数可以用于指定是进行前向越界检测还是后向越界检测,而实际程序/模块踩内存检查时,同时跑两个相同内核模块(可能需要两台机器)或两个相同应用程序,一个进行前向越界检测,一个进行后向越界检测,因为只要越界踩内存必定被OS抓出来,所以这种办法也算是可行的。

前面提到的两种方法对越界踩内存的检测比较可行,但对于随机踩就看运气了,而且运气还要比较好才行。而顺带说一下的第三种做法就是延用上面第二种方法的思想,把OS里没用到的内存都设置为只读,提高这种运气的可能性,然后就是念经拜佛,希望踩内存的那行代码,就给我踩到只读内存页,被OS抓出来吧。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值