恶意代码分析实战 加壳与脱壳 lab 18-1 18-2 18-3 18-4 18-5 手动脱壳和自动脱壳操作
脱壳存根
被加壳程序中的脱壳存根由操作系统加载,然后脱壳存根负载加载原始程序。对于加壳程序来说,可执行程序的入口点指向脱壳存根,而不是原始代码。原始程序通常存储在加壳程序的一个或多个附加节中。
脱壳存根执行了以下三步操作:
1. 将原始程序脱壳到内存中;
2. 解析原始可执行文件的所有导入函数;
3. 将可执行程序转移到原始的程序入口点(OEP)。
尾部跳转
一旦脱壳存根完成脱壳,他就必须转到OEP运行。转到OEP的指令通常被叫做尾部跳转指令(jmp、ret、call)。
书中对于脱壳过程图示如下:
![]()
ESP定律脱壳 本文手动脱壳大量用到此方法
ESP定律是比较常用的脱壳方式,作为新手用的也比较多简单写一下我的看法。
esp定律的使用过程大致为:
1.开始就点F8,注意观察OD右上角的寄存器中ESP有没突现(变成红色),并且只有sp和ip为红色。
2.Command窗口中输入dd 0012FFA4 后回车,跟随esp寄存器后的地址。
3.选中下断的地址,断点--->硬件访--->WORD断点
4.按一下F9运行程序,直接来到了跳转处,按下F8,到达程序OEP(程序入口)
花了很长时间理解了一下原理,简单谈一下,可能有错。
我们把壳理解为一个对程序进行压缩和解压的子程序,相当于调用call指令:call xxxx,call指令遵循堆栈平衡,所以esp作为堆栈指针,程序前后入栈和出栈必须相同。
可刚开始的程序:
0040D000 60 pushad //注意这里ESP=0012FFC4(入栈)
0040D001 E8 00000000 call ASPACK.0040D006 //ESP=0012FFA4
PUSHAD就是把所有寄存器压栈!我们在到壳的最后看看:
代码:
0040D558 61 popad //ESP=0012FFA4(出栈)
0040D559 75 08 jnz short ASPACK.0040D563 //注意这里ESP=0012FFC4我们在0012ffa4处设置下物理断点,壳在出栈时调用ESP=0012FFC4是截断。按下f8到达程序入口。
适用范围:
几乎全部的压缩壳,部分加密壳。只要是在JMP到OEP后,ESP=0012FFC4的壳,理论上我们都可以使用
Lab18-01.exe
首先利用peid进行查壳
可以看出这是一个被UPX加壳的文件。UPX是一个压缩壳,并不是一个加密壳。
在OD中打开Lab18-01.exe分析:
可以看出这并不是OEP,而是脱壳存根。为了进行脱壳,我们需要找到尾部跳转从而得到OEP。00409DC0处的指令为pushad,在脱壳存根中为了保持栈平衡,也一定会一个popad的命令。
PUSHAD 指令
本指令将EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI 这8个32位通用寄存器依次压入堆栈,其中SP的值是在此条件指令未执行之前的值.压入堆栈之后,ESP-32–>ESP.
POPAD 指令
本指令依次弹出堆栈中的32位字到 EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX中,弹出堆栈之后,ESP+32–>ESP.
在OD中查找popad命令,在00409F43处看到jmp指令。
在IDA中定位到00409F43分析:
结果UPX压缩后,IDA不能识别处 00409F43处的有效地址,所以猜测该处的jmp就是尾部跳转指令。
另外在od中看到:
该地址后面一堆这样0000没有意义的代码!并且40154F和409F43的地址差异太大,也是疑点!
OD中在00409F43处下断点并step 下一条指令,看到下面这种奇怪的东西:
然后可以看到二进制数据了:
然后dump:
右键OEP后使用OllyDump插件脱壳:
“重建输入表”取消勾选后点击脱壳。
保持OD不退出, 然后使用ImportREC获取导入表
点击fixdump,修复转存文件
至此已完成脱壳,脱壳后的文件就是unpack_那个!
将该文件放到IDA里分析,看起来就很正常了!
lab 18-2
lab18-02
FSG加壳,定位到ope即可
这里快点选择下面插件可以快速到达程序oep
定位到这里,选择分析,就可以
这里是是选择ollydbg插件直接脱壳,成功.
最后使用import rec修复下导入表即可,和上面1的思路一样。
本次实验我们将会分析lab18-03文件。
将lab18-3载入peview
可以看到这个壳是PECompact
载入od
默认405130为入口点
使用od的插件来查找oep
结果如下
插件猜测的oep起始位置在40a110 但是这里的这些指令不像是oep。==》为啥???
而且这里它访问的值在0040a115处的栈底指针上方,如果这个地址不是文件的入口点,那么栈底指针之上的任何数据都不会被初始化。
使用另外一个插件选项
结果如下
暂停在了ntdll中的一条指令上,这明显也不是oep ==》这个是因为上下文的汇编指令都是有联系的!
使用插件不行的话,我们查看尾部跳转是否容易定位
在上图阴影指令是一条retn指令,后面是一段0字节。==》又是靠肉眼人工脱壳了!
我们在这里下断点,执行过来
可以看到,没有命中我们下的位置,而是显示了一个异常
说明这种方法也不起作用了 ==》为啥呢?
我们查看程序默认的入口点
jmp指令会直接跳到00405138,而00405138,00405139的pushfd,pushad会影响内存。这些指令保存了所有的寄存器和标志信息,加壳程序很可能在跳转到OEP之前恢复所有的寄存器和标志,所以我们可以通过在栈上设置一个断点来尝试找到oep
在尾部跳转之前会有一个popad或popfd,将帮助我们找到oep
单步执行到call指令时
在上图右侧可以看到此时esp为0012ffa0
右键,如下操作
将地址载入到内存转储中
选中栈顶的前四个字节,如下操作
然后执行
可以看到断在了0040754f
看到了retn 4会将程序转移到另一个位置运行,这可能是尾部跳转,我们单步执行到这里,接着就来到了401577处
如下操作
强制od反汇编这些代码,结果如下
在右侧可以看到eip现在指向了00401577的位置
如下操作转储程序
点击get eip as oep
然后单击dump即可保存
再次使用peid查看新文件
可以看到脱壳成功了
这种脱壳还是比较繁琐的!!!反正下次看到
这种壳的时候就留意下。
和3一样,几乎没有区别!都是利用popad进行手动脱壳!
将lab18-4载入peid
可以看到是使用ASPack加壳的
载入od
第一条就是pushad
我们单步步过pushad
如下所示
选中esp的值follow in dump
然后下硬件断点
执行后如下所示
jnz指令的前一条是popad,我们知道跟在popad指令后面的应该是尾部跳转,尾部跳转可以将程序切换到oep运行。
我们单步步过jnz
在push指令后面看到了retn,它将跳转到压入栈的地址处运行,这可能就是尾部跳转
单步步过retn,来到了
右键,如下操作
结果如下
这样就找到了oep
将其转储
将转储后的文件载入peid
可以看到脱壳成功了。
lab 18-5
和34本质没有区别,都是利用esp popad追踪栈的数据来进行脱壳!
将lab18-5载入peid
可以看到是WinUpack加壳得到的
载入od
但是会直接报错
od能够加载文件,但是不能发现脱壳存根的入口点,而会在系统断点上中断
我们单步
直到esp的值为红色
接着,右键-》数据窗口中跟随
然后下断点
执行后停在下图所示位置
这里就是oep的位置
还是同样的转储操作
载入peid看到此时脱壳成功了
但是查看导入信息时,如下所示
无法正确识别
我们需要对导入表进行修复
使用import rec进行修复工作
上图的两个箭头所指的地方是需要修改的,一个是lab18-5的进程,一个是oep地址
单击自动搜索
点击获取输出表
然后点击修复转存文件
点击打开即可
显示saved_success说明修复完毕
将修复后的文件载入peid
可以看到导入信息等都可以查看了
说明脱壳成功
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2020-10-06 通过pwndbg看看c中局部变量是如何在stack上放置的 此外 printf %n的作用终于弄明白了
2020-10-06 Mac 环境下 PWN入门系列(一) when_did_you_born 题目非常好,适合初学者,对局部变量、stack的认识更深刻了,level0也是非常经典的“留后门”题目
2020-10-06 pip 安装过慢 使用清华源 加速
2019-10-06 python 中根据python版本(2或3)定义函数