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

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

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

我用-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 

现在,我正在尝试使用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  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: . Find the GDB manual and other documentation resources online at: . For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from f3...(no debugging symbols found)...done. 

由于文件中没有符号信息,我需要在文件入口点放置一个中断并反汇编它并找到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 

正如我们所期望的那样,入口点是.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) 

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

调试剥离的代码可能非常无用(逆向工程除外),但是你可以让 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> 

在这里,您看到PC位于0x00007ffff7ddd190 。 所以这是你在运行时的入口点。

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

 (gdb) delete Delete all breakpoints? (y or n) y (gdb) c Continuing. 

这个答案的功劳归结于逆向工程的答案

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

ELF 64位LSB共享对象

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

snoot.c

 #include  int square(int test) { return test*test; } int func() { n = 7; printf("The answer is %d\n", square(n)-5); } 

编译

 gcc -shared -fpic snoot.c -o libsnoot.so strip libsnoot.so 

现在我们有了相同的剥离共享库。 如果我们做objdump -T libsnoot.so我们得到这个:

 libsnoot.so: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000580 ld .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 

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

testsnoot.c

 extern void square(void); int main() { square(); } 

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

 gcc testsnoot.c -o testsnoot -L. -lsnoot 

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

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 : mov %edi,-0x4(%rbp) 0x7ffff7bd56e7 : mov -0x4(%rbp),%eax 0x7ffff7bd56ea : imul -0x4(%rbp),%eax 0x7ffff7bd56ee : pop %rbp 0x7ffff7bd56ef : retq 0x7ffff7bd56f0 : push %rbp 0x7ffff7bd56f1 : mov %rsp,%rbp 0x7ffff7bd56f4 : sub $0x10,%rsp 0x7ffff7bd56f8 : movl $0x7,-0x4(%rbp) 0x7ffff7bd56ff : mov -0x4(%rbp),%eax 0x7ffff7bd5702 : mov %eax,%edi 0x7ffff7bd5704 : callq 0x7ffff7bd55b0  0x7ffff7bd5709 : sub $0x5,%eax 0x7ffff7bd570c : mov %eax,%esi 0x7ffff7bd570e : lea 0x18(%rip),%rdi # 0x7ffff7bd572d 0x7ffff7bd5715 : mov $0x0,%eax 0x7ffff7bd571a : callq 0x7ffff7bd55c0  0x7ffff7bd571f : nop 0x7ffff7bd5720 : leaveq 0x7ffff7bd5721 : retq 

现在你可以看到函数的反汇编并看看它在做什么。 在这种情况下,因为我们看到有一个-0x4(%rbp)的引用,很明显这个函数实际上是在期待一个参数,尽管我们真的不知道是什么类型的。

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

我假设你可以从那里拿走它,现在我已经展示了一般程序。