转载自:http://www.cnblogs.com/ajiannet/archive/2007/04/20/721679.html

[汇编学习]献给汇编初学者-函数调用堆栈变化分析(转自黑客风云)

跟一个朋友谈堆栈的时候 就写下了这段文字,顺便发到这里给需要的看看吧
汇编初学者比较头痛的一个问题

比如 我们有这样一个C函数

 1#include<stdio.h>
 2long test(int a,int b)
 3{
 4    a = a + 1;
 5    b = b + 100;
 6    return a + b;
 7}
 8void main()
 9{  
10  printf("%d",test(1000,2000));
11}
12

写成32位汇编就是这样

 1;//
 2.386
 3.module flat,stdcall           ;这里我们用stdcall 就是函数参数 压栈的时候从最后一个开始压,和被调用函数负责清栈
 4option casemap:none            ;区分大小写
 5
 6includelib msvcrt.lib          ;这里是引入类库 相当于 #include<stdio.h>了       
 7printf  PROTO C:DWORD,:VARARG  ;这个就是声明一下我们要用的函数头,到时候 汇编程序会自动到msvcrt.lib里面找的了 
 8                                ;:VARARG 表后面的参数不确定 因为C就是这样的printf(const char *, );
 9                               ;这样的函数要注意 不是被调用函数负责清栈 因为它本身不知道有多少个参数
10                               ;而是有调用者负责清栈  下面会详细说明
11.data
12szTextFmt  BYTE '%d',0        ;这个是用来类型转换的,跟C的一样,字符用字节类型
13a          dword 1000         ;假设
14b          dword 2000         ;处理数值都用双字 没有int 跟long 的区别
15
16;/////
17.code
18
19_test proc A:DWORD,B:DWORD 
20      push ebp
21      mov  ebp,esp
22      mov  eax,dword ptr ss:[ebp+8]
23      add  eax,1
24      mov  edx,dword ptr ss:[ebp+0Ch]
25      add  edx,100
26      add  eax,edx
27      pop  ebp      
28      retn 8
29_test endp
30
31_main proc 
32      push dword ptr ds:b       ;反汇编我们看到的b就不是b了而是一个[*****]数字 dword ptr 就是我们在ds(数据段)把[*****]
33                                ;开始的一个双字长数值取出来
34      push dword ptr ds:a       ;跟她对应的还有 byte ptr ****就是取一个字节出来 比如这样 mov  al,byte ptr ds:szTextFmt 
35                                ;就把 % 取出来 而不包括 d
36      call _test                  
37      push eax                  ;假设push eax的地址是×××××
38      push offset szTextFmt
39      call printf
40      add  esp,8
41      ret             
42_main endp
43end  _main
44
45;// 下面介绍堆栈的变化
46

首先要明白的是 操作堆栈段 ss 只能用 esp或ebp寄存器 其他的寄存器eax ebx edx等都不能够用 而 esp永远指向堆栈栈顶 ebp用来在堆栈段

里面寻址
push 指令是压栈 ESP=ESP-4
pop  指令是出栈 ESP=ESP+4
我们假设main函数一开始堆栈定是 ESP=400
push dword ptr ds:b                 ;ESP-4=396 ->里面的值就是 2000 就是b的数值
push dword ptr ds:a                 ;ESP-4=392 ->里面的值就是 1000 就是a的数值
call test                           ;ESP-4=388->里面的数值是什么?这个太重要了 就是我们用来找游戏函数的原理所在。
                                                 里面的数值就是call test 指令下一条指令的地址->即push eax的地址×××××

到了test函数里面

push ebp                           ;ESP-4=384->里面保存了当前ebp的值 而不是把ebp清零
mov  ebp,esp                       ;这里ESP=384就没变化了,但是 ebp=esp=384,为什么要这样做呢 因为我们要用ebp到堆栈里面找参数
mov  eax,dword ptr ss:[ebp+8]      ;反汇编是这样的 想想为什么a就是[ebp+8]呢
                                   ;我们往上看看堆栈里地址392处就保存着a的值 这里ebp=384 加上8正好就是392了
                                   ;这样就把传递过来的1000拿了出来eax=1000
