1

我对汇编程序有点陌生,但我正在尝试esp使用嵌入式汇编程序代码从堆栈中的 C++ 方法中查找参数。到目前为止,我什至无法将esp指针复制到,ebp因此我可以保留堆栈(以防它发生变化)。即使是这段小代码也让我失败了:

#include <stdlib.h>

int main(int argc, char* argv[])
{
    __asm
    {
        mov ebp, esp
    }
    system("pause");
    return 0;
}

运行此程序后,我得到:

运行时检查失败 #0 - ESP 的值未在函数调用中正确保存。这通常是调用使用一种调用约定声明的函数和使用另一种调用约定声明的函数指针的结果。

不知道该怎么办。请帮助我,并在此先感谢。

4

1 回答 1

2

通常,您应该在将 的值移入EBP之前将 的当前值压ESP入堆栈EBPEBP是 32 位平台上的“被调用者保存”寄存器,这意味着如果要在函数中修改它,则必须先保存它。

如果您希望您的函数返回堆栈指向的位置(或函数调用后它将返回的位置)的值,那么最好的做法是:

void* get_stack_addr()
{
        void* stack_ptr = NULL;

        //on 32-bit systems
        //EBP is pointing at top of stack frame
        //EBP + 4 is the return instruction address
        //EBP + 8 is the value that was in ESP before function call for a function with no arguments
        __asm__
        (
                "movl %%ebp, %0\n\t"
                "addl $8, %0\n\t"
                : "=r" (stack_ptr)
        );

        return stack_ptr;
}

这种方式EAX现在保存在调用之前堆栈指向的值的地址get_stack_addr()。如果你只是ESP在函数中返回值,你实际上不知道你指向哪里,因为编译器经常在 C/C++ 函数中填充堆栈以保持正确的堆栈对齐。它还经常在堆栈上为所有局部变量保留空间,这又会导致堆栈的计算失败。通过使用EBP指向栈帧顶部的 ,可以在 32 位平台上准确计算函数调用前栈的值。最后,我们将返回值放入,EAX因为在 C/C++ 的大多数 OS 应用程序二进制接口上,EAX保存函数的返回值,而不是EBP.

还有一件事......如果您想要调用实际函数的堆栈上的参数开始get_stack_addr(),然后更改movl %%ebp, %0\n\tmovl (%%ebp), %0)\n\t. 这样,您现在就获得了先前的堆栈帧基指针(即,调用者的堆栈帧基指针),并且通过向该地址添加 +8 的值,您将获得存储参数的开头在返回地址上方,或者您正在查看指向当前函数调用者的堆栈帧的地址(即前一个堆栈帧)。


作为增强功能"leal 8(%%ebp), %0\n\t"可以替换:

"movl %%ebp, %0\n\t"
"addl $8, %0\n\t"

leal指令将EBP的值加 8并将结果存储在输出操作数中。

于 2011-05-05T19:03:58.267 回答