# 逆向基础——软件手动脱壳技术入门

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2015-09-07T06:06:00Z
Canonical: https://wepostx.com/topics/1018

> 乌云历史资料归档
>
> **原始作者：** YHZX_2013
> **原始编号：** superkieran-wooyundrops:745
> **原始发布时间：** 2015-09-07 14:06
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

这里整合了一下之前自己学习软件手工脱壳的一些笔记和脱文，希望能给新学软件逆向和脱壳的童鞋们一点帮助。

# 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脱壳笔记
首先进行侦壳：

![原文图片](/media/2016/06/8a789c94a9281cac823c70d95908844a)

首先把程序扔到OllyIce里面可以看到：

![原文图片](/media/2016/06/fa9bdd9a4b2f329fcf6437ec2f045d32)

然后这里尝试使用ESP定理：即在ESP第一次改变时，对ESP的地址设置硬件字访问断点，这样可以在代码被UPX算法还原之后，跳转到程序的正常入口处。

![原文图片](/media/2016/06/01339750580a2df6c5663d3809c4367f)

然后F5运行，并没有直接到跳转到程序入口处的大跳位置，但是可以看到UPX的大跳就在眼前：

![原文图片](/media/2016/06/6468c9de8d8be1a6fcff4ca4e336363b)

所以被还原后的程序入口点就是0x00445151（通过单步往下走，F4略过往回走的循环语句，也可以看到这个大跳的位置。）接下来走到大跳位置，跳到正常程序入口处：

![原文图片](/media/2016/06/267a2af6a888ec0b1d9782920b26a85f)

然后去掉硬件断点，并使用LoadPE的dump功能dump目标程序：

![原文图片](/media/2016/06/8b13b77c0a2b2f4670d4e67ee1bd5d17)

先修正映像大小，然后再选择完整脱壳，这样可以得到第一步dump的程序，然后再使用ImportREC修复dump程序的OEP，OEP的信息通过OD自带的dump功能查询或者直接填45151：

![原文图片](/media/2016/06/a59b0ddee4082efafa9773685936d96d)

将正确的入口地址填入ImportREC中，然后自动搜索IAT信息：

![原文图片](/media/2016/06/8033e67dd35a40af22a36cf0e4daac20)

然后点击获取输入表得到修正IAT之后的程序函数输入表，然后再点击显示无效函数，愉快地发现没有无效函数，那么就可以直接修复转存文件了。

![原文图片](/media/2016/06/22b17a317a94766ea2644e8e48ce49ef)

选择刚刚第一步dump下来的转储文件进行修复，修复完成之后脱壳完成：

![原文图片](/media/2016/06/ab068e4aa23ff5965a021cc7e6395c29)

这里对于压缩壳UPX，直接使用了ESP定律，可以很方便找到OEP并dump程序。

### 4.2 tElock脱壳笔记
这里脱的是一个tElock的壳：

![原文图片](/media/2016/06/d60e41e991a0ef3706eb832798028fd6)

1、先使用最简单的最后一次异常法： 首先把程序扔到OllyIce里面设置OD调试选项中的异常选项，

![原文图片](/media/2016/06/c535462d84b3a19f7158fe19b2a86b9a)

仅保留内存非法访问异常，然后使用异常计数器插件，在使用前要清空断点设置：

![原文图片](/media/2016/06/9a096781c29ac00559880c89fdc7efcd)

等到程序正常运行后，重新加载程序，再选择第二步，停在最后一次异常之前：

![原文图片](/media/2016/06/4d56c1431f272c4afbba643213fd114c)

然后用Alt+M转到内存窗口，对主程序code段下内存断点，SHIFT+F9执行：

![原文图片](/media/2016/06/6ce3934cde3bdfce368e76c9c82fa0b8)

这样程序就中断在了正确的OEP处，可以选择从模块中删除分析以显示正常分析的汇编代码。然后使用LoadPE dump程序，并修正程序映像大小。但是在使用ImportREC v1.6F Fix版，输入正确的OEP，获取函数输入表信息时，会发现无效的指针。使用方法一修复后，再使用方法三可以完全修复：

