第二十一课_堆栈图1

前言

难得端午放假,今天起来写写文章吧。今天画堆栈图,这堆栈图可能会写好几篇文章,这个很重要,这可以把之前的进制、汇编来个简单的复习,这堆栈图画好了,也为后面讲C语言、C++语言的基础。会画堆栈图,后面的一些概念就容易讲解了,不然用概念给大家讲解概念,浪费时间不说,还不能理解本质的东西。

今天画个简单的:就画一个1 + 2的函数的堆栈的变化过程。该函数是用C写的,源码和exe可执行文件都放网盘了,地址在文末,需要的可以下载。(编译器用的是vc6.0exeDebug版的)

说明:利用OD进行测试,入口函数的位置可以用本id的,当然你也可以自己找,有点基础的,也很容易找到main函数的那个Call

具体步骤

1、把从本id网盘中下载的exe拖到OD中打开,Ctrl + G快捷键,在弹出的对话框中输入“0x401068”,然后点OK

2、在定位到的行设置断点,(按F2,或者双击第二列就可以设置)。

3、设置好断点后,按下图中蓝色这个按钮,让程序运行到刚设置的断点处停下来。

4、程序运行到“0x401068”时,当前OD中的显示。

我们就可以一边运行,一边画堆栈图,之前说过,栈是一个连续的内存块。根据当前ESP,EBP寄存器中的值,我们可以画出当前的栈顶、栈底。

5、下一条指令是,PUSH 2,这是一条入栈的指令,我们指令2会被压入栈顶,ESP的值也会减4,那么可以画出如下的堆栈图:

按F8,执行PUSH 2指令。OD上的显示跟我们预想的结果一样。

6、下一条指令是PUSH 1,跟上一条指令一样,1被压入栈顶,DSP的值减4.

按F8运行的结果如我们所想。

7、下一条指令是:CALL 00401005,之前说过,call指令会把Call的下一条指令的地址压入栈顶,当前call的地址是0x40106C,因为第二列中有5个字节,我们可以算出call指令的下一行地址是0x401071,所以指令该条指令后,0x401071会被压入栈顶,EIP寄存器中的值被改为0x401005

在OD中这次就不能按F8了,要按F7

运行后下一条指令是JMP指令,只是跳转,不会修改栈中的内容,直接按F8运行,或者按回车键。

8、下一条指令是PUSH EBP,入栈操作,EBP寄存器中的值被压入栈顶,ESP4

按F8运行,结果和预想的一样。

9、下一条指令是MOV EBP,ESP,该指令只是修改栈底EBP的值。

按F8运行结果如下。

10、下一条指令是SUB ESP,40,这是一个减法指令,修改的是ESP,也就是栈顶,栈顶被提升了。注意这里的40是十六进制的,也就是十进制的64,因为地址是4个字节的,所以64除以4,得到16,也就是向上画16个格。

按F8运行后的结果如下图,跟我们画的一样。

11、下一条指令是PUSH EBX,把寄存器EBX中的值压入栈顶,ESP4.

按F8运行,跟我们画的一样。

12、下一条指令是PUSH ESI,把ESI中的值压入栈,ESP再减4.

按F8运行

13、下一条指令是PUSH EDI,把EDI中的值压入栈顶,ESP4.

按F8运行

14、下一条指令LEA EDI,DWORD PTR SS:[EBP-40],这是一条取地址的指令,取EBP0x40所把的内存地址,保存到EDI中。

按F8运行,可以看到只修改了EDI中的值。栈并没有发生改变。

15、下一条指令是MOV ECX,10,只是修改ECX中的值,栈没有发生改变。

16、下一条指令是MOV EAX,CCCCCCCC,只是把EAX中的值修改为CCCCCCCC,栈没有发生改变。

17、下一条指令是REP STOS DWORD PTR ES:[EDI],这里有两个指令:STOS指令是把EAX中的值写入到EDI所指的内存中,然后EDI中值根据标志寄存器DF位进行加或者减一个数,因为这里的DF0,所以STOS执行一次,EDI中的值就加4REP是重复指令指令,重复执行的次数是EAX中保存的值。执行这条指令后,前面步骤10提升的栈的16个内存都被填入CCCCCCCC

按F8运行的结果如下,可以看到确实是被填充了CCCCCCCC。其实这一步是填的是INT3,是为了防止访问越界了。

18、下一条指令是MOV EAX,DWORD PTR SS:[EBP+8],是把EBP+8所指的内存中的值(即步骤6压入栈的1)保存到EAX中。没有改变栈中的内容。

19、下一条指令:ADD EAX,DWORD PTR SS:[EBP+C],加法指令,把EBP所指内存的值(即步骤5压入栈的2)与EAX中的值相加,保存到EAX中。

20、下一条指令:POP EDI,出栈,栈顶的值复制到EDI中,栈顶加4.

按F8 运行的结果。

21、下一条指令:POP ESI,出栈,栈顶的值复制到ESI中,栈顶加4.

按F8运行的结果。

22、下一条指令:POP EBX,出栈,栈顶的值复制到EBX中,栈顶加4.

按F8运行结果。

23、下一条指令:MOV ESP,EBP,把EBP中的值复制到ESP中,即修改栈顶到现在的栈底。

按F8运行,可以看到ESPEBP中的值相等。

24、下一条指令:POP EBP,出栈,把现在栈顶的值复制到EBP中,结合步骤8,可以知道栈底的返回到调用前的状态。

按F8运行的结果如下:

25、下一条指令:RETN,RETN指令会把栈顶的值弹出到EIP中,即程序运行到调用Call指令(步骤7)的下一条指令。栈顶被修改。

按F8运行的结果如下:

26、下一条指令:ADD ESP,8,是加法指令,ESP的值加了8,这是为了平衡堆栈,即调用函数前后,堆栈的结构要一样。像这种在调用函数外面平衡堆栈的叫外平栈,如果在调用函数里面平衡堆栈的叫内平栈

按F8运行的结果如下图。

到此,一个函数的调用的堆栈图就画好了。

下面对这个过程简单说明一下,步骤5、6的两个PUSH,把两个数放到栈中,这样在call的函数中,就可以用ESP或者EBP通过加、减来获取到PUSH进栈的值。这样就实现了函数调用时参数的传递。

而那个call就是本id之前说的函数调用。

 

写了很多,感觉很多都是废话,考虑到刚接触的人可能不理解只好一句一句地讲解。事实上等我们熟悉后,我们看汇编就是一大块一大块地看了。

例如这样看:

整个过程的堆栈图:

源码和exe执行程序的网盘地址:

21课相关资料网盘链接:

https://pan.baidu.com/s/1BQ1-TFMhUH4S4YlXlMDujQ

提取码: 

el5g

 

写于2020.6.26 16:48

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值