来自:http://blog.163.com/liuguang_123/blog/static/816701920105262543890/

------------------------------------------------------------------------------------------------

理解调用栈最重要的两点是:栈的结构,EBP寄存器的作用。

首先要认识到这样两个事实:

1、一个函数调用动作可分解为:零到多个PUSH指令(用于参数入栈),一个CALL指令。CALL指令内部其实还暗含了一个将返回地址(即CALL指令下一条指令的地址)压栈的动作。

2、几乎所有本地编译器都会在每个函数体之前插入类似如下指令:PUSH EBP; MOV EBP ESP;

即,在程序执行到一个函数的真正函数体时,已经有以下数据顺序入栈:参数,返回地址,EBP。
由此得到类似如下的栈结构(参数入栈顺序跟调用方式有关,这里以C语言默认的CDECL为例):

+| (栈底方向,高位地址) |
| .................... |
| .................... |
| 参数3                |
| 参数2                |
| 参数1                |
| 返回地址             |
-| 上一层[EBP]          | <-------- [EBP]

“PUSH EBP”“MOV EBP ESP”这两条指令实在大有深意:首先将EBP入栈,然后将栈顶指针ESP赋值给EBP。“MOV EBP ESP”这条指令表面上看是用ESP把EBP原来的值覆盖了,其实不然——因为给EBP赋值之前,原EBP值已经被压栈(位于栈顶),而新的EBP又恰恰指向栈顶。

此时EBP寄存器就已经处于一个非常重要的地位,该寄存器中存储着栈中的一个地址(原EBP入栈后的栈顶),从该地址为基准,向上(栈底方向)能获取返回地址、参数值,向下(栈顶方向)能获取函数局部变量值,而该地址处又存储着上一层函数调用时的EBP值!

一般而言,ss:[ebp+4]处为返回地址,ss:[ebp+8]处为第一个参数值(最后一个入栈的参数值,此处假设其占用4字节内存),ss:[ebp-4]处为第一个局部变量,ss:[ebp]处为上一层EBP值。

由于EBP中的地址处总是“上一层函数调用时的EBP值”,而在每一层函数调用中,都能通过当时的EBP值“向上(栈底方向)能获取返回地址、参数值,向下(栈顶方向)能获取函数局部变量值”。
如此形成递归,直至到达栈底。这就是函数调用栈。

编译器对EBP的使用实在太精妙了。

从当前EBP出发,逐层向上找到所有的EBP是非常容易的:

unsigned int _ebp;
__asm _ebp, ebp;
while (not stack bottom)
{
    //...
    _ebp = *(unsigned int*)_ebp;
}

基于上面的原理,我们可以实现:Copy code

function getIntHex(var a; len: integer): string;//整型转成HEX字符串
var
  d: pchar;
  i: Integer;
begin
  getmem(d, len * 2);
  binToHex(@a, d, len);
  result := '';
  for i := len - 1 downto 0 do
    result := result + d[i * 2] + d[i * 2 + 1];
  freemem(d);
end;

function PrintCallStack(): string;
var
  curEBP, nextEBP, val1, val3: Cardinal;
  p: ^Cardinal;
begin
  asm
       mov curEBP,ebp  ;//取得当前EBP
       mov eax,dword ptr ss:[ebp];
       mov  nextEBP,eax;//上一层的EBP
  end;
  val3 := 0;
  result := '';
  repeat
    p := Pointer(curEBP + 4);
    val1 := p^; //上一层的调用函数的断点(下一语句地址)
    val3 := val3 + 1;
    result := result + '================= No.' + IntToStr(val3) + ' ============='#13#10;
    result := result + '当前EBP:' + getIntHex(curEBP, SizeOf(curEBP)) + #13#10;
    result := result + '上一EBP:' + getIntHex(nextEBP, SizeOf(nextEBP)) + #13#10;
    result := result + '上一断点:' + getIntHex(val1, SizeOf(val1)) + #13#10;
    p := Pointer(curEBP);
    curEBP := p^;
    p := Pointer(curEBP);
    nextEBP := p^;
  until (nextEBP = 0) or (DWORD(p) >= $0012FFFC) ;//到栈顶了吗?
end;
有这样的方法,查找游戏的CALL的基址就不再是极难的事了

