- 第一章 - OD 的各个窗口介绍
- 第二章 - 数值系统
- 第三章 - 寄存器
- 第四章 - 汇编指令
- 第五章 - 数学指令
- 第六章 - 比较和条件跳转
- 第七章 - call ret
- 第八章 - 循环 字符串指令和寻址方式
- 第九章 - 基本概念
- 第十章 - 断点
- 第十一章:硬件断点与条件断点
- 第十二章 - 消息断点
- 第十三章 - 硬编码序列号寻踪-Part1
- 第十四章 - 硬编码序列号寻踪-Part2
- 第十五章 - 硬编码序列号寻踪-Part3
- 第十六章 - 序列号生成算法分析-Part1
- 第十七章 - 序列号生成算法分析-Part2
- 第十八章 - 序列号生成算法分析-Part3
- 第十九章 - OllyDbg 反调试之 IsDebuggerPresent
- 第二十章 - OllyDbg 反调试之检测 OD 进程名
- 第二十一章 - OllyDbg 反调试之检测 OD 进程名,窗口类名,窗口标题名
- 第二十二章 - OllyDbg 反调试之 UnhandledExceptionFilter,ZwQueryInformationProcess
- 第二十三章 - OllyDbg 反调试之 ProcessHeap,NTGlobalFlag,OutputDebugStringA
- 第二十四章 - OllyDbg 反调试之综合 CrackMe
- 第二十五章 - 异常处理
- 第二十六章 - Visual Basic 程序的破解-Part1
- 第二十七章 - Visual Basic 程序的破解-Part2
- 第二十八章 - Visual Basic 程序的破解-Part3
- 第二十九章 - P-CODE-Part1
- 第三十章 - P-CODE-Part2
- 第三十一章 - 脱壳简介
- 第三十二章 - OEP 寻踪
- 第三十三章 - 神马是 IAT 如何修复
- 第三十四章 - 手脱 UPX,修复 IAT
- 第三十五章 - 手脱 ASPack V2.12
- 第三十六章 - IAT 重定向
- 第三十七章 - 论 IAT 重定向之修复
- 第三十八章 - 手脱 Yoda's Protector v1.3(Yoda's Crypter)
- 第三十九章 - 神马是 stolen bytes
- 第四十章 - OllyDbg 脚本的编写
- 第四十一章 - 神马是 AntiDump
- 第四十二章 - ACProtect V1.09 脱壳(寻找 OEP 绕过硬件断点的检测 修复 Stolen code)
- 第四十三章 - ACProtect V1.09 脱壳(编写脚本修复 IAT)
- 第四十四章 - ACProtect V1.09 脱壳(修复 AntiDump)
- 第四十五章补充章节-ReCrypt v0.80 脱壳(续)
- 第四十六章 - Patrick 的 CrackMe-Part1
- 第四十七章 - Patrick 的 CrackMe-Part2
- 第四十八章 - PeSpin V1.3.04 脱壳-Part1
- 第四十九章 - PeSpin V1.3.04 脱壳-Part2
- 第五十章 - 再谈 ReCrypt v.0.80 脱壳(调戏 OutputDebugString)
- 第五十一章 - ASProtect v2.3.04.26 脱壳-Part1
- 第五十二章 - ASProtect v2.3.04.26 脱壳-Part2
- 第五十三章 - TPPpack 脱壳
- 第五十四章 - EXECryptor v2.2.50.a 脱壳-Part1
- 第五十五章 - ExeCryptor v2.2.50.a-Part2
- 第五十六章 - EXECryptor v2.2.50.b 脱壳
- 第五十七章 - ExeCryptor v2.2.50.c/d/e/f/g 脱壳
- 第五十八章 - ExeCryptor v2.2.50
第三十一章 - 脱壳简介
原定本章是要介绍 P-CODE 的 Part3,然后再介绍脱壳的。但是很多朋友跟我说 P-CODE,WKT 的教程有很多,并且现在 P-CODE 的应用程序已经很少了,没有必要过多的介绍 P-CODE,希望我能够多讲讲壳,提高大家脱壳的能力。所以说本章我们开始讨论壳,首先要给大家明确一点,壳的种类繁多(PS:这里说种类繁多,并不准确,种类也就那么几种,压缩壳,加密壳,虚拟机壳,所以说应该是数量多,之前发过一篇帖子收集了 T4 社区的各种脱壳脚本,充斥着各种各样的壳,大家可以看看),所以接下来的章节,我不会介绍所有的壳,只会给大家介绍壳的基本概念以及原理,还有几个具体壳的实例,大家不要指望看完本教程将能搞定所有壳,本教程的目的在于帮助大家理解壳的原理,锻炼大家解决未知壳的能力,大家要学会举一反三,触类旁通。
本章我们将介绍壳的基本概念,这对我们后面脱壳是大有裨益的,大家不要因为简单,就忽视它,后面章节,我们会介绍一些手工脱壳的实例。
首先大家可能会问为什么要给程序加壳?(PS:其实地球人都知道,嘿嘿)
我们知道一个未加壳或者脱过壳的程序修改起来很方便,但是如果一个加过壳或者自修改的程序,要想修改它就比较困难了,我们在入口点(PS:这里指的入口点是壳的入口点) 处修改程序是不起作用的,只有当壳把原程序区段解密完成后修改才能起作用。
加过壳的程序,原程序代码段通常是被加密过的,我们想修改它就不那么容易了。
加壳程序给目标程序加壳的原理通常是加密/压缩原程序各个区段,并且给目标程序添加一个或者多个区段作为原程序的引导代码(壳代码),然后将原程序入口点修改为外壳程序的入口点。
如果我们将加过壳的程序用 OllyDbg 加载的话,OllyDbg 会停在壳的解密例程的入口点处,由此开始执行。
壳的解密例程(外壳程序) 首先会定位加密/压缩过的原程序的各个区段,将其解密/解压,然后跳转至 OEP(程序未加壳时的入口点) 处开始执行。
现在我们来看一个最简单的壳,UPX 壳,大家可以去下面网址中下载一个 GUI 版,名称为 GUiPeX_Setup。
http://www.blueorbsoft.com/guipex/
安装好以后运行起来。
我们加壳的对象就选择大家熟悉的 CrueHead 的 CrackMe,首先我们不加壳将其加载到 OllyDbg 中。
我们可以看到其入口点是 401000,也就是说,运行它,将从 401000 地址处开始执行。
现在我们用 GUiPeX 将其加壳,解密其区段,并且将入口点修改为解密例程(外壳程序) 的起始地址。
当我们运行加壳后的 CrackMe,首先会从解密例程开始执行,解密例程会解密原程序各个区段,然后定位到位于原程序代码段中的入口点,也就是大家常说的 OEP(Origianl Entry Point),即 401000,接着跳往 OEP 处开始执行原程序代码。
下面我们就来讨论 CrueHead 这个被加过壳的 CrackMe,该 CrackMe 的 OEP 位于何处我们已经清楚了。
我们将该 CrackMe 保存一份备份并将其放到一个安全的地方,比如说桌面,因为接下来我们需要将加过壳的 CrackMe 与原始的 CrackMe 进行对比。
打开 GUiPeX。
我们将 CrackMe 拖拽到上面那个窗口中。
我们可以看到上面窗口中显示出了带加壳的 CrackMe 的完整路径。后边的 Commands(命令) 选项我们选择 Compress(压缩)。
通过这个工具我们可以给目标程序压缩或者解压缩。
然后我们按 Run 按钮就开始给该 CrackMe 加壳了。
我们可以看到 UPX Output 窗口中显示该 CrackMe 加壳成功了。
我们将加壳后的 CrackMe 重命名为 CRACKME UPX.EXE。
我们会发现加壳后要比原来的小很多,加壳程序给原程序添加了额外的代码来保护原程序,体积反而变小了,嘿嘿。
我们将加壳后的 CrackMe 运行起来,可以看到跟原 CrackMe 运行效果一样。
现在我们用两个 OllyDbg 分别加载 CRACKME UPX.EXE 和 CRACKME.EXE,比较一下两者有什么不同。
CRACKME.EXE的入口点
CRACKME UPX.EXE的入口点
正如大家所看到的,CRACKME UPX 的入口点变成了 409BF0,将从这里开始执行解密例程,如果我们定位 401000 地址处,会发现找不到原程序的代码任何踪迹。
正如大家所看到的原程序的代码段是空的,即加壳程序将原程序代码段加密/压缩后保存到其他地方,并且将原程序代码段清空了。
通常情况下,大部分加壳程序会在待加壳程序中创建自己的区段,从自己的区段开始执行解压/解密程序,解压/解密程序会将对原程序各个区段进行解压/解密。
下面我们一起来看看解压/解密的例程,不运行,直接往下拉。
继续往下,直到你看到:
我们可以看到从解压例程开始执行,解压例程执行完毕以后就会跳往 OEP 处,没有做任何的隐藏工作,是不是很有喜感,不难吧,我们继续。
我们给 JMP OEP 这条指令处设置一个断点。
运行起来。
我们可以看到断下来了,这个时候原程序代码段已经解压修复完毕,我们按 F7 键跳转到 OEP 处(原程序代码段的第一行)。
这里我们可以看到到了 OEP 处,跟原 CrackMe 的入口点处代码一样。至此,解压例程就完成了其全部任务,整个过程是首先解压恢复了原程序的各个区段,原程序代码段恢复完毕以后,接着跳转到 OEP 处开始执行原程序代码,大家应该还记得刚加载的时候,401000 处是空的吧。
以下是加过壳的程序执行的整个流程(PS:现在的一些强壳的执行流程已经不能简单的这么概括了,以后再讨论):
1.执行解压/解密例程
2.解压/解密原程序的各个区段的数据
3.跳往 OEP 处
4.执行原程序代码
经过了这么多年的洗礼,加密解密技术水平的提高,壳的作者们也在使用各种奇技淫巧来扩充这个流程,他们会想尽各种办法来隐藏 OEP,并且还有加入各种各样的反调试保护手段,但是不管怎么变,加过壳的程序执行的常规流程就是上面说的几点(PS:如今的强壳以上几点不足以概括了,按 kanxue 老大的话来说就是”壳里有肉,肉里有壳”,以后我们再讨论)。
我们继续来讨论 CRACKME UPX。
很明显,原代码段是空的,在合适的时间,解压/解密程序会将解压/解密后的代码段数据重新写到这里,所以我们可以对这里设置内存访问断点,当解压/解密例程向这里写入数据的时候就会断下来。
我们一起来看看。
我们将 CRACKME UPX 的区段与 CRACKME 的区段对比一下就会发现,原先的代码段变大了。
原程序的代码段起始地址为 401000,大小占 1000 字节,而 CRACKME UPX 的代码段起始地址变为了 409000。
这里,我们直接给原程序代码段设置内存访问断点,接着运行起来。
断了下来,我们可以看到 401000 开始处的内存单元的第一个字节将被赋值为 AL 的值,此时 AL 值为 6A,我们再来看看原程序 401000 处的第一个字节是什么。
这里我们可以看到原程序入口点 401000 处的第一个字节也是 6A,如果我们按 F9 键,会看到接下来一个字节被填入了 00。
如果我们逐字节的跟踪,可以看到解密/解压例程将原程序代码恢复的整个过程。但是并不是所有的壳都是按照这个顺序来解密/解压原程序区段的,这里我们所看到的是最常规的情况。
如果大家仔细跟踪这个流程的话,就会发现这是一个循环,逐字节读取加密过的字节,接着进行数学运算(例如说:加法,乘法,等等) 来解密,运算完毕得到原始字节值,然后将其恢复到原处。
接下来,我们清除掉前面设置的内存访问断点,单击菜单项中 Animate into(自动步入) 选项,将能看到原程序代码段被逐字节还原的动画过程,原程序各个区段被还原后,就会断在我们前面设置的 JMP OEP 处的断点处。
这里我们可以看到解压例程解压原程序代码段的动画过程。
当执行到这里的时候,我们可以看到解压例程在循环读取加密后的原程序区段数据,对其进行解密运算,然后填充到原程序区段所在的原始位置,原程序所有区段被解密还原后就断在了 JMP OEP 处。
这里我们按 F7 键跳转到 OEP 处。
这里我们就到达了 OEP 处,我们之前说过加壳后的程序使用 OllyDbg 修改代码是不能直接保存到文件的,下面我们一起来看一看。
例如:我们想将前两个字节 6A 00 修改为 90 90,然后尝试保存到文件,OllyDbg 会报错:
这里 OD 提示在可执行文件中定位不到该数据。
如果你执意要修改的话,可以使用十六进制编辑器将 401000 对应的数据修改为 90 90,然后用 OllyDbg 加载该修改过的程序,接着来到 401000 处,可以看到是 90 90 开头,然后紧接着全是 00,当解密例程开始解密时,会将解密后的原始的 6A 00 再次填充到 401000 处,当解密区段完成并跳往 OEP 后,依然执行的是 6A 00 而不是 90 90,也就是说我们所做的修改将被解密例程覆盖掉了。
也就是说,如果你想修改这些字节的话,你需要定位到读取加密后字节的地方,在其解密出原始字节时,将其修改掉,这样才能成功修改这些字节,但是如果这样弄的话,需要做的工作太多了,得不偿失,大家把这个过程理解为文件补丁,如果待打补丁的程序被加壳,压缩或者有自校验,则补丁就不会被顺利利用。正因为有了如此不便之处,所以,在文件补丁技术以外,还需要一种更加高阶,隐蔽的方法,也就是内存补丁。
内存补丁的总体思想就是在某个时刻(解压,校验或某种情况发生以后),在目标程序的地址空间中修改数据,因此也被称为 Loader,每次使用时都需要调用程序运行。当然这就不是本教程所要讨论的话题了(PS:如果大家想了解内存补丁的话,可以参考加密解密 3)。
好了,本章就到这里,下一章节我们将讨论该 CrackMe 的脱壳。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论