![原文图片](/media/2016/06/25f0ae7f5b9d8f9f4857ab22ee5ee0f4)

再点击Fix dump，可以修复之前dump下来的程序，脱壳完成：

![原文图片](/media/2016/06/d60c8f7f5a4df73f30ac76e5c0243605)

2、使用二次内存断点法： 首先载入程序，将所有的异常类型忽略，然后在idata段设置内存断点, 然后SHIFT+F9：

![原文图片](/media/2016/06/b4e417bceab4eaa90f9301b63505666b)

![原文图片](/media/2016/06/2c47e737d3f5d76da6e0872c80dcb836)

停下来后再次在code段设置内存断点，再次SHIFT+F9执行，可以直接达到正确的OEP中：

![原文图片](/media/2016/06/cfd7e01de116cc0b4dc44a4de6142971)

然后LoadPE dump，然后修复IAT。修复方法同方法1。
3、寻找magic jump以及修复函数表完成后dump程序： 前两步还是加内存断点（idata、code），然后定位到程序的正确OEP处

![原文图片](/media/2016/06/b2451a46cd64742c04cc4c64d470dfba)

然后如果这时使用LoadPE dump后修复，就和前两种一样了。这里先是使用ImportREC获取函数输入表第一个位置的指针地址。

![原文图片](/media/2016/06/64507c60fa89d3b433cabc0f2dd2df75)

然后得到函数指针偏移地址在0x005512C，加上基地址后为0x045512C，这时在该位置下硬件访问双字断点。再重新SHIFT+F9忽略异常执行后，由于下了断点，会触发tElock的CRC校验错误：

![原文图片](/media/2016/06/cdf751a684681071d1298ed1516fbc1c)

所以这里要先绕过CRC校验，才能成功执行到硬件断点位置，所以首先暂停程序，然后使用Alt+F9返回用户代码。点击确定按钮后，程序暂停在调用ExitProcess的位置：

![原文图片](/media/2016/06/cd6c2f9eaf877a637438200190574359)

现在要向上找一找能跳过这个退出的跳转（CRC判断跳转），然后进行修改并跳过：

![原文图片](/media/2016/06/7bffa96db0401c8e89187c18509bdb52)

找到了应该修改的位置，但是如果修改之后重新运行是会被恢复的，所以先记下来这个跳转的地址，0x00469622。重新运行之后，在idata断设置内存断点，SHIFT+F9停下后，再Ctrl+G找到修改点再修改。修改完之后再设置之前的硬件断点，这样不会触发CRC校验错误了。
无数次的SHIFT+F9之后，在寄存器窗口可以看到指针以及能够正常显示：

![原文图片](/media/2016/06/6ea0fe7498f1e38a0ff0ef2ed9855c5e)

然后此时F8单步，找magic jump……看小生大大的视屏是通过分析疑似CRC跳转得到magic jump的位置：

![原文图片](/media/2016/06/e3bae174dff7102996ffc385a80821c5)

这里记下来magic jump的地址是0x0046973B，然后清空udd文件，删除硬件断点，再次重新运行程序，然后在idata下内存断点停住，然后Ctrl+G找到magic jump位置处，修改跳转：

![原文图片](/media/2016/06/f8c52bd2ba1687c3c8ab0a2b6e2fa957)

然后在code段下内存断点：

![原文图片](/media/2016/06/4ef835117b873e1e39636b4ca195e98d)

然后SHIFT+F9执行，停下来就到了OEP的位置：

![原文图片](/media/2016/06/21cbf783fcd5744343ba9d82e0c9ad49)

这时候再dump程序，IAT表已经被修复，可以直接获得脱壳版程序：

![原文图片](/media/2016/06/2b0246acbd22e9d2ea0f072c412be19c)

这里尝试使用了另外两种脱壳方法，并且通过预先找OEP的方式，修复了CRC校验后，直接dump到了IAT被修复了的程序。

### 3.3 PEncrypt脱壳笔记
首先进行侦壳：

![原文图片](/media/2016/06/e123e18bf3c54a855afac115189aecee)

先把程序扔到OllyIce里面，然后程序停在这里，看起来蛮怪的：

![原文图片](/media/2016/06/eae1a8bc26ed4c509e5ab1e7b4b63c37)

