对于一个软件开发商来说,要想了解PE文件加密处理的技术,第一步必须要研究PE文件格式,研究PE文件格式不但可以给我们洞悉Windows结构的良机,而且对于如何去保护自己开发的软件,有非常好的帮助,那么PE文件能否加密呢?下面我们就给大家介绍一下如何给PE文件加密。
一、什么是PE文件
PE文件的意思就是Portable Executable(可移植的执行体)。它是Win32环境自身所带的执行体文件格式,它的一些特性继承自Unix的Coff(common object fileformat)文件格式。“portable executable”(可移植的执行体)意味着此文件格式是跨win32平台的,即使Windows运行在非Intel的CPU上,任何win32平台的PE装载器都能识别和使用该文件格式。当然,移植到不同的CPU上PE执行体必然得有一些改变。所有win32执行体都使用PE文件格式,包括动态库NT的内核模式驱动程序(kernel?mode drivers)。EXE与DLL的差别完全是语义上的,它们使用格式,其实都是PE文件格式。唯一的区别就是其中有一个字段标识出是EXE还是DLL,还有很多DLL的扩展比如OCX,CPL等都是PE文件格式。
二、PE文件加密
对被保护软件的可执行文件加密是软件保护产品的重要组成部分,一般的加密思路是对可执行文件进行加一层保护壳,即对原始文件的代码段、数据段、引入表和原始入口点等进行加密,只有在运行完加壳的代码,才对以上内容进行解密。对PE文件加密是软件开发商保护自己合法软件的一个重要步骤,它可以极大的提高被保护软件的安全性。下面介绍一下加过密的可执行文件的装载过程。
1、加密过程
a、获取壳自己所需要使用的API地址
b 、解密原程序的各个区块的数据
c、重定位
d、HOOK-API
e、跳转到程序的原入口点
在外壳中可以实现很多软件保护技术,同时对于软件开发商来说是透明的,不需要软件开发商了解低层的保护技术,只需要使用外壳工具进行简单设置,即可生成被保护软件。在外壳中,目前常见的比较成熟的技术有:垃圾代码插入、压缩、调用变换、花指令、指令集转换、系统函数隐藏技术、防篡改保护、反调试技术等。
关于象反调试技术和反篡改等技术,已经有很多相关的资料和文档有很详细的介绍,下面我们将重点介绍关于如何让加密代码和被保护软件的代码融合在一起,让破解者很难通过内存dump的方法得到原始的数据段和代码段等进行重构新的未加密的程序。如何让加密代码和被保护代码融合为一体?我们可以采用以下技术来保护PE文件格式。
2、多态和变形
多态既是指完成同一功能采用了不同的方法。多态的特点是随机性和变化性。例如一个检查过程,调用了3个子过程完成全部安全检查机制。应用多态技术,这个检查过程在每次程序运行时,会随机调用不同的子程序并以不同的调用次序来完成全部的安全检查计划。如此,破解者很难跟踪,并做出通用破解补丁。这个随机的初始化过程一般由一个初始化程序完成,这个程序通常称为“多态引擎”。
变形:基于多态技术,它发展和提升了多态技术。它可以动态生成更复杂的检查过程,包括对重要程序片断的动态加密、解密,动态变换全部检查过程的代码和数据,基于内置的各种不同加密算法、解密算法。对目标代码段进行动态控制和编/解码。这其中可能还包括代码混淆过程。使得跟踪者迷惑于复杂的变换和还原过程,无法找到真正的检查过程和解码程序。如果其中包含复杂的加密算法会使得整个安全检查机制十分难于分析和最终的破解。
其实多态和变形技术最开始是使用在病毒上,但是技术本身没有对错之分,如今我们也可以用于对PE文件加密上,它可以保证每次产生的加密文件所使用的保护代码是不一样的,并且每次执行的代码也具有随即性,这样可以增大破解者脱壳的难度。
那么我们如何来编写一个多态呢?首先,我们必须在必须在脑海中清楚想要保护的代码是什么,以及解密程序是什么样。例如下面代码所示:
movecx,virus_size
leaedi,pointer_to_code_to_crypt
moveax,crypt_key
@@1:
xordword ptr [edi],eaxaddedi,4
loop@@1
这是一个非常简单的例子,主要有6条指令。多态的主要思想就是:改变寄存器,修改指令的执行顺序,为了达到同样的目的使用不同的指令,插入什么也不做的指令,插入垃圾等等。让我们看看对这个同样的解密程序,用一个简单的多态引擎初始的可能解密代码:
shleax,2
addebx,157637369h
imuleax,ebx,69
movecx,virus_size
rclesi,1
cli
leaedi,pointer_to_code_to_crypt
xchgeax,esi
moveax,crypt_key
movesi,22132546h
andebx,0FF242569h
xordword ptr [edi],eax
oreax,34548286h
addesi,76869678h
addedi,4
stc
pusheax
xor_edx,24564631h
pop _esi
loop _ 00401013h _cmc
or edx,132h
3、截获系统函数调用
处理引入表函数的目的就是让程序正常运行起来后,仍然需要定期执行加壳程序,否则破解者可以很轻松的对运行程序的内存进行dump,进行脱壳处理。其基本原理就是一般的win32应用程序都会调用向Kernel32.dll等系统核心动态库,通过截获对系统函数的调用达到保护的目的。
(1)修改代码
直接修改可执行文件的代码段中通过CALL或者JMP调用的函数地址,这种方法需要通过一定程度的反汇编分析代码段的二进制文件,找到相关的调用点,修改其地址,到壳的处理函数中,然后在壳的处理函数中,做完相应的检查措施后,再重新恢复堆栈,及相关寄存器的内容,跳回到原来需要jump的地址。这种方法实现难度比较大,但是安全性非常的高,一般破解者很难把修改过的代码重新恢复成原始代码。
另外一种方法实现起来也是非常的复杂。简而言之,就是通过确定原始函数的地址,然后使用JMP指令修改其中起始的几个字节,重定向到自定义的函数,这种方法非常的巧妙,包括需要对没一个函数进行一系列的恢复操作。原理如下图所示,左图为没有经过加密处理的反汇编代码,右图为加密过的反汇编代码。通过加密后,原本应该跳到Target Function的指针被修改成跳到Target Function+5的地址去执行了,执行完后再重新跳到Target Function地址继续执行。但是这种方式在多线程的环境下可能会又有一定问题。不过这种方式确实是一种非常好的解决方案,不但适用于PE文件加密,还适用于解决多种问题,比如如何把单机的PE文件转变成支持网络版的程序。
(2)加密引入表
在我们讨论修改引入表(IAT)之前,我们先阐述以下PE文件调用引入函数的过程。程序加载器的主要任务是负责把PE文件和相关的DLL加载到内存当中,但是每个DLL将被加载到什么地址,事先是不知道的。加载器是不能够决定实际的引入函数的地址。加载器必须执行其它一些工作来保证程序可以正确的调用每个引入函数。但是如果在内存中遍历每个可执行文件,然后去修改每一个引入表地址将会浪费大量的时间。实际上每一个调用引入函数的CALL动作都将被引入表使用JMP指令改成一个间接调用,这种设计的好处是加载器不需要去搜索整个映象文件,同时这种方式也比较简单,只是去修改在引入表的所有地址。如下图使用工具对PE文件的引入表的显示。可以看到在引入表中Kernel32.dll中保护两个引入函数CreateFileA ()和CloseHandle()。
如果我们对PE文件的原始引入表进行加密,在运行的时候进行解密,同时在调用引入函数的时候进行hook,同时再把以上提到的文件加密技术应用上,那么破解者将很难进行解密。
小知识之COFF
COFF –通用对象文件格式(Common Object File Format),是一种很流行的对象文件格式(注意:这里不说它是“目标”文件,是为了和编译器产生的目标文件(*.o/*.obj)相区别,因为这种格式不只用于目标文件,库文件、可执行文件也经常是这种格式)。大家可能会经常使用VC吧?它所产生的目标文件(*.obj)就是这种格式。其它的编译器,如GCC(GNU Compiler Collection)、ICL(Intel C/C++ Compiler)、VectorC,也使用这种格式的目标文件。不仅仅是C/C++,很多其它语言也使用这种格式的对象文件。