Debugging 显示在gdb中执行的每个汇编指令

Debugging 显示在gdb中执行的每个汇编指令,debugging,assembly,gdb,freebsd,Debugging,Assembly,Gdb,Freebsd,我目前有一个棘手的错误,发生在我无法访问源代码或符号的地方,即我可以看到发生崩溃的指令及其地址,但仅此而已我想做的是让gdb在不需要交互的情况下运行,并在运行时显示每一条指令,但我还没有找到一种方法 我希望实现的是这样的目标: (gdb) /* some command */ 0x2818a7c0: push %ebp 0x2818a7c1: mov %esp,%ebp 0x2818a7c3: push %ebx 0x2818a7c4: sub $0x4,%esp ... 0

我目前有一个棘手的错误,发生在我无法访问源代码或符号的地方,即我可以看到发生崩溃的指令及其地址,但仅此而已我想做的是让
gdb
在不需要交互的情况下运行,并在运行时显示每一条指令,但我还没有找到一种方法

我希望实现的是这样的目标:

(gdb) /* some command */
0x2818a7c0: push   %ebp
0x2818a7c1: mov    %esp,%ebp
0x2818a7c3: push   %ebx
0x2818a7c4: sub    $0x4,%esp
...
0x28563622: mov    %esi,0x0(%eax)
Program received signal SIGSEGV, Segmentation fault.
我一直在做的是设置程序计数器的显示,如下所示:

(gdb) display/i $pc
然后使用
stepi
运行代码:

(gdb) stepi
1: x/i $pc  0x2818a7c0: push   %ebp
然而,崩溃离指令有数百或数千条之遥,我希望有一种方法可以看到每一条指令(如果更好的话,可以放在一起),而不必按“回车”那么多次。另外,如果我手动执行,我会看到每条指令之间出现
(gdb)
提示,这是不理想的

我简要研究过的一种方法是,但我唯一的想法是设置在
main()
,让它显示和另一个中断(用于下一条指令),然后继续,但这样我就不能在
命令块中使用
命令,所以它不会按照我想象的方式工作