![原文图片](/media/2016/06/9d890dba6f34a38df75ffe730fd07b6d)
 好吧，重新加载程序，尝试使用最后一次异常法，不忽略所有异常，然后使用异常计数器插件，程序停在最后一次异常处：

![原文图片](/media/2016/06/c4fe3e8724355482196a74706cd1fb6e)

如果此时F8单步下去，程序会触发异常处理，然后又到不了OEP了。这时需要看一下堆栈数据情况：

![原文图片](/media/2016/06/49bd1ea77bd51907162789b18c172176)

这时需要在0040CCD7处F2下断点，然后SHIFT+F9执行，可以跳过这个坑：

![原文图片](/media/2016/06/a7721ad99fee8eebf04b58f37e0694d5)

然后接下来就是F8+F4的操作，一路直到OEP：

![原文图片](/media/2016/06/ef5ea1ae5cc0cfa682c7dbdbc7237eb7)

![原文图片](/media/2016/06/f8ccd830795e9cab8756b08431998dc6)

用LoadPE脱壳，然后用ImportREC修复后，虽然没有无效指针，但是还是不能运行：

![原文图片](/media/2016/06/9462f807936a846f434d0e98b7fab62a)

这时候用LoadPE的重建PE功能：

![原文图片](/media/2016/06/25ddf0315cb340e556830ca3591e7045)

![原文图片](/media/2016/06/6a566e59edb80feb7b9732d41c7e1eea)

然后就可以正常运行了：

![原文图片](/media/2016/06/2bb0c94c9e33f7ceadc28773ed06d344)

这个壳使用了单步跟踪的脱壳方法，一路跳过程序“陷阱”，最后达到OEP。并且使用了LoadPE的重建PE功能，对程序进行了重建，最终完成了这个加密壳的脱壳全过程。

### 3.4 FSG变形壳脱壳笔记
首先进行侦壳：

![原文图片](/media/2016/06/e495d73594326d9398eefc6e9ec849d7)

使用ESP定律，首先把程序扔到OllyIce里面，F8单步走，观察ESP变化，在ESP第一次发生变化时，对ESP对应的地址处设置内存硬件访问WORD断点，然后SHIFT+F9运行，在程序停下来之后，取消硬件断点，进行F8单步：

![原文图片](/media/2016/06/8e470d66cbe3adbf023ed6b6fd75f8ad)

用F4略过向后的跳转（循环），然后继续往下找，一直到这里：

![原文图片](/media/2016/06/083910d54a39f77985bcd6af68c00760)

在这个jmp下面F4，程序会跑飞。说明程序代码在这个循环中就已经释放完毕，所以向上找找这个循环中有没有带条件的大跳。这样很容易找到magic jump的位置，然后我们Enter或者Ctrl+G到00402666的位置，发现果然是OEP，重新分析，然后F2下断点，让程序走到OEP：

![原文图片](/media/2016/06/a3926b9513fe0d5702aa067fd3626459)

如果是FSG1.33，直接使用LoadPE dump文件，然后使用ImportREC修复，就可以正常脱壳了。但是这里在使用ImportREC修复时，会出现一个无效指针：

![原文图片](/media/2016/06/4d33914de18b90099129560d9b8c575d)

这里直接剪掉（或者删掉）这个指针，然后修复转存文件，发现无法正常打开：

![原文图片](/media/2016/06/049015366f097c9f32dbfb1c7c553723)

然后再把修复后的程序，丢到OllyIce中F9直接运行：

![原文图片](/media/2016/06/e93c8f452524b1027fd070e48ed0a38c)

这里是变形壳添加的一个暗桩，会导致程序出现异常退出，这里直接nop掉或者把之前的jle（校验）改成jmp，然后保存修改另存文件。然后就可以运行了

![原文图片](/media/2016/06/b63d06fb09410f901cb3fa3b059ad17f)

# 4 参考
【百度百科（各种概念）、自己之前的脱文……】 另附一篇脱壳步骤汇总： [http://www.52pojie.cn/thread-259984-1-1.html](http://www.52pojie.cn/thread-259984-1-1.html)

## Replies
