使用gdb反向拆解核心文件

4
在模糊测试过程中,被测应用程序崩溃,并生成了一个核心文件。在使用gdb打开核心文件后,它显示该应用程序没有调试符号。
使用backtrace命令可以得到以下结果:
 Program terminated with signal 11, Segmentation fault.
 #0  0x0000000000000000 in ?? () 
  (gdb) bt
 #0  0x0000000000000000 in ?? ()
 #1  0x00007f8749f065d3 in ?? ()
 #2  0x00007f874c6a9000 in ?? ()
 #3  0x00007f8749f06568 in ?? ()
 #4  0x00007f874c6ab9a0 in ?? ()
 #5  0x00007f874c6aaa00 in ?? ()
 #6  0x0000000000000001 in ?? ()
 #7  0x00007f8749f06664 in ?? ()
 #8  0x0000000000000000 in ?? ()

那么,当我输入 (gdb) disass 0x00007f8749f065d3, +1 后,我会得到以下输出:
Dump of assembler code from 0x7f8749f065d3 to 0x7f8749f065d4:
   0x00007f8749f065d3:  mov    QWORD PTR [rbx+0x70],0x0
End of assembler dump.

现在我的问题是:

例如,当我在前一行(0x00007f8749f065d3)时,想要分析执行前两行之前的代码,那么我应该输入什么?

注意:像(gdb) disass 0x00007f8749f065d3, -2这样的命令,我直觉地输入并没有帮助。

最好的问候,


你是如何调用GDB的?你又是如何“打开核心文件”的?很有可能,你并没有做对。 - Employed Russian
我在shell中输入了"gdb /path_to_app/crashed_app_name /path_to_core_file/core"。因此,第一个参数是gdb,第二个参数是崩溃应用程序的路径,第三个参数是核心文件的路径。 - user3097712
1个回答

7
你需要了解如何将源代码行与汇编指令关联起来吗?或者你是想知道如何在当前指令之前反汇编指令?
以下是两个问题的答案。
回溯命令中显示的十六进制数字是函数入口点的程序计数器地址。地址处的汇编命令将是一个单一的指令,比如call或jmp。
像gcc这样的编译器会将源代码转换为汇编指令。编译器将在可执行映像中包含“DWARF”信息。DWARF信息将用于将一组汇编指令与特定的源代码行相关联。
考虑以下C片段:
int main(int argc, char *argv[])
   {
       int s, tnum, opt, num_threads;
       struct thread_info *tinfo;
       pthread_attr_t attr;
       int stack_size;
       void *res;

       /* The "-s" option specifies a stack size for our threads */

   stack_size = -1;


  while ((opt = getopt(argc, argv, "s:")) != -1) {
       switch (opt) {
       case 's':
           stack_size = strtoul(optarg, NULL, 0);
           break;

使用disass /m命令可以展示gdb如何将源代码行与汇编代码关联起来。

48         {
   0x0000000000400bdf <+0>: push   %rbp
   0x0000000000400be0 <+1>: mov    %rsp,%rbp
   0x0000000000400be3 <+4>: add    $0xffffffffffffff80,%rsp
   0x0000000000400be7 <+8>: mov    %edi,-0x74(%rbp)
   0x0000000000400bea <+11>:    mov    %rsi,-0x80(%rbp)
   0x0000000000400bee <+15>:    mov    %fs:0x28,%rax
   0x0000000000400bf7 <+24>:    mov    %rax,-0x8(%rbp)
   0x0000000000400bfb <+28>:    xor    %eax,%eax

49             int s, tnum, opt, num_threads;
50             struct thread_info *tinfo;
51             pthread_attr_t attr;
52             int stack_size;
53             void *res;
54  
55             /* The "-s" option specifies a stack size for our threads */
56  
57             stack_size = -1;
   0x0000000000400bfd <+30>:    movl   $0xffffffff,-0x60(%rbp)

58             while ((opt = getopt(argc, argv, "s:")) != -1) {
   0x0000000000400c04 <+37>:    jmp    0x400c56 <main+119>
   0x0000000000400c56 <+119>:   mov    -0x80(%rbp),%rcx
   0x0000000000400c5a <+123>:   mov    -0x74(%rbp),%eax
   0x0000000000400c5d <+126>:   mov    $0x401002,%edx
   0x0000000000400c62 <+131>:   mov    %rcx,%rsi
   0x0000000000400c65 <+134>:   mov    %eax,%edi
   0x0000000000400c67 <+136>:   callq  0x400a00 <getopt@plt>
   0x0000000000400c6c <+141>:   mov    %eax,-0x5c(%rbp)
   0x0000000000400c6f <+144>:   cmpl   $0xffffffff,-0x5c(%rbp)
   0x0000000000400c73 <+148>:   jne    0x400c06 <main+39>

59                 switch (opt) {
   0x0000000000400c06 <+39>:    mov    -0x5c(%rbp),%eax
   0x0000000000400c09 <+42>:    cmp    $0x73,%eax
   0x0000000000400c0c <+45>:    jne    0x400c2c <main+77>

60                 case 's':
61                     stack_size = strtoul(optarg, NULL, 0);
   0x0000000000400c0e <+47>:    mov    0x2014cb(%rip),%rax        # 0x6020e0 <optarg@@GLIBC_2.2.5>
   0x0000000000400c15 <+54>:    mov    $0x0,%edx
   0x0000000000400c1a <+59>:    mov    $0x0,%esi
   0x0000000000400c1f <+64>:    mov    %rax,%rdi
   0x0000000000400c22 <+67>:    callq  0x400a10 <strtoul@plt>
   0x0000000000400c27 <+72>:    mov    %eax,-0x60(%rbp)

62                     break;
   0x0000000000400c2a <+75>:    jmp    0x400c56 <main+119>

一些源代码行是单条指令,而其他行由多条指令组成。请注意,一些行的地址顺序也是不按顺序的。
如您所见,在可执行文件中没有调试信息,就没有自动将汇编指令与源代码行相关联的方法。调试信息的目的就是将这两者关联起来。
对于像x86这样的可变长度指令结构,没有简单的方法向后查找上一条指令。在调试器中,最好的策略是只需减去一些值(比如16),然后进行反汇编。如果当前指令正确,则大部分反汇编都是正确的。从我的示例中,让我们考虑0x0000000000400c06。
因此,通过减去16(0x10),我们得到了0x400bf6。
(gdb) disass 0x0000000000400bf6, +20
Dump of assembler code from 0x400bf6 to 0x400c0a:
   0x0000000000400bf6 <main+23>:    add    %cl,-0x77(%rax)
   0x0000000000400bf9 <main+26>:    rex.RB clc 
   0x0000000000400bfb <main+28>:    xor    %eax,%eax
   0x0000000000400bfd <main+30>:    movl   $0xffffffff,-0x60(%rbp)
   0x0000000000400c04 <main+37>:    jmp    0x400c56 <main+119>
   0x0000000000400c06 <main+39>:    mov    -0x5c(%rbp),%eax
   0x0000000000400c09 <main+42>:    cmp    $0x73,%eax

与上面的反汇编相比,从“xor”指令开始的反汇编是正确的。如果你熟悉汇编语言,你会注意到像“rex”这样的奇怪指令。
合理地问一下gdb如何反汇编代码。答案是它总是从函数的第一条指令开始向下工作。
好问题!

你好,Matthew。谢谢你的回答。我明白了。所以,你取特定地址,减去一些值,然后进行反汇编。好的。这也是一个想法,但我认为可能有一个gdb指令可以实现它。我错了。但我喜欢你的方法。 - user3097712

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接