汇编程序指令,用于在调用的函数中修改返回地址

时间:2019-02-23 01:03:56

标签: assembly x86 32-bit

我是汇编语言的新手,但我仍然不了解大多数说明。

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

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上一次修改堆栈中的返回地址,因此,返回时,它到达do_other_thing而不是do_something < / p>

为了使它变得美观和动态,我不想对返回地址进行硬编码,因此我想添加这46位。我也知道寄信人地址在EBP + 4中。我已经在x32dgb中进行了手动测试,并且可以正常工作。

我想我将不得不:

  1. 获取EBP + 4(可能在EAX中)指向的内容
  2. 总和46到EAX
  3. 将此值写回EBP + 4

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

你能帮我吗?

3 个答案:

答案 0 :(得分:4)

  

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

不要。

对于用C编写的代码;编译器拥有堆栈,编译器生成适合其自身及其优化的函数启动代码,编译器生成适合其自身启动代码的退出代码。任何对堆栈布局进行任何假设的内联程序集都是“易碎的”-任何地方的微小更改(甚至只是更改无害的局部变量,更改编译器命令行arg或将编译器更新到下一个版本)都可能并最终将导致堆栈布局上的差异会破坏内联汇编。实际上,实际情况远比这糟得多-编译器可以并且将生成同一“ C函数”的多个不同版本(并将某些版本内联到其他函数中,以避免函数调用等开销),因此最终试图(并保证会失败)处理多个完全不同的堆栈布局的同一行内联汇编;即使您确实成功返回了其他地方,也无法可靠地避免函数返回后搞砸了一切。

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

本质上;对于试图学习汇编的人来说,花大量时间对他们花费时间学习更为重要(这不是“脆弱”的,也不是没有用的)。这就像学习如何双手站立时驾驶汽车(使用加速器/制动踏板上的脸,而看不见周围的东西)一样。

答案 1 :(得分:1)

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

<块引用>

对于用 C 编写的代码;编译器拥有堆栈,编译器生成适合自身及其优化的函数启动代码,并且编译器生成适合其自身启动代码的退出代码。任何对堆栈布局做出任何假设的内联程序集都是“脆弱的”...

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

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

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

答案 2 :(得分:-1)

请注意C中的编纂和符号

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

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

可能有效...