滴水三期:day06.1-堆栈图

一、画堆栈图(debug版)

1.准备工作

  • 先要找到函数入口地址,但是现在还没有学PE文件格式,所以现在直接告诉你函数从哪个地址开始,打开OD,直接使用CTRL+G定位到地址即可
  • 在当前地址设置断点F2,再按F9让程序执行到此处停下
  • 现在就可以开始根据寄存区区域,堆栈区域,以及反汇编区域,开始分析堆栈,并且推出函数的功能

2.画出堆栈图

  • 将helloworld程序用OD打开,第一个函数入口0x401168,定位到此处,并且让程序执行到此处停下

    image-20211130173904746
  • 开始根据汇编指令推测堆栈的变化情况,然后依次F8单步执行,遇到call函数F7进入函数执行,验证推测是否正确


  • 指令如下:

    image-20211130173942452image-20211130174009876image-20211130174033896

  • 画出堆栈图并分析:

    image-20211130171558690
    • push 2push 1操作一般是将函数要用到的参数赋值并压入栈中,后面函数可能要使用;
    • call函数表示调用此函数,让CPU跳转到后面跟的地址,开始执行函数,先会经过jmp指令,起一个跳板的作用,两个都会改变eip的值,让CPU跳转执行,而且最要注意的是,call函数跳转前会先将函数的返回地址压入栈中
    • push ebp是将函数调用前的源栈底储存后面,后面要恢复堆栈

  • 指令接着是:

    image-20211130174115564
  • 画出堆栈图并分析:

    image-20211130172139003
    • mov ebp,espsub esp,40是为函数开辟新的内存,即缓冲区,用来存储函数中要使用到的数据
    • push ebxpush esipush edi是为了保留现场,这些寄存器中可能存着函数调用前的一些信息,函数在执行时可能会使用到这些寄存器存储一些其他的数据,所以先要保留现场,存储到内存中
    • lea edi,dword ptr ss:[ebp-40]表示将缓冲区的开始地址用edi记录下来,方便后面填充缓冲区
    • mov ecx,0x10mov eax,0xccccccccrep stos dowrd ptr es:[edi]这三段指令就表示用cccccccc填充满整个缓冲区
    • CCCCCCCC是四个十六进制数,CC其实就是int3的硬编码,int3是断点,填充这么多CCCCCCCC有什么好处呢?防止缓冲区溢出:因为在写代码时,这块新分配出来的内存(缓冲区),就是给程序用的,当有数据要存储时,就会存到这块内存中,其他不用的地方全是CCCCCCCC,即int3,那么当程序一旦超过缓冲区时,就会自动停下来!
    • 其次,这块新分配出来的内存,上一个函数执行时可能使用过,留下垃圾数据(对本次函数没有意义的数据),那么将缓冲区填充的还有一个好处就是将缓冲区的垃圾信息覆盖,所以最好给参数赋初始值

  • 再接着指令为:

    image-20211130174222144
  • 画出堆栈并分析:

    image-20211130173640311
    • mov eax, dword ptr [ebp+8]add eax, dword ptr [ebp+C]这两条指令就是函数真正的功能,将两个函数的参数相加,将结果存到eax中返回(==如果结果大于0xffffffff怎么办呢?==不一定都将结果存储到寄存器中返回,可以存到内存中)
    • pop edipop esipop ebx用来恢复现场,将原来的寄存器中值恢复
    • retn等价于pop eip,而此时栈顶中的值就是函数的返回地址,所以此指令执行后让CPU在函数快执行完时回到这函数的下面一行指令地址

  • 函数的最后一条指令为:

    image-20211130180913270
  • 画出堆栈图并分析:

    image-20211130180938129
    • 一个函数执行前和执行后,栈结构应该是不会发生变化的,即栈顶指针和栈底指针是不会发生变化的,这个过程就叫做平衡堆栈,分为内平栈和外平栈。所以call指令下面的指令其实就是用来平衡堆栈用的,当这条指令执行完成,一个函数才真正执行完成。且此平衡指令是在函数外部,谁调用谁平衡就叫外平栈
    • 此时这个函数就算全部执行完成,可以分析出它的功能是做加法

二、作业

1.画出第二个函数的堆栈图

  • 和上述过程同理,这里已知第二个函数的起始地址为0x401174,设置断点,执行到此处时开始分析

  • 指令如下:

    image-20211130181346765 image-20211130181414892 image-20211130181445140
  • 画出堆栈并且分析功能,如下图所示:

    image-20211130181530977 image-20211130181606141 image-20211130181714221 image-20211130181809017

2.画出第三个函数的堆栈图

  • 第三个函数的起始地址为0x401182

  • 指令如下:

    image-20211130182022766 image-20211130182054904 image-20211130182123962
  • 画出堆栈图并分析:

    image-20211130182302231 image-20211130182454823 image-20211130182530961 image-20211130182549418

3.画出第四个函数的堆栈图

  • 第四个函数因为涉及到有条件的跳转,所以要分析出它要做什么,按照昨天学的JCC指令的跳转跳转看标志寄存器某几位,或者通过背的跳转数据比较大小的判断条件,来分析应该怎么跳转,跳转条件是什么,跳到哪里,最后输入什么,进而推出函数的功能

    但是此题有一个疑问,为什么指令明明是JLE,则上一条的cmp指令中如果前操作数小于等于后操作数,则跳转,那么推出来的代码应该是if前面的参数小于等于后面的参数则执行。但是最后还原出来应该是if前面的参数大于后面的参数,如果你写成if前面的参数小于等于后面的参数,汇编指令则为JG,此时结果正好相反(已解决:因为C语言翻译成汇编后,所有代码的排版应该是顺着的)

  • 该函数的指令为:

    image-20211130183505622 image-20211130183522388 image-20211130183546945
  • 画出堆栈图,并分析函数的功能

    image-20211130183623319 image-20211130183656646 image-20211130183720381 image-20211130183905642
  • 还原出来的代码如下:

    #include "stdafx.h"
    int function(int a,int b,int c) //a=9,b=12,c=5
    {
    	if(a>b){
    		if(a>c){
    			return a;
    		}
    		return c;
    	}
    	if(b>c){
    		return b;
        }
    	return c;
    }
    
    void main (int argc,char* argv[])
    {
    	function(5,12,9);  //先入后出
    } 
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值