Gointerface接口声明实现及作用详解
269
2022-10-06
逆向脱壳破解分析基础学习笔记七 堆栈图(重点)(逆向分析入门)
本文为本人 大神论坛 逆向破解脱壳学习笔记之一,为本人对以往所学的回顾和总结,可能会有谬误之处,欢迎大家指出。
陆续将不断有笔记放出,希望能对想要入门的萌新有所帮助,一起进步
堆栈图
首先给定一段反汇编代码,分析该段代码的堆栈的变化情况,并绘制出堆栈图
函数调用
00401168 |. 6A 02 push 0x2 0040116A |. 6A 01 push 0x1 0040116C |. E8 99FEFFFF call HelloWor.0040100A 00401171 |. 83C4 08 add esp,0x8
CALL内部
00401040 /> \55 push ebp 00401041 |. 8BEC mov ebp,esp 00401043 |. 83EC 40 sub esp,0x40 00401046 |. 53 push ebx 00401047 |. 56 push esi 00401048 |. 57 push edi 00401049 |. 8D7D C0 lea edi,dword ptr ss:[ebp-0x40] 0040104C |. B9 10000000 mov ecx,0x10 00401051 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 00401056 |. F3:AB rep stos dword ptr es:[edi] 00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8] 0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC] 0040105E |. 5F pop edi ; HelloWor.00401171 0040105F |. 5E pop esi ; HelloWor.00401171 00401060 |. 5B pop ebx ; HelloWor.00401171 00401061 |. 8BE5 mov esp,ebp 00401063 |. 5D pop ebp ; HelloWor.00401171 00401064 \. C3 retn
开始分析
分析流程较为冗长,可能会有些乏味,可以先看最后的流程总结,再来看分析的细节
我们现在开始逐语句分析堆栈的变化情况:
执行前
寄存器状态
堆栈内容
初始堆栈图
我们观察堆栈的情况:
此时ESP:0012FF34 EBP:0012FF80
结合寄存器和堆栈内容绘出简易堆栈图
执行中
压入参数
00401168 |. 6A 02 push 0x2
可以看到执行后ESP减少了4=0012FF30 并且0012FF30里的内容为2,这就是所谓的入栈操作
0040116A |. 6A 01 push 0x1
可以看到执行后ESP又减少了4=0012FF2C ,并且0012FF2C里的内容为1
上面的两条push语句是将两个立即数 2和1压入到堆栈中,我们可以画出对应的堆栈图:
CALL指令
0040116C |. E8 99FEFFFF call HelloWor.0040100A
F7单步步入
可以看到CALL之后跳转到了0040100A,并且esp又减少了4=0012FF28
而且我们可以注意到此时堆栈中0012FF28存放的内容是:00401171正好是我们call指令的下一行指令的地址
0040116C |. E8 99FEFFFF call HelloWor.0040100A 00401171 |. 83C4 08 add esp,0x8
所以应证了前面所学的call指令会将要返回的地址压入栈中来保存现场
此时的堆栈图为
接着我们就跳转到了call的内部
CALL内部指令
初始化堆栈
00401040 /> \55 push ebp
ESP变化
堆栈内容
EBP被压入到堆栈中,此时堆栈图为
接着执行
00401041 |. 8BEC mov ebp,esp
ebp赋值为esp,此时堆栈图为
接着执行
00401043 |. 83EC 40 sub esp,0x40
将esp的值减去0x40=64,我们这里的相差的数据宽度为4即16,64/4=16,因此堆栈图里多了16格(蓝色部分),这种操作常被叫做提升堆栈,此时堆栈图为:
我们可以发现提升完堆栈以后,堆栈的数据有些意义不明,这是因为堆栈中存放的是临时的数据,可能是之前使用时没有清理的垃圾数据
接着执行
00401046 |. 53 push ebx 00401047 |. 56 push esi 00401048 |. 57 push edi
将三个通用寄存器压入堆栈,用于保护现场,注意CALL之前和CALL之后,其前后环境要一致,这就是所谓的堆栈平衡
堆栈内容
根据此时的堆栈内容绘制堆栈图
接着执行
00401049 |. 8D7D C0 lea edi,dword ptr ss:[ebp-0x40]
将ebp-40所指向的内存地址赋给edi
前面我们执行了sub esp,0x40 所以这里其实就是将那时esp的地址传给了edi(就是push ebx esi edi)之前的的esp
此时堆栈图并发生没有变化
接着看下一行
0040104C |. B9 10000000 mov ecx,0x10 00401051 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
分别给ecx和eax赋值,堆栈图依旧没有发生变化
接着看下一行
00401056 |. F3:AB rep stos dword ptr es:[edi]
这条语句用到了我们前面所学的逆向基础笔记五 标志寄存器中的内容(如有疑惑可前往查看)
rep的作用是,重复执行 stos dword ptr es:[edi],每次执行都会使ecx-1,直到ecx为0再执行下一条语句
前面赋值ecx为0x10=16,正好对应我们堆栈图中蓝色的格子数,所以将会执行16次
stos dword ptr es:[edi]则是将eax的值赋值给edi所指向的内存地址里的值,并且每执行一次edi都会增加4(D标志位为0所以是增加)
结合前面edi==esp,这里其实是将我们提升堆栈的那部分内存区域初始化
此时的堆栈内容为
很明显地看到原本的垃圾数据被我们初始化为了CCCCCCCC
堆栈图也变成了
实际执行内容
接着看下面的代码
00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8] 0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
根据堆栈图我们可以很清晰地看出
[ebp+0x8]正是我们call外部push的参数:1
[ebp+0xc]正是我们call外部push的参数:2
这里是将eax赋值为1,然后再给eax+2,最终结果eax=3
还原现场并返回
此时堆栈图依旧没有发生变化,接着看下面的语句
0040105E |. 5F pop edi ; HelloWor.00401171 0040105F |. 5E pop esi ; HelloWor.00401171 00401060 |. 5B pop ebx ; HelloWor.00401171
出栈,还原现场,堆栈图
下一条
00401061 |. 8BE5 mov esp,ebp
还原esp,前面mov ebp,esp对应也要还原
此时堆栈图为:
继续看下一条指令
00401063 |. 5D pop ebp ; HelloWor.00401171
将ebp出栈,恢复现场,此时的堆栈图为
最后一句
00401064 \. C3 retn
此时栈顶为
返回,相当于于pop eip
执行后
执行后的堆栈图为
执行返回后
此时返回到了
也就是之前call的下一句指令
此时的堆栈图
我们可以发现此时的ESP和EBP又变回到了原本执行前的状态,(寄存器也一样),这就是所谓的堆栈平衡
总结
通过上面的分析,我们可以得出这段代码所处理的大致流程
可分为三个部分:压入参数、调用CALL、CALL返回后
压入参数
压入参数部分十分简单,就是将调用CALL所需的参数压入堆栈,方便CALL内部执行时调用
这里对应的语句为
00401168 |. 6A 02 push 0x2 0040116A |. 6A 01 push 0x1
即这个CALL得到的参数为2和1
调用CALL
调用CALL又可以分为六个部分:
提升堆栈 保护现场 初始化提升的堆栈 执行实际内容 恢复现场 返回
提升堆栈
对应语句为
00401040 /> \55 push ebp 00401041 |. 8BEC mov ebp,esp 00401043 |. 83EC 40 sub esp,0x40
将堆栈提升了0x40
保护现场
对应语句为
00401046 |. 53 push ebx 00401047 |. 56 push esi 00401048 |. 57 push edi
这里将我们提升的堆栈中的内容全部初始化为CCCCCCCC
为什么是初始化为CC?防止缓冲溢出
CC的硬编码对应的指令为int 3,即断点
这么做有什么好处呢?当程序执行超过缓冲区时,遇到int 3就会自动停下来
执行实际的内容
对应语句为
00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8] 0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
就是将前面压入的参数2和1进行相加得到3
恢复现场
对应语句为
0040105E |. 5F pop edi ; HelloWor.00401171 0040105F |. 5E pop esi ; HelloWor.00401171 00401060 |. 5B pop ebx ; HelloWor.00401171 00401061 |. 8BE5 mov esp,ebp 00401063 |. 5D pop ebp ; HelloWor.00401171
与前面保护现场相对应
返回
对应语句为
00401064 \. C3 retn
CALL返回后
对应语句为
00401171 |. 83C4 08 add esp,0x8
用为平衡堆栈
逆推C语言代码
根据我们前面的分析,我们不难发现这其实就是个简单的加法函数
int add(int x,int y){ x=x+y; //这里的x和y分别对应压入的参数 return x; //对应RETN 默认采用eax作为返回值的传递载体 }
事后感言
一个小小的加法函数其对应的汇编代码却不少,而其中的关键代码只有两句
00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8] 0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
其它的大部分代码主要都是为保护现场和恢复现场所服务
编译器编译出的Debug和Release版本对应的汇编代码会有所差异,但只要掌握了核心思想,万变不离其宗
本笔记可能会有谬误之处,欢迎大家指出,一起探讨,共同提升
本系列逆向脱壳破解基础学习都在下方链接中,欢迎下载并交流沟通
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~