如果有关系的话,我正在研究FreeBSD

  • 分别分解二进制文件(例如使用objdump),并在调试时查阅清单
  • 使用及其调试器。更好的经验

  • (免责声明:我为Hex-Rays工作)

    以下内容应满足您的要求:

    # not strictly required, but you'll likely want the log anyway
    (gdb) set logging on
    
    # ask gdb to not stop every screen-full
    (gdb) set height 0
    
    (gdb) while 1
     > x/i $pc
     > stepi
     > end
    
    然而,这种调试方法很可能被证明是徒劳的:即使在最简单的程序中执行的指令也太多了

    更好的方法可能是运行程序直到崩溃,尝试了解当前函数在做什么以及谁调用它,并适当设置断点

    在x86上,即使在完全剥离的可执行文件中,也常常可以推断函数边界


    您还需要了解的另一件事是
    strace/truss
    输出,这样您就可以看到崩溃点之前的系统调用。

    Python脚本

    这将提供比GDB脚本更大的灵活性来实现您的疯狂想法

    这里的主要问题,就像GDB脚本一样,对于没有目标硬件支持的大多数应用程序来说,这可能太慢,例如:一个C hello world只需要1分钟就可以完成18k指令

    gdb.py

    class TraceAsm(gdb.Command):
        def __init__(self):
            super().__init__(
                'trace-asm',
                gdb.COMMAND_BREAKPOINTS,
                gdb.COMPLETE_NONE,
                False
            )
        def invoke(self, argument, from_tty):
            argv = gdb.string_to_argv(argument)
            if argv:
                gdb.write('Does not take any arguments.\n')
            else:
                done = False
                thread = gdb.inferiors()[0].threads()[0]
                last_path = None
                last_line = None
                with open('trace.tmp', 'w') as f:
                    while thread.is_valid():
                        frame = gdb.selected_frame()
                        sal = frame.find_sal()
                        symtab = sal.symtab
                        if symtab:
                            path = symtab.fullname()
                            line = sal.line
                        else:
                            path = None
                            line = None
                        if path != last_path:
                            f.write("path {}{}".format(path, os.linesep))
                            last_path = path
                        if line != last_line:
                            f.write("line {}{}".format(line, os.linesep))
                            last_line = line
                        pc = frame.pc()
                        f.write("{} {} {}".format(hex(pc), frame.architecture().disassemble(pc)[0]['asm'], os.linesep))
                        gdb.execute('si', to_string=True)
    TraceAsm()
    

    梅因

    global _start
    _start:
        ; Write.
        mov rax, 1
        mov rdi, 1
        mov rsi, hello_world
        mov rdx, hello_world_len
        syscall
    
        ; Exit.
        mov rax, 60
        mov rdi, 0
        syscall
    
    hello_world db "hello world", 10
    hello_world_len equ $ - hello_world
    

    组装和运行:

    as -o main.o main.S
    ld -o main.out main.o
    gdb -nh -batch -ex 'source ~/test/gdb.py' -ex 'starti' -ex 'trace-asm' ./main.out
    cat trace.tmp
    
    输出:

    0x401000 mov    $0x1,%rax 
    0x401007 mov    $0x1,%rdi 
    0x40100e mov    $0x402000,%rsi 
    0x401015 mov    $0xc,%rdx 
    0x40101c syscall  
    0x40101e mov    $0x3c,%rax 
    0x401025 mov    $0x0,%rdi 
    0x40102c syscall
    
    warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
    ----------------
    IN: 
    0x0000000000401000:  mov    $0x1,%rax
    0x0000000000401007:  mov    $0x1,%rdi
    0x000000000040100e:  mov    $0x402000,%rsi
    0x0000000000401015:  mov    $0xc,%rdx
    0x000000000040101c:  syscall 
    
    hello world
    ----------------
    IN: 
    0x000000000040101e:  mov    $0x3c,%rax
    0x0000000000401025:  mov    $0x0,%rdi
    0x000000000040102c:  syscall
    
    QEMU仿真

    这比GDB python解决方案执行得快得多,C hello works可以瞬间运行!但是,同一个可执行文件上的日志只有10k指令长,而不是18k指令长,因此它必须跳过一些通常要运行才能理解的内容

    例如,在用户模式模拟中:

    qemu-x86_64 -d in_asm ./main.out
    
    输出:

    0x401000 mov    $0x1,%rax 
    0x401007 mov    $0x1,%rdi 
    0x40100e mov    $0x402000,%rsi 
    0x401015 mov    $0xc,%rdx 
    0x40101c syscall  
    0x40101e mov    $0x3c,%rax 
    0x401025 mov    $0x0,%rdi 
    0x40102c syscall
    
    warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
    ----------------
    IN: 
    0x0000000000401000:  mov    $0x1,%rax
    0x0000000000401007:  mov    $0x1,%rdi
    0x000000000040100e:  mov    $0x402000,%rsi
    0x0000000000401015:  mov    $0xc,%rdx
    0x000000000040101c:  syscall 
    
    hello world
    ----------------
    IN: 
    0x000000000040101e:  mov    $0x3c,%rax
    0x0000000000401025:  mov    $0x0,%rdi
    0x000000000040102c:  syscall
    


    在Ubuntu 18.10、GDB 8.2、QEMU 2.12.0中测试。

    我更新了我的问题,注意到我目前正在使用FreeBSD,我认为这排除了#2?啊,是的,目前IDA不支持调试FreeBSD二进制文件。但是,您仍然可以将其用作反汇编程序(在Windows/Wine上或在FreeBSD上使用Linux支持),这比查看死列表要好得多。是否有需要导入的特殊gdb模块?@ttb hi,您只需像常规gdb脚本一样为其提供源代码,请参阅更新的答案。绑定内置于GDB中,不需要安装任何额外的东西。在回顾这篇文章的同时,我也从QEMU中得到了一些乐趣,并补充了答案。