加壳到底是怎么回事?

603 阅读5分钟

很多不搞底层的小伙伴对于软件加密这个事儿都是爱恨交织的,一方面这事儿太底层,通常和本职工作无关,另一方面老板和市场又需要,所以基本上就是一个“加壳工具”了事。这时候,一个靠谱的加壳工具就很重要了。

多数人并不是很了解破者的工作,他们并不是电影里黑客那种在屏幕前运指如飞,甚至和网络安全都不沾边。破解最主要工作是逆向,而逆向本身是无关好坏的,很多工作都需要逆向技术。逆向就需要对程序进行庖丁解牛、分析处理,这其中需要的基本功、经验、缜密、耐心和运气确实能带来不小的成就感。我们通常觉得只有机器认识的二进制文件,在这些高手和工具面前,就和源代码没有区别。关于这一点,给大家看几张 IDA Pro 官网上的图会更直观。

1.jpg 主页上这张宣传图已经很能说明问题了,二进制程序反汇编成汇编代码,到这步,已经就可以进行人工分析了,现在工具又更进了一步,反编译成了高级代码,这时候再分析就更加如鱼得水了。为了说明这个问题,再上一张图,更加一目了然:

2.jpg

说了这么多,这才进主题,我们使用的加壳工具,不管是用了加密还是混淆还是虚拟化还是什么技术,从原理上说就为了让上面这种反汇编和反编译变得尽可能复杂,“反”出来结果尽可能无意义,甚至根本就“反”不出来。下面我就大体上说一下加壳工具是怎么工作的,我不是写壳的高手,实际中各种工具的处理步骤也会有所不同,但是万变不离其宗,了解清楚这个基本原理,大家也应该在工具选择和使用中多一些“知其然”吧。

解析二进制

二进制程序可能是 PE(Windows) 或 ELF(Unix/Linux) 或 Mach-O(macOS) 等格式,这一步就是把各种格式的二进制程序进行反汇编等操作,最终拆成一个一个的处理单元,我们后面称之为 Block。这些 Block 有可能是一个函数,也有可能是一个函数的一部分,里面有地址和代码指令。

这样我们就把一个程序变成了一堆 Block,此时配合编译信息和文件格式信息,就可以看到每一个 Block 所对应的函数名,这时候就可以针对每一个 Block 选择加密方式了,如混淆、加密、虚拟化,不同的加密方式强度不同,对性能的影响也不同,这里不展开说了。总之重要的 Block 就选安全强度最高的,不重要 Block 的就选性能最好的或不保护。

有一些工具会智能给出建议,这个功能在实际是很有用的。

安装壳代码

之所以叫“壳”,就是指把原始的程序装到一个“外壳”里保护起来。所以被保护过的程序启动其实是从壳代码开始的,然后才会启动处理之后的原始程序,因为原始程序上加了一堆的加密和虚拟化手段,没有壳代码参与,操作系统已经不能正确运行了。

另外有一些程序在销售时可能还需要许可证,比如对使用的时间进行限制,这时检查许可证的代码也可以自动放在壳代码中。一般许可或加密狗厂商提供的壳会自带这个功能,好处是简单,一个工具就行了,也不用调加密狗或许可的 API,坏处是不能个性化加密,强度一般,许可也不够灵活。

编译保护

这一步是关键,对上面的 Block 进行选定的保护,各种假分支、立即数加密、指令混淆、虚拟化、代码加密等技术,大家八仙过海,各显神通,结果就是一个 Block 会被掰开、揉碎、打散,变成多个 Block,而且还分散在各处。经过这一步,上面截图里那种工具直接反汇编或反编译的情况就基本上可以消除了,此时的程序,已经“面目全非”。

链接和保存

我们把程序拆了一顿蹂躏,产生了无数的 Block,还要组合起来,不然操作系统不认啊(我们最后还要在人家上面执行不是嘛……)。所以这一步的工作就是分配 Block、修复 Block 的重定位,最后保存成一个“合法”的可执行程序,操作系统就能执行了。

好了,一个“加壳”过程大体上就是这样了,当然这只是最基本上,还有很多的细节被省略了,例如程序数据的保护、导入表的保护、压缩、基本的反调试等等。

因为涉及底层,所以对操作系统、文件格式、指令等都有很多兼容问题需要处理,因为直接对程序二进制文件执行了“非常规”操作,又增加了不确定性,所以要打造一个可用、好用的壳,不可能一蹴而就,是需要时间打磨的,基本上就是一个避坑打怪的过程。

说到这里,有一个问题想必大家也就已经明白了,就是为什么加了壳的程序容易“报毒”?很简单,被处理过的程序内部已经不能像一个“正经”程序那样被人随便分析了,杀毒软件的思路通常是“不让我看懂就说明你有问题”,所以就报毒,这问题不能完全避免,但是完全可以做到不影响业务,有机会可以介绍一下。