换一种方式做加密—著名程序员刘涛涛谈扭曲加密变换技术
作者:刘涛涛 发布时间:2009-10-26 5:37:19
什么是扭曲加密变换呢?这是我开发的一款软件,可能官方的名词叫做“混淆加密”,我给自己的软件起名叫做扭曲加密变换技术。我断断续续用了一年多的时间开发这款软件,但是没做什么推广,这款软件直到现在不对外公开的。
现在加密变换适用于VC6、VS2005、VS2008等这么几个平台,下面我给大家讲一下在这个软件的开发过程中的一些思考,以及我的一些解决方案。
一、加密软件现状
当前常用的加密软件可以分为:
加壳类,比如ASProtect;
虚拟机类,比如VMProtect。
对于加壳类,壳总是壳,它与它要保护的内容总是有明显的分界线。尽管现在的加壳软件总是尽可能地加强壳与核的联系,但壳总是能脱掉的。当初我开发了一款跟踪调制器,可以对付这一类的加壳软件,因为一般来说,加壳软件的壳在一个新的段里面,就是原来的PE程序有数据段、代码段,壳比较难以做到和原来的程序处在同一个段,通常会在新的段里面,这就是它的分界线。所以在调试一个加过壳的软件的时候,运行到从一个段到另外一个段的时候的跳转时就停下来,这个时候原来程序的入口点就找到了,这也是加壳软件的弱点,就是难以和原来的程序严丝无缝地合并在一起。
那么当我们找到它的入口点之后,用一条命令DumpPE,把当前的内存映象存为一个文件。然后,我又加了一个命令MakePE,用这个命令,把这个映象重组一下,于是这个程序就脱壳了。当时在Win98时代,那个时候的壳也比较简单,我的TRW2000真的好用,三个命令就脱掉壳了。
后来,加壳者发现这样不行。他们一般来说还会提供一些很难跟踪,很难调试的函数,推荐加壳者,推荐开发者,调用一下你的函数,我再用加壳,这样的程序就不能简单用以上的方法。不过能够调用的函数通常也是比较简单的。
我们要破解一个软件的时候,我们首先第一步要做的通常是把一个软件脱壳,脱壳成了破解的第一步,也成了所有破解者的基本功。破解者说,谁不会脱壳啊?如果他不会脱壳的话,就没法搞破解。所以说脱壳成了破解的基本功,大家先要把壳脱了,然后分析它。
但是如果我们要逆向一个程序,我们不是要破解它,我们想把这个程序整个的变成源文件,整个我要搞懂它,这个时候其实反倒简单了。怎么说呢,我根本不需要脱这个壳,我只需要把程序运行起来,把内存映象保存下来,就可以分析了。我根本不需要脱壳,至于你在程序中调用的加壳软件提供的API函数多半是不重要的,可以不管它。通常来说,加壳软件对于逆向是没有什么保护作用的。
另外一个虚拟机类的加密,它需要用户选择一个或几个函数进行加密,它只能保护几个重点函数,不能保护你的整个软件不被逆向。它通常把几行代码,通过虚拟机膨胀之后,变成几百行,甚至是更高。所以呢,虚拟机类的加密软件,他们通常只能让你加密你代码中的几个函数,或者是函数中的一部分,会推荐你在编程序的时候,前面加一个标志,这段是用虚拟机加密的。这就决定了用虚拟机类加密,通常只能加密少量代码,而你的软件的绝大多数代码都会暴露的,这显然是不安全的。
并且我们看VMProtect的说明书,它明确说明它是为了保护你的注册机制,它并不能保护你的软件设计思路。
总之,现有的加密软件只能防破解,不能防逆向。
二、逆向的可怕
根据经验,如果努力一点,一人一天可以逆1000行C++代码,我有多年的软件逆向的工作经验,经常干这种事情,并且我最近一段时间,还开了一个逆向的培训班,教学生学习一些逆向的知识。直到上个月,我还刚刚完成了一个六万行C++代码的工程。根据经验,如果努力一点,一个人一天可以逆1000行C++代码!也就是说你辛辛苦苦几年时间开发出来的软件,如果不加保护,可能会以每人每天1000行的速度被逆向全部代码,这是多么可怕的一件事情。尤其是驱动。我们知道,驱动是最富技术含量的,驱动开发是最难的,能够做驱动开发的人,都是顶尖高手,都是薪水很高的人。而恰恰驱动呢,却是软件保护中一个问题最严重的地方。我们可以看到,我们的系统里面的那些驱动程序,个个很短小。就是说我们可能花费了很大的精力,很长的时间开发出来一个驱动,包括了我们软件的所有核心技术,但是它可能只有几十KB。被一个有经验的逆向者,可能几天时间逆向出全部源码。
这个也不妨告诉大家,我最近在做的软件,叫WinMount。这个软件的思想是合并了WinRAR和Daemon Tools两个软件的功能,就是我可以把一个压缩包展开,也可以把一个虚拟光驱文件展开,这样一下子把两款电脑的必备软件合并在一起了。我又增加了自己的思想,你有一个包,我把这个包直接虚拟成不存在的一个盘,把它放到虚拟的盘里去,这是我的思想。所以我现在正在做的这个软件,就是即时解压的这么一个思想,这是我现在主要搞的软件。
我要给大家说什么呢,即使是一个优秀的程序员,有一个想法,就这么凭空能把程序编出来吗?很困难的。一般我们都要参考一下别的软件,参考一下它们的思路。我当时有这个想法,有这个思想做这个软件做WinMount,又参考了一个类似的软件才做出来的。当然,那个软件的功能和这个软件的功能是完全不相同的,那个软件有一个子功能,可以虚拟出一个盘出来,我一看这个软件不错,我要把这个功能发扬光大,做成当时心目中的工程,于是我把那个软件逆向了一下,把里面所需要的内容拿出来了。
所以说被破解不可怕,可怕的是被逆向。如果你的软件被逆向了,就会冒出多个与你的软件功能相同的软件,你所有的技术就都不存在了。
三、加密产品如何切入?
如果我们要开发一款加密产品,怎么做?
刚才我们把传统的加密软件都推翻了,我们说加壳的也不行,虚拟机的也不行,我们首先要考虑第一个问题,我们要用什么作为我们加密的输入。比方说我们可以考虑,如果我用C++源码作为输入,搞得很难懂怎么样?这个辛苦点,因为你源码搞得再混乱,编译器会优化的,所以这一步是行不通的。所以说呢,多数程序就选择了用最终的执行文件EXE作为输入,现在的加壳软件和虚拟机都用了这个方法,我很通俗的把他们所用的方法,加壳的软件叫做包粽子,EXE已经存在在那里了,你想保护他,你包一层就可以了吗?虚拟机叫做挖窟窿。比如一个房子建好了,这个房子已经建好了,不是正在建,你感觉住着不牢靠,我把这两块砖敲下来,换成石头的,这个就是安全的吗?所以我们认为他们的方式都是不合适的。在实际分析当中,也是有问题的。比如说函数占用的空间是2KB,通常虚拟机类软件,把这2KB,填一些随机代码,这个函数的前面一个函数他不敢动,后面一个函数他也不敢动,这就是一个窟窿,一个优秀的分析者,他肯定能识别出这个窟窿,然后我还没分析这个函数,已经知道这个函数有多大了,在加密的战争中你已经失败了。
分析了以上两种可能,我们感觉它那种方法不好,所以我想出来一个方法,就是用编译中间文件OBJ作为加密的输入。我后来没有了解,是不是到目前为止,只有我一款软件用这个方法的,反正我是自己独立想出来的,我感觉这个方法很好。优点是什么?就是我加密的代码是无缝连接到工程中的。再一个就是我们分析OBJ的格式,比起C++的源码会简单得多。属于我的OBJ文件中有一个符号,这个OBJ会告诉我这儿有一个符号。所以我认为反汇编OBJ比反汇编EXE是安全的。
具体做法是,把OBJ文件解析,保留数据部分不变,反编译代码部分,然后扭曲变换成难读懂的代码,然后输出一个ASM,再调用ASM编译器生成新的OBJ文件,再LINK为最终目标文件。
如果你想这个代码安全,就不应该大段大段地使用那些容易透露你信息的数据,你可以把这些数据掩盖起来,或者用代码计算出来。
因为扭曲加密变换是采用OBJ作为输入。大家知道,库文件LIB实际上是多个OBJ文件的集合,这样我们可以把LIB中的OBJ逐个加密,再用LIB工具替换回去,使用这样的库编译我们的项目更安全。
我们分析一个程序,总是要先搞懂一些技术的库函数,找到printf、CString.Format、fopen等关键函数,这些基本函数找到了,理解一个软件就不难了,反之,如果连这些基本函数都找不到,或搞不懂,则分析工作无从下手。
四、遇到的问题
C++编译生成的OBJ,并不总是能从ASM编译得到,比如COMMON段,比如WEAK Symbol等,暂时使用改符号名的方法,把符号改为symbol-name-weak,编译为OBJ后再patch回来,把符号名改一下,编译完之后再替换回来,这算是一个解决方案。
五、变形的思考
虚拟机变形,很强大,很复杂,这也是它的缺点。它的变换并不神秘,并不可怕。你也可以构思一个虚拟机,可以大胆地构思,假如说我有一个CPU是8位的,那么这时候当你遇到一条32位的一条指令的时候,要想用你8位的CPU实现这个代码,就不得不编一长串的程序,完成32位下的一条指令。
它的倍率很高,但倍率不可调。我的选择,用多种低倍率的变换,迭代使用,这样倍率可随意调整,强度一点也不差。最重要的,我可以全文件低倍加密,重点部分高倍加密。
实际上要让代码难懂,并不需要那么麻烦。不就是要把代码扭曲吗?不就是要把代码混淆吗?难道非用虚拟机不可吗?我的意思是我们并不一定非要用虚拟机,我认为虚拟机变换不过是精心设计的一套变换,不过是一套变换,一套规律,尽管这套规律非常复杂,但是你毕竟是一套规律,我只要有精力去学习,我肯定还是能搞懂的。我认为与其我们用一套复杂的规律去对代码做加密变换,倒不如我们用多套简单的变换来做同样的事情。
用轻量的变换,然后再对少数参数做重点的变化,这是我的思想。
所以如果说有人给我一个软件,说它是加壳的,我肯定说这个没问题,有办法解决它。如果有人给我一个软件,用虚拟机加密的,我也会说这个没问题,因为多数函数是明码,只有极少数是加密的。我把90%以上的函数都搞懂了,剩下的几个函数尽管是用虚拟机加密的,我知道入口点在哪里,我知道入口参数是什么,甚至我也可以把它当成一个黑箱,看看它返回什么东西,这时候我已经搞懂了。
但是如果有人给我一个用我说的方法加密的软件,每一个函数都加了密,每一个简单函数都需要费很大精力才能搞得懂,我会说这个东西没办法做了,这是我的思路。
在实际的软件分析过程中,如果你看见一个高手在分析一个软件,过程是什么样子的呢?是一步一步去理解这个软件。先是找一些最简单的函数,把它搞懂,比如这个函数是打开一个文件,这个函数是什么,肯定是先从这些最简单的函数着手,然后在这个过程当中,这个分析者逐步地增强信心,他的思路逐渐明确,他越来越有信心,这个软件最后被搞定了。但是如果按照我的思路的话,他是不断地受到打击,他是举步维艰,就像我上面说的,如果他分析了一天,分析出一个函数,发现是两个数相加,他会气坏了,不干了。
我们具体要做加密,我的做法是基本上是以函数为单位进行加密变换,我首先会对这个函数进行堆栈分析,就是我要知道它那些技术变量是怎么实现的,有一些冷僻的汇编代码,汇编指令是无法识别的。下一步进行标志分析,也就是标志寄存器。如果我们每一步加密变换,都小心翼翼不敢去破坏这个标志的话,太受约束了,这个变化就不会丰富多彩了。我们用高级语言,用C++写出来的代码,绝大多数是不依赖于标志寄存器的。
实际上,这里使用的加密变换,不过是一个由简入繁的过程,应该说,每个人都可以编出自己的一套变换,不过碰巧这是我写出来的变换罢了。这里的每一个变换都不可怕,可怕的是我这种变换是可以迭代的。我起名字很简单,叫变换一、变换二、变换三,来了一个软件,准备加密他,来个一二一吧,先做一变换,再做二变换,再做一变换,变换之后这个代码就非常可怕了。
可以想像,只要我们这套将引擎开发完成了,要想增加新的变形方法,简直是易如反掌。我们可以召集几个程序员开会,限定你们十分钟之内,给我想两个加密变换。这个没有问题,由繁入简是唯一的,但是由简入繁方法太多了,经过一番复杂的运算,最后逆过来。所以说这个变换可以充分发挥我们的想像力。比方说你可以让你的EAX总是对真正的EAX取个反。
经过以上几步变换,代码已经很难懂,但还是顺序的。再加一个变换,就是用JMP指令打乱。这时候已经分不出我这个函数从哪里开始,到哪里结束。你找不到call,好不容易找到一个,结果是假的。这时候分析者连一个函数都找不到了。
有人说,你为什么不一条指令一跳呢?我觉得一条指令一跳代码的膨胀量太大了,2K的代码非要变成2MB,我希望我们的变换在前面的一二一里面有技术含量,用跳转太没有技术含量了,我每三五条跳转一下也就够了。
最后一步叫做缠绕。以上用JMP打乱还只是本函数内部打乱,如果把多个函数缠到一起,就无法从内存区域划分一个个函数。
通过以上多种变换方案的组合、迭代,我们可以自由选择控制加密深度,一个100KB的软件,我们可以加密为200KB,2MB,甚至200MB,而且没有垃圾代码,这200MB代码都是有用的,如果要理解它,就都要分析,海量的代码使分析变为不可能。
我现在开发出来这一套扭曲加密变换之后,没有公开,也没有做大量的广告,只给很少的几个朋友说了说,并且有好多人有意跟我联系说,把这个东西卖给我吧。我说不卖,第一个我认为一套加密软件,如果用户能够买到,能够下载,实际上已经不安全了。比方说最著名的那些加密产品都是有价钱的,当你分析一个软件,是用这个虚拟机加密的,先买一套,然后我们看到是怎么加密的,先给它一个最简单的函数,看看怎么变换,跟踪一下,再加两行,看看怎么搞一下。也就是说破解者可以用这种方法测试你的虚拟机加密软件的加密行为,那么这时候你这个加密实际上已经是不安全了,这是一个。就是说软件不卖,大家都拿不到,这时候我认为我的加密是安全的,你没办法说,我拿一个小函数让它变换一下试一试,做不到,这是一个,这是一点,就是说你能够买到加密软件,它就是不安全的。比方说我们经常需要打狗,一套软件是用加密狗加密的,破解者会买一个和你一模一样的狗,阅读一下这个狗的使用手册,提供了哪些调用函数,怎么调的,他学会使用了,他解你的狗也就不难了。所以我认为加密软件是不能卖的。
再有一个,我认为我的这个扭曲加密变换,比他们的加密软件都牛。为什么这么说呢?因为刚才我们已经把他们的加密软件全部推翻了,加壳的也不行,虚拟机的也不行。但如果用我这套东西加密的话,真正使破解者没办法分析了。并且我这个东西非常好用,你只要用VC编一个工程,然后把代码全过一遍,膨胀一下,再认可,到时候就没法分析了。我有一个担心就是,万一我这个软件要是散布出去的话,市场上所有的我们用到的程序都变成用我这个东西膨胀的,那我们这帮高手不就没饭吃了吗?
六、会不会影响效率?
一个软件中绝大多数代码,实际上都是只运行一遍,或者很少一遍的,你把一个软件整个加密两三倍,是不会影响效率的,针对现在快速的CPU来说,这点损失不算什么,少数重点函数可以不加密,也可以的。当你一个函数,几兆的软件,只有两三个函数没有加密,破解者找都找不到这两个函数,效率不是问题。
七、用扭曲变换加密就安全了吗?
没有绝对的安全,只能说,这种加密方案,更方便全面保护你的软件,使逆向和破解都更难,经过扭曲变换加密的软件,对破解者和逆向者来说,更吓人,更累人。
本文主要内容整理自著名程序员刘涛涛先生在2008中国软件安全峰会(www.sinoit.org.cn)上的演讲
本文转自: 黑客武林(www.hack50.com) 详细出处参考:http://www.hack50.com/stu/sort092/sort0105/27635.html