这里整合了一下之前自己学习软件手工脱壳的一些笔记和脱文,希望能给新学软件逆向和脱壳的童鞋们一点帮助。
1 一些概念
1.1 加壳
加壳的全称应该是可执行程序资源压缩,是保护文件的常用手段。加壳过的程序可以直接运行,但是不能查看源代码。要经过脱壳才可以查看源代码。
加壳是利用特殊的算法,对EXE
、DLL
文件里的资源进行压缩、加密。类似WINZIP
的效果,只不过这个压缩之后的文件,可以独立运行,解压过程完全隐蔽,都在内存中完成。它们附加在原程序上通过Windows
加载器载入内存后,先于原始程序执行,得到控制权,执行过程中对原始程序进行解密、还原,还原完成后再把控制权交还给原始程序,执行原来的代码部分。加上外壳后,原始程序代码在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原,这样就可以比较有效地防止破解者对程序文件的非法修改,同时也可以防止程序被静态反编译。
壳的类型通常分为压缩壳和加密壳两类。压缩壳的特点是减小软件体积大小,加密保护不是重点。加密壳种类比较多,不同的壳侧重点不同,一些壳单纯保护程序,另一些壳提供额外的功能,如提供注册机制、使用次数、时间限制等。
1.2 OEP
OEP:(Original Entry Point
),程序的入口点。软件加壳一般隐藏了程序真实的OEP
(或者用了假的OEP
), 我们需要寻找程序真正的OEP
,才可以完成脱壳。
一般加壳程序在使用Ollydbg
等动态调试工具时,会停在壳的预处理块。即处在对于程序原始代码块的解压或解密操作之前,在运行完程序自脱壳模块后,会停留在程序加壳之前的OEP
位置,此时是dump
程序的最佳时期。脱壳时在真实OEP
处下int3
断点,就可以捕捉到程序代码段完全恢复的状态。因此,寻找加壳程序的正确OEP,也成了手动脱壳时的第一要务。
1.3 IAT
IAT:(Import Address Table
),导入地址表。由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL
中。当PE
文件被装入内存的时候,Windows
装载器才将DLL
装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成。其中导入地址表就指示函数实际地址。 多数加壳软件在运行时会重建导入地址表,因此获取加壳程序正确的导入地址表也是手动脱壳操作中的一个关键问题。
2 一些脱壳方法
2.1单步跟踪法
单步跟踪法的原理就是通过Ollydbg
的单步(F8
)、单步进入(F7
)和运行到(F4
)功能,完整走过程序的自脱壳过程,跳过一些循环恢复代码的片段,并用单步进入确保程序不会略过OEP
。这样可以在软件自动脱壳模块运行完毕后,到达OEP
,并dump
程序。
2.2 ESP定律法
ESP
定律法是脱壳的利器,是应用频率最高的脱壳方法之一。
ESP
定律的原理在于程序中堆栈平衡的合理利用。由于在程序自解密或者自解压过程中,不少壳会先将当前寄存器内容压栈,如使用pushad
,在解压结束后,会将之前的寄存器值出栈,如使用popad
。因此在寄存器出栈时,往往程序代码被自动恢复,此时硬件断点触发。然后在程序当前位置,只需要少许单步跟踪,就很容易到达正确的OEP
位置。
2.3内存镜像法(二次断点法)
内存镜像法是在加壳程序被加载时,通过OD
的ALT+M
快捷键,进入到程序虚拟内存区段。然后通过加两次内存一次性断点,到达程序正确OEP
的位置。
内存镜像法的原理在于对于程序资源段和代码段下断点,一般程序自解压或者自解密时,会首先访问资源段获取所需资源,然后在自动脱壳完成后,转回程序代码段。这时候下内存一次性断点,程序就会停在OEP
处。
2.4一步到达OEP
所谓的一步到达OEP
的脱壳方法,是根据所脱壳的特征,寻找其距离OEP
最近的一处汇编指令,然后下int3
断点,在程序走到OEP
的时候dump
程序。 如一些压缩壳往往popad
指令距离OEP
或者Magic Jump
特别近,因此使用Ollydbg
的搜索功能,可以搜索壳的特征汇编代码,达到一步断点到达OEP
的效果。
2.5最后一次异常法
最后一次异常法的原理是,程序在自解压或自解密过程中,可能会触发无数次的异常。如果能定位到最后一次程序异常的位置,可能就会很接近自动脱壳完成位置。现在最后一次异常法脱壳可以利用Ollydbg
的异常计数器插件,先记录异常数目,然后重新载入,自动停在最后一次异常处。
2.6 模拟跟踪法
模拟跟踪法的原理就是使用Ollydbg
下条件断点,SFX
相当于是一个自解压段,在自解压段结束时(eip
的值转到代码段时),已经距离OEP
很近,但是这种跟踪方法会比较耗时。
2.7 “SFX”法
“SFX
”法利用了Ollydbg
自带的OEP
寻找功能,可以选择直接让程序停在OD
找到的OEP
处,此时自解压已经完成,可以直接dump
程序。
3一些脱壳实践
下面给出整理的使用以上方法,自己尝试手动脱这几种常用壳的脱壳笔记。
3.1UPX脱壳笔记
首先进行侦壳:
首先把程序扔到OllyIce
里面可以看到:
然后这里尝试使用ESP
定理:即在ESP
第一次改变时,对ESP
的地址设置硬件字访问断点,这样可以在代码被UPX
算法还原之后,跳转到程序的正常入口处。
然后F5
运行,并没有直接到跳转到程序入口处的大跳位置,但是可以看到UPX
的大跳就在眼前:
所以被还原后的程序入口点就是0x00445151
(通过单步往下走,F4
略过往回走的循环语句,也可以看到这个大跳的位置。)接下来走到大跳位置,跳到正常程序入口处:
然后去掉硬件断点,并使用LoadPE
的dump
功能dump
目标程序:
先修正映像大小,然后再选择完整脱壳,这样可以得到第一步dump的程序,然后再使用ImportREC
修复dump
程序的OEP
,OEP
的信息通过OD
自带的dump
功能查询或者直接填45151
:
将正确的入口地址填入ImportREC
中,然后自动搜索IAT
信息:
然后点击获取输入表得到修正IAT
之后的程序函数输入表,然后再点击显示无效函数,愉快地发现没有无效函数,那么就可以直接修复转存文件了。
选择刚刚第一步dump
下来的转储文件进行修复,修复完成之后脱壳完成:
这里对于压缩壳UPX
,直接使用了ESP
定律,可以很方便找到OEP
并dump
程序。
4.2 tElock脱壳笔记
这里脱的是一个tElock
的壳:
1、先使用最简单的最后一次异常法: 首先把程序扔到OllyIce
里面设置OD
调试选项中的异常选项,
仅保留内存非法访问异常,然后使用异常计数器插件,在使用前要清空断点设置:
等到程序正常运行后,重新加载程序,再选择第二步,停在最后一次异常之前:
然后用Alt+M
转到内存窗口,对主程序code
段下内存断点,SHIFT+F9
执行:
这样程序就中断在了正确的OEP
处,可以选择从模块中删除分析以显示正常分析的汇编代码。然后使用LoadPE dump
程序,并修正程序映像大小。但是在使用ImportREC v1.6F Fix
版,输入正确的OEP
,获取函数输入表信息时,会发现无效的指针。使用方法一修复后,再使用方法三可以完全修复:
再点击Fix dump
,可以修复之前dump
下来的程序,脱壳完成:
2、使用二次内存断点法: 首先载入程序,将所有的异常类型忽略,然后在idata
段设置内存断点, 然后SHIFT+F9
:
停下来后再次在code段设置内存断点,再次SHIFT+F9执行,可以直接达到正确的OEP中:
然后LoadPE dump
,然后修复IAT
。修复方法同方法1。
3、寻找magic jump
以及修复函数表完成后dump
程序: 前两步还是加内存断点(idata
、code
),然后定位到程序的正确OEP
处
然后如果这时使用LoadPE dump
后修复,就和前两种一样了。这里先是使用ImportREC
获取函数输入表第一个位置的指针地址。
然后得到函数指针偏移地址在0x005512C
,加上基地址后为0x045512C
,这时在该位置下硬件访问双字断点。再重新SHIFT+F9
忽略异常执行后,由于下了断点,会触发tElock
的CRC
校验错误:
所以这里要先绕过CRC
校验,才能成功执行到硬件断点位置,所以首先暂停程序,然后使用Alt+F9
返回用户代码。点击确定按钮后,程序暂停在调用ExitProcess
的位置:
现在要向上找一找能跳过这个退出的跳转(CRC
判断跳转),然后进行修改并跳过:
找到了应该修改的位置,但是如果修改之后重新运行是会被恢复的,所以先记下来这个跳转的地址,0x00469622
。重新运行之后,在idata
断设置内存断点,SHIFT+F9
停下后,再Ctrl+G
找到修改点再修改。修改完之后再设置之前的硬件断点,这样不会触发CRC
校验错误了。
无数次的SHIFT+F9
之后,在寄存器窗口可以看到指针以及能够正常显示:
然后此时F8
单步,找magic jump
……看小生大大的视屏是通过分析疑似CRC
跳转得到magic jump
的位置:
这里记下来magic jump
的地址是0x0046973B
,然后清空udd
文件,删除硬件断点,再次重新运行程序,然后在idata
下内存断点停住,然后Ctrl+G
找到magic jump
位置处,修改跳转:
然后在code
段下内存断点:
然后SHIFT+F9
执行,停下来就到了OEP
的位置:
这时候再dump
程序,IAT
表已经被修复,可以直接获得脱壳版程序:
这里尝试使用了另外两种脱壳方法,并且通过预先找OEP
的方式,修复了CRC
校验后,直接dump
到了IAT
被修复了的程序。
3.3 PEncrypt脱壳笔记
首先进行侦壳:
先把程序扔到OllyIce
里面,然后程序停在这里,看起来蛮怪的:
好吧,重新加载程序,尝试使用最后一次异常法,不忽略所有异常,然后使用异常计数器插件,程序停在最后一次异常处:
如果此时F8
单步下去,程序会触发异常处理,然后又到不了OEP
了。这时需要看一下堆栈数据情况:
这时需要在0040CCD7
处F2
下断点,然后SHIFT+F9
执行,可以跳过这个坑:
然后接下来就是F8+F4
的操作,一路直到OEP
:
用LoadPE
脱壳,然后用ImportREC
修复后,虽然没有无效指针,但是还是不能运行:
这时候用LoadPE
的重建PE
功能:
然后就可以正常运行了:
这个壳使用了单步跟踪的脱壳方法,一路跳过程序“陷阱”,最后达到OEP
。并且使用了LoadPE
的重建PE
功能,对程序进行了重建,最终完成了这个加密壳的脱壳全过程。
3.4 FSG变形壳脱壳笔记
首先进行侦壳:
使用ESP
定律,首先把程序扔到OllyIce
里面,F8
单步走,观察ESP
变化,在ESP
第一次发生变化时,对ESP
对应的地址处设置内存硬件访问WORD
断点,然后SHIFT+F9
运行,在程序停下来之后,取消硬件断点,进行F8
单步:
用F4
略过向后的跳转(循环),然后继续往下找,一直到这里:
在这个jmp
下面F4
,程序会跑飞。说明程序代码在这个循环中就已经释放完毕,所以向上找找这个循环中有没有带条件的大跳。这样很容易找到magic jump
的位置,然后我们Enter
或者Ctrl+G
到00402666
的位置,发现果然是OEP
,重新分析,然后F2
下断点,让程序走到OEP
:
如果是FSG1.33
,直接使用LoadPE dump
文件,然后使用ImportREC
修复,就可以正常脱壳了。但是这里在使用ImportREC
修复时,会出现一个无效指针:
这里直接剪掉(或者删掉)这个指针,然后修复转存文件,发现无法正常打开:
然后再把修复后的程序,丢到OllyIce
中F9
直接运行:
这里是变形壳添加的一个暗桩,会导致程序出现异常退出,这里直接nop
掉或者把之前的jle
(校验)改成jmp
,然后保存修改另存文件。然后就可以运行了
4 参考
【百度百科(各种概念)、自己之前的脱文……】 另附一篇脱壳步骤汇总:http://www.52pojie.cn/thread-259984-1-1.html