为什么在功能序言尾声中使用ebp?

为什么在功能序言尾声中使用ebp?,第1张

概述前段时间我正在尝试编写程序集 例程并将其与C程序链接,我发现了 我可以跳过标准的C-call序言尾声 push ebp mov ebp, esp (sub esp, 4 ... mov esp, ebp) pop ebp 只是跳过这一切,只需通过esp,就像 mov eax, [esp+4] ;; take argument mov 前段时间我正在尝试编写程序集
例程并将其与C程序链接,我发现了
我可以跳过标准的C-call序言尾声
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更快地解决什么?

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

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

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,当你推/d时它会改变),通常更容易引用堆栈相对于EBP的数据,而不是相对于ESP,因为对于后者,你必须知道在函数的开始和引用之间可能已经调用过的任何push / pop指令.

正如其他人所提到的,你应该避免使用ESP下面的堆栈地址,因为你对其他函数的任何调用都可能会覆盖这些地址的数据.您应该通过以下方式在堆栈上保留空间以供您的函数使用:

sub esp,[number of bytes to reserve]

在此之后,初始ESP和ESP之间的堆栈区域 – [保留的字节数]是安全的.
在退出函数之前,必须使用匹配释放保留的堆栈空间:

add esp,[number of bytes reserved]
总结

以上是内存溢出为你收集整理的为什么在功能序言/尾声中使用ebp?全部内容,希望文章能够帮你解决为什么在功能序言/尾声中使用ebp?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1237344.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存