add  eax,1                         ;相当于 a+1了 eax=1001
mov  edx,dword ptr ss:[ebp+0Ch]    ; 0Ch=12 一样道理这里指向堆栈的地址是384+12=396 就是2000了 edx=2000
add  edx,100                       ;相当于 b+100 edx=2100
add  eax,edx                       ;eax=eax+edx=1001+2100=3101 这里eax已经保存了最终的结果了 
                                   ;因为win32汇编一般用eax返回结果 所以如果最终结果不是在eax里面的话 还要把它放到eax
                                   ;比如假设我的结果保存在变量nRet里面 最后还是要这样 mov eax,dword ptr nRet
pop  ebp                           ;ESP=384+4=388 而保存在栈顶384的值 保存到 ebp中 即恢复ebp原来的值                       
                                   ;因为一开始我们就把ebp的值压栈了,mov ebp,esp已经改变了ebp的值,这里恢复就是保证了堆栈平衡
retn  8                            ;ESP+8->396 这里retn是由系统调用的 我们不用管 系统会自动把EIP指针指向 原来的call的下一条指令
                                   ;由于是系统自动恢复了call那里的压栈所以 真正返回到的时候ESP+4就是恢复了call压栈的堆栈
                                   ;到了这个时候 ESP=400 就是函数调用开始的堆栈,就是说函数调用前跟函数调用后的堆栈是一样的
                                   ;这就是堆栈平衡 
由于我们用stdcall上面retn 8就是被调用者负责恢复堆栈的意思了,函数test是被调用者,所以负责把堆栈加8,call 那里是系统自动恢复的

push eax                ;ESP-4=396->里面保存了eax的值3101
                        ;上面已经看到了eax保存着返回值,我们要把它传给printf也是通过堆栈传递       
push offset szTextFmt   ;ESP-4=392->里面保存了szTextFmt的地址 也就是C里面的指针 实际上没有什么把字符串传递的,我们传的都是地址
                        ;无论是在汇编或C 所以在汇编里没有什么字符串类型 用最多的就是DWORD。嘿嘿游戏里面传递参数 简单多了
call printf             ;ESP-4=388->里面保存了下一条指令的地址
add  esp,8              ;ESP+8=400 恢复了调用printf前的堆栈状态
                        ;上面说了由于printf后面参数是:VARARG 这样的类型是有调用者恢复堆栈的 所以printf里面没有retn 8之类的指令
                        ;这是由调用者负责清栈 main是调用者 所以下面一句就是 add esp,8 把堆栈恢复到调用printf之前
                        ;而call printf那里的压栈 是由系统做的 恢复的工作也是系统完成 我们不用理 只是知道里面保存是返回地址就够

;了
ret                     ;main 函数返回 其他的事情是系统自动搞定 我们不用理 任务完成

