GDB无法插入断点,无法访问地址XXX的内存?

Abr*_*ham 7 c debugging gdb entry-point

我写了一个非常简单的程序:

ebrahim@ebrahim:~/test$ cat main.c
int main() {
    int i = 0;
    return i;
}
Run Code Online (Sandbox Code Playgroud)

而我编译它-s剥离模式:

ebrahim@ebrahim:~/test$ gcc -s main.c -o f3
ebrahim@ebrahim:~/test$ file f3
f3: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4dc6b893fbae8b418ca41ddeef948df1fcb26d3d, stripped
Run Code Online (Sandbox Code Playgroud)

现在,我正在尝试使用GDB找出主函数起始地址:

ebrahim@ebrahim:~/test$ gdb -nh f3
GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu2) 7.11.90.20161005-git
Copyright (C) 2016 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from f3...(no debugging symbols found)...done.
Run Code Online (Sandbox Code Playgroud)

由于文件中没有符号信息,我需要在文件入口点放置一个中断并反汇编它并找到main函数的起始地址.所以我用info file命令查找文件entry point地址:

(gdb) info file
Symbols from "/home/ebrahim/test/f3".
Local exec file:
    `/home/ebrahim/test/f3', file type elf64-x86-64.
    Entry point: 0x530     <<<<=============
    0x0000000000000238 - 0x0000000000000254 is .interp
    0x0000000000000254 - 0x0000000000000274 is .note.ABI-tag
    0x0000000000000274 - 0x0000000000000298 is .note.gnu.build-id
    0x0000000000000298 - 0x00000000000002b4 is .gnu.hash
    0x00000000000002b8 - 0x0000000000000360 is .dynsym
    0x0000000000000360 - 0x00000000000003f1 is .dynstr
    0x00000000000003f2 - 0x0000000000000400 is .gnu.version
    0x0000000000000400 - 0x0000000000000420 is .gnu.version_r
    0x0000000000000420 - 0x00000000000004f8 is .rela.dyn
    0x00000000000004f8 - 0x000000000000050f is .init
    0x0000000000000510 - 0x0000000000000520 is .plt
    0x0000000000000520 - 0x0000000000000528 is .plt.got
    0x0000000000000530 - 0x00000000000006e2 is .text
    0x00000000000006e4 - 0x00000000000006ed is .fini
    0x00000000000006f0 - 0x00000000000006f4 is .rodata
    0x00000000000006f4 - 0x0000000000000728 is .eh_frame_hdr
    0x0000000000000728 - 0x000000000000081c is .eh_frame
    0x0000000000200de0 - 0x0000000000200de8 is .init_array
    0x0000000000200de8 - 0x0000000000200df0 is .fini_array
    0x0000000000200df0 - 0x0000000000200df8 is .jcr
    0x0000000000200df8 - 0x0000000000200fb8 is .dynamic
    0x0000000000200fb8 - 0x0000000000201000 is .got
    0x0000000000201000 - 0x0000000000201010 is .data
    0x0000000000201010 - 0x0000000000201018 is .bss
Run Code Online (Sandbox Code Playgroud)

正如我们所期望的那样,切入点是.text节的开头.所以我在这个地址上加了一个断点:

(gdb) b *0x0000000000000530
Breakpoint 1 at 0x530
(gdb) r
Starting program: /home/ebrahim/test/f3 
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x530

(gdb)
Run Code Online (Sandbox Code Playgroud)

问题是为什么GDB无法插入此断点?

小智 9

剥离调试代码可能是(除逆向工程)很没用,但是你可以导致gdb停在第一个指令,你已经这样做了意外.如果无法映射断点的地址,则gdb停止并告诉您错误.作为副作用,您的程序在第一条指令处停止.保证不可映射的地址是0,所以只需执行以下操作:

(gdb) b *0
Breakpoint 1 at 0x0
(gdb) r
Starting program: [...]
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x0

(gdb) disas
Dump of assembler code for function _start:
=> 0x00007ffff7ddd190 <+0>: mov    %rsp,%rdi
   0x00007ffff7ddd193 <+3>: callq  0x7ffff7de0750 <_dl_start>
Run Code Online (Sandbox Code Playgroud)

在这里你看到PC坐在0x00007ffff7ddd190.所以这是你在运行时的入口点.

为了能够继续(或者:例如单步),您必须删除有问题的断点:

(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) c
Continuing.
Run Code Online (Sandbox Code Playgroud)

对于这个答案的看法是关于逆向工程的答案

  • 这对我不起作用。break *0x0 仍然导致“无法设置断点 1 无法访问 0x0 处的内存” -&gt; 我开始相信这与 x86_64 有关...顺便说一句,我在虚拟机上,内存地址似乎以某种方式被扰乱。我通过 readelf 仔细检查了 .text 部分,地址很好...但是,`break main`工作了[不确定剥离时是否仍然工作]。从那里我可以`stepi`到函数调用并使用`x/i $pc`来显示实际的内存位置。也许这对某人有帮助 (2认同)

Edw*_*ard 6

问题是您正在尝试调试共享对象,就好像它是可执行文件一样。特别是您file报告的:

ELF 64 位 LSB 共享对象

因为它是共享对象而不是可执行文件,所以您可能需要从实际的程序开始。在这种特殊情况下,您需要将该共享对象文件与您自己制作的另一个程序链接起来。例如,我创建了一个简单的共享对象:

snoot.c

#include <stdio.h>

int square(int test) {
    return test*test;
}

int func() {
    n = 7;
    printf("The answer is %d\n", square(n)-5);
}
Run Code Online (Sandbox Code Playgroud)

编译用

gcc -shared -fpic snoot.c -o libsnoot.so
strip libsnoot.so
Run Code Online (Sandbox Code Playgroud)

现在我们有了相当于您剥离的共享库的东西。如果我们这样做,objdump -T libsnoot.so我们会得到这个:

libsnoot.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000580 l    d  .init  0000000000000000              .init
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 printf
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000  w   D  *UND*  0000000000000000              _Jv_RegisterClasses
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000201028 g    D  .got.plt   0000000000000000  Base        _edata
00000000000006e0 g    DF .text  0000000000000010  Base        square
0000000000201030 g    D  .bss   0000000000000000  Base        _end
0000000000201028 g    D  .bss   0000000000000000  Base        __bss_start
0000000000000580 g    DF .init  0000000000000000  Base        _init
0000000000000724 g    DF .fini  0000000000000000  Base        _fini
00000000000006f0 g    DF .text  0000000000000032  Base        func
Run Code Online (Sandbox Code Playgroud)

该部分中唯一的两个符号.text是我们定义的两个函数。不幸的是,没有通用的方法来确定如何调用函数(也就是说,没有办法恢复原始的 C 函数原型),但我们可以简单地猜测。如果我们猜错了,堆栈就会关闭。例如,让我们尝试链接到square这个程序:

测试snoot.c

extern void square(void);

int main() {
    square();
}
Run Code Online (Sandbox Code Playgroud)

假设so文件位于同一目录中,我们可以像这样编译和链接:

gcc testsnoot.c -o testsnoot -L. -lsnoot
Run Code Online (Sandbox Code Playgroud)

现在我们可以正常调试了,因为这个测试驱动程序在我们的控制之下:

LD_LIBRARY_PATH =“。” gdb ./testsnoot

请注意,我们需要进行设置LD_LIBRARY_PATH,否则我们正在使用的库将不会被加载并且执行将终止。

(gdb) b square
Breakpoint 1 at 0x400560
(gdb) r
Starting program: /home/edward/test/testsnoot 
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.24-4.fc25.x86_64

Breakpoint 1, 0x00007ffff7bd56e4 in square () from ./libsnoot.so
(gdb) x/20i $pc
=> 0x7ffff7bd56e4 <square+4>:   mov    %edi,-0x4(%rbp)
   0x7ffff7bd56e7 <square+7>:   mov    -0x4(%rbp),%eax
   0x7ffff7bd56ea <square+10>:  imul   -0x4(%rbp),%eax
   0x7ffff7bd56ee <square+14>:  pop    %rbp
   0x7ffff7bd56ef <square+15>:  retq   
   0x7ffff7bd56f0 <func>:   push   %rbp
   0x7ffff7bd56f1 <func+1>: mov    %rsp,%rbp
   0x7ffff7bd56f4 <func+4>: sub    $0x10,%rsp
   0x7ffff7bd56f8 <func+8>: movl   $0x7,-0x4(%rbp)
   0x7ffff7bd56ff <func+15>:    mov    -0x4(%rbp),%eax
   0x7ffff7bd5702 <func+18>:    mov    %eax,%edi
   0x7ffff7bd5704 <func+20>:    callq  0x7ffff7bd55b0 <square@plt>
   0x7ffff7bd5709 <func+25>:    sub    $0x5,%eax
   0x7ffff7bd570c <func+28>:    mov    %eax,%esi
   0x7ffff7bd570e <func+30>:    lea    0x18(%rip),%rdi        # 0x7ffff7bd572d
   0x7ffff7bd5715 <func+37>:    mov    $0x0,%eax
   0x7ffff7bd571a <func+42>:    callq  0x7ffff7bd55c0 <printf@plt>
   0x7ffff7bd571f <func+47>:    nop
   0x7ffff7bd5720 <func+48>:    leaveq 
   0x7ffff7bd5721 <func+49>:    retq   
Run Code Online (Sandbox Code Playgroud)

现在您可以看到该函数的反汇编并了解它在做什么。在本例中,由于我们看到有一个引用,所以-0x4(%rbp)很明显该函数实际上需要一个参数,尽管我们并不真正知道是什么类型。

我们可以重写测试驱动功能并迭代地进行调试,直到我们了解剥离的库正在做什么。

既然我已经展示了一般程序,我假设您可以从那里获取它。