逆向学习-画堆栈图
0x00:写在前面
对很多初学者来说,入门逆向很困难,很多坐高地需要去攀登,那么堆栈对于很多人来说就是一座高地,如何去征服堆栈?画堆栈图是一种很好的理解堆栈的方式。
0x01:介绍堆栈
堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
要点:堆:顺序随意 栈:后进先出(Last-In/First-Out)
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表
0x02:开始着手
准备:OD(吾爱破解专用OD)
画图工具 excel
程序:Helloworld.exe(链接:https://pan.baidu.com/s/130job_JfoQLEWQxKn0SMYQ 密码:71xq)
这里我们OD加载程序以后F2加断点在00401168处。
这里的00401168就是Helloworld.exe的其中一个函数入口点
当程序断在此处时,注意寄存器窗口EIP:00401168,那么此时程序的下一跳就是此位了。
观察寄存器窗口的ESP,EBP
那么初始的堆栈图为下图
一步一步F8往下执行
首先看00401168位置处的push 0x2 (压栈 0x2(16进制))
因为两个push是一个类型的汇编指令。放一起写~
往堆栈里压入0x2和0x1(注意均是16进制),那么入了两次栈 ESP(栈顶-8)=0012FF2C,EBP(栈底无变化),2和1被压入堆栈待使用。
接下来到call HelloWor.0040100A这个函数了
那么直接F7进入call中
此时堆栈变化 ESP=0012ff28
可以看到是到了jmp指令处,jmp(直接跳转,无条件),直接回车跟随jmp后的地址
00401040就是jmp后的地址,也是我们下一步执行的命令 push ebp
把EBP压入栈中,此时ESP-4
接着F8执行mov ebp,esp
把esp(栈顶的值赋给ebp),此时ESP和EBP相等
再顺序执行sub esp,0x40,esp-40(换算为16进制就是64,那么栈顶向下移动16个空格(堆栈由下向上是递减的)一格4个字节(32位))
F8单步过至push ebx,push esi,push edi
把ebx,esi,edi的值压入堆栈,相当于把这三个寄存器的值放到堆栈进行保存
lea edi,dword ptr ss:[ebp-0x40] :意思是把edp-0x40的地址值保存到edi中
EDI=0012FEE4
接下来到了循环处,ecx(循环寄存器)赋值0x10,eax赋值0xcccccccc,rep循环10次,且edi的地址每次循环+4,eax赋给其代表值
堆栈图为下图(VC会将未初始化的栈内存上的指针全部填成 0xcccccccc,翻译中文就是烫烫烫烫烫烫烫烫。。。
接下来执行
mov eax,dword ptr ss:[ebp+0x8] 将ebp+0x8的值赋给eax
add eax,dword ptr ss:[ebp+0xc] 将ebp+0xc的值和eax进行add相加
那么此时的eax=00000003堆栈图如下
接着执行pop edi,pop esi,pop ebx。让保存的3个寄存器值取出来恢复。
mov esp,ebp将ebp值赋给esp
pop ebp(将栈顶出栈赋给ebp)
最后函数最后retn进行返回,retn转换为汇编代码就是 pop esi
最后一步 add esp,0x8
进行对比可知,一次函数执行的前后,堆栈是最终恢复成函数执行初始的样子,对比第一步和最后一步ESP,EBP即可知。
那么经过堆栈图的制作,我们通过过程得知了这个函数功能很简单,就是传参然后实现相加并返回结果。
0x04:总结
堆栈在逆向,程序开发中都尤为重要,知道代码运行过程中堆栈的变化会让逆向清晰起来。
一个函数的调用前和return结束过程后,堆栈不会发生变化,遵循堆栈平衡
cc对应着int 3调试中断,堆栈中的存放的局部数据一般情况下是只读的,当发生意外执行堆栈里面的数据就会引发该调试中断,可以把0xccccccc理解为占位符,防止堆栈空白导致程序出错