gpt4 book ai didi

c - 为什么要在函数序言/结语中使用 ebp?

转载 作者:太空狗 更新时间:2023-10-29 17:15:06 24 4
gpt4 key购买 nike

前段时间我在尝试写汇编例程并将其与 C 程序链接,我发现我可以跳过标准的 C 调用序言结语

    push ebp
mov ebp, esp
(sub esp, 4
...
mov esp, ebp)
pop ebp

直接跳过,直接用esp地址,比如

    mov eax, [esp+4]          ;; take argument
mov [esp-4], eax ;; use some local variable storage

看起来效果不错。为什么使用这个 ebp - 也许是通过 ebp 寻址更快还是什么?

最佳答案

没有要求使用堆栈框架,但肯定有一些优点:

首先,如果每个函数都使用相同的过程,我们可以利用这些知识通过反转过程轻松确定调用序列(调用堆栈)。我们知道在 call 指令之后,ESP 指向返回地址,被调用函数要做的第一件事就是 push 当前EBP 然后将ESP 复制到EBP。因此,在任何时候我们都可以查看 EBP 指向的数据,这将是之前的 EBPEBP+4 将是最后一次函数调用的返回地址。因此,我们可以使用类似(请原谅生锈的 C++)的方式打印调用堆栈(假设为 32 位):

void LogStack(DWORD ebp)
{
DWORD prevEBP = *((DWORD*)ebp);
DWORD retAddr = *((DWORD*)(ebp+4));

if (retAddr == 0) return;

HMODULE module;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*)retAddr, &module);
char* fileName = new char[256];
fileName[255] = 0;
GetModuleFileNameA(module, fileName, 255);
printf("0x%08x: %s\n", retAddr, fileName);
delete [] fileName;
if (prevEBP != 0) LogStack(prevEBP);
}

然后这将打印出整个调用序列(好吧,它们的返回地址)直到那一点。

此外,由于 EBP 不会更改,除非您明确更新它(与 ESP 不同,它会在您 push/ 时更改pop),相对于 EBP,而不是相对于 ESP,通常更容易引用堆栈上的数据,因为对于后者,您必须注意可能在函数开始和引用之间调用的任何 push/pop 指令。

正如其他人所提到的,您应该避免使用下方 ESP 的堆栈地址,因为您对其他函数所做的任何调用 都可能会被覆盖这些地址的数据。您应该改为在堆栈上保留空间供您的函数按常规使用:

sub esp, [number of bytes to reserve]

在此之后,初始 ESPESP - [保留字节数] 之间的堆栈区域可以安全使用。在退出您的函数之前,您必须使用匹配释放保留的堆栈空间:

add esp, [number of bytes reserved]

关于c - 为什么要在函数序言/结语中使用 ebp?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15655553/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com