函数调用中的堆栈平衡相关推荐

  1. 从汇编角度理解 ebpesp 寄存器、函数调用过程、函数参数传递以及堆栈平衡

    关于函数参数的传递及堆栈指针的变化,一直缺乏系统的认识和了解,各种博客也只是片面的讲解某个局部知识点,并没有全局的把握和对栈的深刻理解.本文试图从汇编以及整体上,讲解函数调用时,堆栈的变化,以及到底是 ...

  2. 从函数调用过程中的堆栈变化理解缓冲区溢出

    一.说明 本来是想直接写一个缓冲区溢出的例子,但是一是当前编译器和操作系统有溢出的保护措施没有完全弄清怎么取消,二是strcpy等遇到00会截断需要进行编码这比较难搞,所以最终没有实现. 但已经双看了 ...

  3. 堆栈平衡:估计这是最详细的讲解堆栈平衡的了 vc++6.0

    转自:https://blog.csdn.net/lixiangminghate/article/details/43195717 #include <stdio.h> #include ...

  4. 汇编达人视频学习6(汇编眼中的函数、CALL指令执行函数、堆栈传参、堆栈平衡、外平栈、内平栈)

    title: 汇编达人视频学习6 date: 2021年8月4日 15点15分 tags: 汇编达人 categories: 汇编达人 21.汇编眼中的函数 1.什么是函数 函数就是一系列指令的集合, ...

  5. 在地址随机化的操作系统环境下的堆栈平衡

    最近学习汇编以及各种和汇编有关系的课,在看汇编代码的时候发现各种对esp啊ebp的操作,最后的目的很多是为了堆栈平衡,因为如果函数调用之前和之后的堆栈不一致,就可能导致找不到数据或者找错数据.然后就想 ...

  6. 再次理解STM32中的堆栈机制

    再次理解STM32中的堆栈机制 刚拿到STM32时,你只编写一个死循环 void main() { while(1); }BUILD://Program Size: Code=340 RO-data= ...

  7. 【汇编语言与计算机系统结构笔记15】子程序设计:调用与返回,保护与恢复寄存器,子程序的参数传递,堆栈平衡,结构伪操作 STRUC

    本次笔记内容: 20.子程序设计-1-1 21.子程序设计-1-2 22.子程序设计-1-3 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录老师上课强调的 ...

  8. go中的堆栈与C++中的堆栈

    各自努力,最高处见!加油! go中的堆栈与C++中的堆栈 一.C++中的堆栈 二.go程序中的堆栈 一.C++中的堆栈 C/C++中的堆栈是操作系统级别的概念,它通过编译器所在的环境来决定. 栈:指的 ...

  9. 汪劲:生命系统中的非平衡物理学

    来源:集智俱乐部 作者:汪劲 编译:袁郭玲 编辑:邓一雪 导语 非平衡物理学将在生物应用中发挥重要作用,如酶动力学.新陈代谢.基因调控,细胞的结构.功能和动力学,生理学.癌症.分化和发育,免疫.衰老和 ...

  10. 在函数调用中,星号运算符是什么意思? [重复]

    本文翻译自:What does the star operator mean, in a function call? [duplicate] This question already has an ...

最新文章

  1. 【安全漏洞】CVE-2021-32682 elFinder RCE 简单分析
  2. Tablecontrol动态序号实现
  3. 网络爬虫与HTTP协议
  4. 电烤箱计算机控制系统,台式电烤箱及其控制系统 Desktop oven and control system
  5. 广东大学 计算机 排名2015,2015美国大学计算机排名
  6. 旭荣管理软件怎么修改小票内容_美萍超市管理软件——让管理回归轻松
  7. 【新技术】 移动支付过程中的NFC技术
  8. Head First Java 中文高清版 免费分享
  9. 全球及中国工业废物处置服务行业行业发展趋势及十四五规划展望报告2022-2028年
  10. 互联网项目经理的职业规划
  11. 利用python下载视频
  12. 电动汽车动力电池SOC估算模型电池参数辨识模型【10例】
  13. Java讯序_java使用顺序表制作电子通讯录,实现添加,删除和排序的功能.
  14. 动态规划法——常见题型及算法思路
  15. 分享 | NB-IoT智能井盖传感器
  16. 跨时钟域信号处理(二)——异步fifo的Verilog实现(附同步fifo的实现)
  17. JLINK的SWD接口调试器制作
  18. JPEG2000图像压缩算法学习
  19. 强大、优秀的文件管理软件评测:图片管理、书籍管理、文献管理
  20. linux应用程序固件升级,一种多设备同步烧录Linux固件及应用程序的方法与流程...

热门文章

  1. 大数据与云计算课后习题
  2. 【大电流H桥电机驱动电路的设计与解析(包括自举电路的讲解,以IR2104+LR7843为例)】
  3. linux下软件安装的四种方法
  4. Python拟合SHARP红外测距传感器相关数据
  5. python 拼音搜索中文_JS实现输入拼音搜索中文列表
  6. 第十届泰迪杯数据挖掘大赛B题电力系统负荷预测
  7. IBM X3650M4服务器拆机风扇 69Y5611 94Y6620 GFC0812DS 线序
  8. 郝斌PHP视频教程,郝斌数据结构自学视频_IT教程网
  9. 杭电oj —— 2052
  10. 哇嘎显示等待无服务器,vagaa 哇嘎搜索不了资源怎么破?vagaa 哇嘎无法搜索的原因分析和解决方法介绍...