来自: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. NO10——各种欧几里得

    int gcd(int n,int m)//n>m { //最大公约数 int r; while(m) { r = n%m; n = m; m = r; } return n; } int kg ...

  2. Week2 Teamework from Z.XML 软件分析与用户需求调查(三)必应助手体验评测

    评测人:毛宇 肖俊鹏 说明:言辞激烈,请勿介意 我花了2天的时间来试用这个软件<必应缤纷桌面手机助手>,有了很多体会,这里,我来谈一下这款软件在体验部分的表现情况. 体验部分主要分为三个部 ...

  3. Javascript Array和String的互转换

    Array类可以如下定义: var aValues = new Array(); 如果预先知道数组的长度,可以用参数传递长度 var aValues = new Array(20); -------- ...

  4. 算法(4) Rotate Image

    题目:把一个N×N的矩阵旋转90° 思路:这个题目折腾了好长时间,确切地说是两个小时!这道题也反映出自己的逻辑比较混乱 这道题我到底卡在了哪里?自己已经在本子上画出了一个转移的关系 a[0][0] - ...

  5. javaScript this 之谜

    作为接触js没多久的人对变量作用域和this所指表示非常迷惑,专门花了一个下午google了一下,感觉以前百度到的结果都是什么鬼... 下面是我对this的认识,学疏才浅请拍砖 每一个方法都有自己的上 ...

  6. BZOJ 1030 文本生成器 | 在AC自动机上跑DP

    题目: http://www.lydsy.com/JudgeOnline/problem.php?id=1030 题解: 鸽 #include<cstdio> #include<al ...

  7. BZOJ1407 [Noi2002]Savage 【扩展欧几里得】

    题目链接 BZOJ1407 题解 枚举\(m\)用扩欧判即可 #include<algorithm> #include<iostream> #include<cstrin ...

  8. Android中代码设置RadioButton的高端技巧

    不知道怎么起标题,就这样了. 目前主要讲两个方面内容: 代码方式 设置RadioButton的 android:button . android:background 等属性为 @null : 代码方 ...

  9. Any gotchas at all with converting from MyISAM to InnoDB?

    Q: I'm ready to move from MyISAM to InnoDB but wanted to know if there was a full list of things to ...

  10. (转)用python获取页面返回的cookie

    网址如下: crifan:http://www.crifan.com/get_cookie_from_web_response_in_python/ . . . .