如何在gdb中调用汇编?

8
在gdb中,我可以使用call来运行函数,但如果我想要运行一些额外的汇编代码怎么办?
2个回答

9
在GCC 5之前(1),我不知道有什么方法可以运行任意的机器码,除非你将机器码输入到内存中并运行它。
如果您想要运行已经在内存中的代码,只需将指令指针设置为开始位置,在结尾处设置断点,然后运行。然后,在断点之后,将指令指针更改回其原始值即可。
但实际上我看不出这种情况的用例。这并不意味着没有这样的用例,只是您可以通过直接修改寄存器、标志、内存等来实现运行代码的任何操作。
例如,以下命令:
info registers

在执行时,会将寄存器的当前值转储出来:

set $eax = 42

将会把 eax 寄存器更改为 42

您也可以通过以下方式更改内存:

set *((char*)0xb7ffeca0) = 4

这会将一个单字节写入内存位置0xb7ffeca0,您也可以使用同样的方法存储更宽的数据类型。
(1) GCC 5允许您使用compile code命令编译和执行任意代码,文档在这里记录。

如果不能直接运行mov等指令,如何修改寄存器? - gdb
@gdb:例如,您可以使用set $eax = 42。请参见更新。 - paxdiablo
你能演示一下如何使用“set”更改内存吗? - gdb
有时需要在内存中执行特定的汇编指令。这可能是一些多媒体命令,其结果可能不容易计算。或者是一些常规命令,执行起来比手动编辑寄存器/内存容易得多,并且存在犯错的可能性。 - Hi-Angel
制作一个Python API命令会很有趣。找到一些可用的自由内存,编译,将命令写入其中,将ESP更改为它,将最后一个命令设置为跳回。可能会很困难 :-) - Ciro Santilli OurBigBook.com
“编译代码”最近被添加到GDB中,允许“任意”编译:https://dev59.com/1VXTa4cB1Zd3GeqP4LRs#31709579 - Ciro Santilli OurBigBook.com

4

编译代码命令

该命令在7.9左右引入,允许进行代码编译和注入,文档请参考:https://sourceware.org/gdb/onlinedocs/gdb/Compiling-and-Injecting-Code.html

main.c

#include <stdio.h>

int main(void) {
    int i = 0;
    printf("%d\n", i);
    return 0;
}

编译和运行:

gcc -ggdb3 -o main.out main.c
gdb main.out

然后在GDB中:

(gdb) start
Temporary breakpoint 1 at 0x113d: file main.c, line 4.
Starting program: /home/ciro/test/main.out 

Temporary breakpoint 1, main () at main.c:4
4           int i = 0;
(gdb) next
5           printf("%d\n", i);
(gdb) compile code int j = 1; i = j; asm("nop");
(gdb) continue
Continuing.
1
[Inferior 1 (process 30256) exited normally]

程序输出:

1

要让此功能在GDB 7.9.1、GCC 5.1中正常工作,我必须使用以下命令运行GDB:

LD_LIBRARY_PATH="$(dirname "$(gcc -print-libgcc-file-name)"):$LD_LIBRARY_PATH" gdb main.out

为了让libcc1.so可见:这是最近的GCC组件,它向cc1编译器公开了C API,但在Ubuntu 19.04上不再需要。然而,在一个最小化的环境中仍然需要它来支持C++:

main.cpp

#include <iostream>

int main() {
    int i = 0;
    std::cout << i << std::endl;
}

并且:

compile code int j = 1; i = j; asm("nop");

有几个结构不符合我的预期:

  • return: In the GDB compile code command, what language constructs behave exactly as if they were present in the original source?

  • includes such as:

    compile code printf('asdf\n')
    

    in Ubuntu 19.04.

    On main.c above, printf without include worked because we already included it in the source, but if I try it on an empty main() without the include it fails with:

    gdb command line:1:1: warning: incompatible implicit declaration of built-in function ‘printf’
    gdb command line:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
    gdb command line:1:8: warning: character constant too long for its type
    gdb command line:1:8: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast [-Wint-conversion]
    gdb command line:1:8: note: expected ‘const char *’ but argument is of type ‘int’
    gdb command line:1:1: warning: format not a string literal and no format arguments [-Wformat-security]
    
  • In C++ Ubuntu 19.04:

    compile code std::cout << "Hello world\n";
    

    failed with:

    *** WARNING *** there are active plugins, do not report this as a bug unless you can reproduce it without enabling any plugins.
    Event                            | Plugins
    PLUGIN_PRE_GENERICIZE            | libcp1plugin
    PLUGIN_GGC_MARKING               | libcp1plugin
    PLUGIN_PRAGMAS                   | libcp1plugin
    gdb command line:1:6: internal compiler error: in plugin_build_decl, at libcc1/libcp1plugin.cc:1059
    Please submit a full bug report,
    with preprocessed source if appropriate.
    See <file:///usr/share/doc/gcc-8/README.Bugs> for instructions.
    Compilation failed.
    

    See also: How to compile C++ code in GDB?


1
当语言为C时,似乎这并不适用于像asm volatile ("mov $abc, %eax")这样的东西。在compile命令返回后,$rax仍然保持其原始值。GDB 7.10不支持源语言为汇编语言时的compile命令,因此即使在调试使用汇编语言编写的函数时,这也无法正常工作。 - Peter Cordes
@PeterCordes 谢谢,我还没有尝试过那个。compile 真的非常有限。但是它有潜力。 - Ciro Santilli OurBigBook.com
是的,它看起来对于C/C++非常方便,但不适用于尝试使用不同输入执行指令。我猜你可以将C变量用作输出操作数,但那么你需要一条指令来使用它。此时,将指令放入文件中,进行汇编/链接并单步执行可能更容易。感谢您指出这一点,即使它不是对问题的好答案。 >.< - Peter Cordes
您使用 std::cout 的示例可以使用 -raw 参数重写为 compile code -raw #include <iostream> ^Mvoid _gdb_expr() { std::cout << "Hello\n"; }(在此处将 ^M 替换为 Ctrl-V,Enter)。这对我有效。 - Ruslan
@Ruslan感谢您澄清了-raw的作用!不幸的是,我无法在Ubuntu 19.04上使其工作 :-( C++的hello world失败并显示“‘cout’ is not a member of ‘std’”,而一个空的main则会在尝试时显示“warning: Could not find symbol "_ZSt4cout" for compiled module "/tmp/gdbobj-dxRNbH/out1.o".”。 - Ciro Santilli OurBigBook.com
“cout”不是“std”的成员,意味着您在编译命令中未能包含“#include <iostream>”。当应用程序本身没有包含“<iostream>”时,链接器错误会发生在我身上,这一点我不确定如何从GDB内部修复。有关详细信息,请参见完整的工作bash/gdb会话此处 - Ruslan

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