例程并将其与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?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)