在 GDB 中的第一条机器代码指令处停止

Posted

技术标签:

【中文标题】在 GDB 中的第一条机器代码指令处停止【英文标题】:Stopping at the first machine code instruction in GDB 【发布时间】:2012-05-16 01:08:02 【问题描述】:

将可执行文件加载到 gdb 后,如何在执行第一条指令之前在入口点中断?

我正在分析的可执行文件是一段经过加密的恶意软件,因此break main 完全没有任何作用。

【问题讨论】:

对于lldb,请参阅:How to stop debugger right after the execution? 相关:reverseengineering.stackexchange.com/questions/8724/… 【参考方案1】:

info files 命令可能会给你一个可以破解的地址:

(gdb) info files
    ...
    Entry point: 0x80000000
    ...
(gdb) break *0x80000000
(gdb) run

【讨论】:

这对我来说实际上不适用于fasm /dev/stdin test <<< $'format ELF executable\nint3' 生成的简单 ELF。 (如果我理解正确的话)加载地址与虚拟地址不同,根据this answer,对于某些程序。程序运行前,显示的地址是根据程序的虚拟地址,不一定是加载地址。 是的,根据我的经验,在启动 gdb 后,它会显示 0x10e0。我设置了一个断点,运行,它无法插入断点。但此时i files0x5555555550e0 显示为入口点。有了这个就可以了。 它不适用于一些没有节的精心制作的文件(例如,使用 upx 生成的文件)。一般情况下,您必须手动从readelf -h $binary 输出中复制入口点地址。【参考方案2】:

b _start”或“b start”可能会也可能不会起作用。如果没有,用 readelf/objdump 找出入口点地址并使用“b *0x<hex address>”。

【讨论】:

奇怪的是它在标头中指定的入口点中断并且反汇编看起来不错,但是直接反汇编可执行文件会显示垃圾。但是你回答了这个问题。 ;) 顺便说一句,很荣幸能从 Hex-Rays 的一个人那里得到答案! 当我在 GDB 中尝试 b _init; run 时,来自 cru/init-first.c_init 似乎甚至在 _start 或 GCC 4.8 glibc 2.19 Ubuntu 14.04 中的入口地址之前运行。发生了什么事? 提问于:***.com/questions/31379422/… 即使它确实有效,它也可能不是动态链接的可执行文件中的第一条用户空间指令。动态链接器首先运行。【参考方案3】:

将可执行文件加载到 gdb 后,如何在执行第一条指令之前在入口点中断?

您可以找到在int main()set backtrace past-main on 之前调用的函数,找到它们后在它们上设置断点并重新启动程序:

>gdb  -q  main
Reading symbols from /home/main...done.
(gdb) set backtrace past-main on
(gdb) b main
Breakpoint 1 at 0x40058a: file main.cpp, line 25.
(gdb) r
Starting program: /home/main

Breakpoint 1, main () at main.cpp:25
25        a();
(gdb) bt
#0  main () at main.cpp:25
#1  0x0000003a1d81ed1d in __libc_start_main () from /lib64/libc.so.6
#2  0x0000000000400499 in _start ()
(gdb) b _start
Breakpoint 2 at 0x400470
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/main

Breakpoint 2, 0x0000000000400470 in _start ()

【讨论】:

【参考方案4】:

此 hack 已被 starti 淘汰,但如果您使用较旧的 GDB,则很有用。

不费吹灰之力的解决方案是利用失败的副作用来设置断点:

$ gdb /bin/true
Reading symbols from /bin/true...(no debugging symbols found)...done.
(gdb) b *0
Breakpoint 1 at 0x0
(gdb) r
Starting program: /bin/true 
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x0

(gdb) disas
Dump of assembler code for function _start:
=> 0xf7fdd800 <+0>:     mov    eax,esp
   0xf7fdd802 <+2>:     call   0xf7fe2160 <_dl_start>
End of assembler dump.

(gdb) d 1       # delete the faulty breakpoint

(您需要delete无效断点才能继续或单步执行。)

想法来自this answer at RE.SE。

【讨论】:

有趣的是,在您的解决方案之前,我无法在 Go 应用程序的 GDB 中使用断点。任何其他方法都不起作用。 对我来说也很有效,但是即使stepi 也失败了,所以我还必须使用delete breakpoints 来继续。 @Ped7g 你可以删除你设置为失败的确切断点,在上面的例子中它是d 1。无需全部删除。【参考方案5】:

从 GDB 8.1 开始,有一个特殊的命令:starti。示例 GDB 会话:

$ gdb /bin/true
Reading symbols from /bin/true...(no debugging symbols found)...done.
(gdb) starti
Starting program: /bin/true 

Program stopped.
0xf7fdd800 in _start () from /lib/ld-linux.so.2
(gdb) x/5i $pc
=> 0xf7fdd800 <_start>: mov    eax,esp
   0xf7fdd802 <_start+2>:       call   0xf7fe2160 <_dl_start>
   0xf7fdd807 <_dl_start_user>: mov    edi,eax
   0xf7fdd809 <_dl_start_user+2>:       call   0xf7fdd7f0
   0xf7fdd80e <_dl_start_user+7>:       add    ebx,0x1f7e6

【讨论】:

这个答案应该以某种方式传播,因为这是自 gdb 8.1 版本以来最简洁的解决方案。 此解决方案在/lib/ld-linux.so.2 中的第一条指令处中断,当人们对给定可执行文件的第一条指令感兴趣时,这可能毫无意义。 @codeman48 这仅适用于动态可执行文件。但是,如果您真的想调试从main 开始的动态可执行文件,请在__libc_start_main 处放置一个断点,并将其第一个参数作为新断点的地址。不过你会错过静态初始化,所以要小心。 @Ruslan 是的,在静态构建上它会起作用。对于动态构建,据我了解,一种可行的方法是了解Entry point address: 输出中的readelf -h &lt;elf_name&gt; 并在那里设置断点。我一直看到这个地址指向_start,之后调用__libc_start_main,然后是main...

以上是关于在 GDB 中的第一条机器代码指令处停止的主要内容,如果未能解决你的问题,请参考以下文章

计算机开机加电时,运行的第一条指令是

gdb中的nexti和stepi有啥区别?

王爽《汇编》检测9.1 | 若要使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?

CPU加电后,第一条指令是多少? 这条指令在内存中,还是BIOS中,为啥?

计算机开机运行是如何实现第一条取指指令的?请从电路层级解释

在 gdb 中寻找特定功能