分析函数调用堆栈的原理和Delphi实现的更多相关文章

  1. C++反汇编代码分析--函数调用

    推荐阅读: C++反汇编代码分析–函数调用 C++反汇编代码分析–循环结构 C++反汇编代码分析–偷调函数 走进内存,走进汇编指令来看C/C++指针 代码如下: #include "stdl ...

  2. 深入浏览器工作原理和JS引擎(V8引擎为例)

    浏览器工作原理和JS引擎 1.浏览器工作原理 在浏览器中输入查找内容,浏览器是怎样将页面加载出来的?以及JavaScript代码在浏览器中是如何被执行的? 大概流程可观察以下图: 首先,用户在浏览器搜 ...

  3. 嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误

    嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误 2015-05-27 14:19 184人阅读 评论(0) 收藏 举报  分类: 嵌入式(928)  一般察看函数运行时堆栈的 ...

  4. linux下利用backtrace追踪函数调用堆栈以及定位段错误

    一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...

  5. Mybatis插件原理和PageHelper结合实战分页插件(七)

    今天和大家分享下mybatis的一个分页插件PageHelper,在讲解PageHelper之前我们需要先了解下mybatis的插件原理.PageHelper 的官方网站:https://github ...

  6. 用户态使用 glibc/backtrace 追踪函数调用堆栈定位段错误【转】

    转自:https://blog.csdn.net/gatieme/article/details/84189280 版权声明:本文为博主原创文章 && 转载请著名出处 @ http:/ ...

  7. Linux下函数调用堆栈帧的详细解释【转】

    转自:http://blog.chinaunix.net/uid-30339363-id-5116170.html 原文地址:Linux下函数调用堆栈帧的详细解释 作者:cssjtuer http:/ ...

  8. Linux下利用backtrace追踪函数调用堆栈以及定位段错误[转]

    来源:Linux社区  作者:astrotycoon 一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序 ...

  9. Lab_1:练习5——实现函数调用堆栈跟踪函数

    题目:实现函数调用堆栈跟踪函数 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_stackframe来跟踪函数调用堆栈中记录的返回地址.如 ...

随机推荐

  1. B - 寻找M

    B - 寻找M Time Limit: 1000/1000MS (C++/Others) Memory Limit: 65536/65536KB (C++/Others) Problem Descri ...

  2. Solr的搭建和部署

    1.概述 简介 Solr,全称Search On Lucene Replication.一个开源的搜索服务器,对外提供类似于WebService的API接口. 用户可以通过http请求,向搜索引擎服务 ...

  3. JSON使用(4)

    把JSON文本转换为JavaScript对象 JSON最常见的用法之一,是从web服务器上读取JSON数据(作为文件或作为HttpRequest),将JSON数据转换为JavaScript对象,然后在 ...

  4. jQuery下拉列表二级联动插件

    jQuery下拉列表二级联动插件的视图代码: <!doctype html> <html lang="en"> <head> <meta ...

  5. [洛谷P2590][ZJOI2008]树的统计

    题目大意:一棵树,支持三个操作, $CHANGE\;u\;t:$ 把结点$u$的权值改为$t$ $QMAX\;u\;v:$ 询问从点$u$到点$v$的路径上的节点的最大权值 $QSUM\;u\;v:$ ...

  6. AtCoder Grand Contest 025 Problem D - Choosing Points

    题目大意:输入$n,d1,d2$,你要找到$n^2$个整点 x, y 满足$0 \leqslant x, y<2n$.并且找到的任意两个点距离,既不是$\sqrt{d1}$,也不是 $\sqrt ...

  7. Visual Studio调试之符号文件

    原文链接地址:http://www.cnblogs.com/killmyday/archive/2009/10/14/1582882.html 前面在不能设置断点的检查步骤和Visual Studio ...

  8. 关于label标签的作用

    label标签的定义和用法: <label> 标签为 input 元素定义标注(标记). label 元素不会向用户呈现任何特殊效果.不过,它为鼠标用户改进了可用性.如果您在 label ...

  9. CSS样式权重的级联cascade的概念

    我们知道,firefox在众多浏览器中是对css 2高度兼容的一款浏览器,那是我能够编写一个中型b2b网站的时候(并不能说是我遇到过的难题)在禅意花园中看到的一个案例,说的是某个菜单在css中定义了以 ...

  10. idea初学建立maven项目报错

    原理,是因为你没把新创建好的maven项目给设置成一个可被tomcat部署的web项目 参考此博文,讲的非常详细: 归根到底是因为web项目的部署问题: 解决方案:在创建的到时候,idea下部会提示是 ...