用于修改被调用函数内的 return 地址的汇编器指令

Assembler instructions to modify the return address within the called function

我对汇编程序还很陌生,我仍然不知道大部分指令。

我正在尝试向 C 程序添加一些 asm 行(只是为了好玩,玩一下)来修改函数的 return 地址。

C代码看起来像

int my_function()
{
    int my_number = 1;

    __asm__ ("nop"); 
    __asm__ ("nop"); 

    return my_number;
}

int main(int argc, char *argv[])
{
    //declarations

    ...lots of stuff

    int my_number = my_function();

    do_something;

    ...lots of stuff

    do_other_thing;

    ... lots of stuff (46 bits in assembler)

    return 0;
}

所以,我尝试做的是在 my_function 上修改堆栈中的 return 地址一次,因此,当 returning 时,它会转到 do_other_thing 而不是去 do_something

为了使它更好、更动态,我宁愿不对 return 地址进行硬编码,所以我想添加这 46 位。我也知道 return 地址在 EBP + 4 中。我已经在 x32dgb 内手动测试了它并且它有效。

我想我将不得不:

  1. 得到EBP+4指向的内容(可能在EAX中)
  2. 将 46 加到 EAX
  3. 将这个值写回 EBP + 4

到目前为止我已经理解了它,但不确定如何将其编码为 ASM 语句...

你能帮帮我吗??

I am trying to add some asm lines to a C program (just for fun, playing with it) to modify the return address of a function.

不要。

对于用 C 编写的代码;编译器拥有堆栈,编译器生成适合自身及其优化的函数启动代码,编译器生成适合自己启动代码的退出代码。任何对堆栈布局做出任何假设的内联程序集都是 "fragile" - 任何地方的微小变化(即使只是改变一个无辜的局部变量,改变编译器命令行参数,或将编译器更新到下一个版本)最终都可以将导致堆栈布局的差异,从而破坏内联汇编。实际上,它实际上比这更糟糕——编译器可以并且将生成相同 "C function" 的多个不同版本(并将一些版本内联到其他函数中以避免函数调用的成本等),这样你最终使用同一块内联程序集尝试(并且肯定会失败)处理多个完全不同的堆栈布局;即使你在其他地方成功 return 你也无法可靠地避免在你的函数 returned.

之后搞砸一切

另一个问题是,一开始几乎没有理智的理由想要这样做;即使您正在编写可以完全控制堆栈布局的汇编语言(而不是使用无法控制堆栈布局的内联汇编语言编写 C)。

本质上;对于试图学习汇编以花时间学习的人来说,有很多事情更重要(不是 "fragile" 也不是无用的)。这就像学习如何用手站立驾驶汽车(用脸踩 accelerator/brake 踏板并且看不到周围的任何东西)- 它没有用。

请注意 C

中的编码和符号

如@zx489 所述,尝试类似的操作:

__asm__(".intel_syntax prefix");
__asm__("add dword ptr [%ebp + 4], 46");

可能有用...

使用内联汇编似乎很危险,正如@Brendan 所提到的:

For code written in C; the compiler owns the stack, the compiler generates function startup code to suit itself and its optimisations, and the compiler generates exit code to suit its own startup code. Any inline assembly that makes any assumptions about the stack layout is "fragile"...

但是假设“return 地址位于当前函数堆栈帧的正上方”,也许您可​​以尝试这样的操作:

void foo(void)
{
    uintptr_t *p_return_address = __builtin_frame_address(0) + sizeof(size_t);
    *p_return_address = /* the new return address */;
}

然而,如果foo由于修改了return地址而没有return给它的调用者,那么调用者的尾声将永远不会被执行,进而导致许多其他讨厌的问题,例如堆栈溢出。所以,这还是一件很危险的事情。