随着计算机与互联网相关技术的蓬勃高速发展,计算机已不再是“专业人员”的独有工具软件应用也逐渐与人们的日常生活深度绑定。如何妥善的保护软件,让软件安全运行在用户/客户的设备上,更好的服务大众助力业务稳定发展,软件保护自始至终都是一个重要的问题。
从软件保护的视角来看,可以笼统的分为应用保护、代码保护、算法保护,本文也将从这 3 个纬度进行阐述软件保护相关的问题
应用加壳:压缩壳、加密壳、抽取壳、保护壳
应用保护(也称应用加壳),主要是为了保护软件的著作信息、关键资源和关键实现等资产的一种有效措施。“加壳”与自然界和生物界中动植物为了保护关键“种子”非常相似,当然这壳有先天的(譬如蛋壳,天生自带保护壳),也有后天的动物的羽毛、古时的铠甲盔甲
应用保护历史悠远,从 DOS 时代起就出现了反编译相关技术,而应用保护在那个时期已初见雏形。 壳的初始作用是保护软件,但后来发展的方向不一就出现了各种各样的壳,一般有压缩壳、加密壳、抽取壳、保护壳、虚拟执行壳几个阶段,具体如下所示
软件加壳除了保护软件的著作信息、关键资源和关键实现外,还为了隐藏了程序真实的 程序入口点(OEP,Original Entry Point。或者用了假的 OEP ), 对于隐藏了 OEP 的保护,需要先寻找应用真正的 OEP,才可以完成 OEP 脱壳。
应用加壳和脱(解)壳彼此互为逆运算(类比于密码学中的加密与解密的思路与流程)。无非是在具体过程中采用的措施和思路并不相同,大致流程分为如下三个步骤
从而达到应用保护的目的,但又不干扰实际的逻辑
一般来说获取到抽取后的信息、解壳的逻辑后,然后由逆向工程师去“模拟”壳中脱壳逻辑即可完成脱壳(参考上述步骤 3)。【当然壳类型不同,模拟的过程也不相同。 譬如在虚拟执行壳中需要定位壳的解释器和自定义字节码的映射关系,才可还原】。
上述,“模拟”是个较为笼统的讲述。而实际的脱壳方式有内存dump、缓存dump、文件监听、内存重组、主动调用
静态保护:布局混淆、数值混淆、控制流混淆、预防混淆
动态保护:VMP、自修改代码
其他保护:多态保护、代码变形、代码加壳
如果说应用保护是给应用程序的大门加上了一把“大锁”,那么代码保护就是针对组成应用程序的源码加了多个“小锁”。在实际的代码保护中由于代码的粒度更细,组成更为分散,那么保护措施相对来说更多。
在实际应用中,代码保护通常无法完整且长期的保护,也仅只能给对应的逆向人员延长分析与调试的时间。在实际的应用中引入代码保护会需要消耗更多的计算资源和存储资源的消耗,所以对于性能损耗和阻碍逆向人员的资源投入,需要在实际场景中进行相关的权衡取舍。
对于代码保护有如下几种分类方式
所谓的静态分析是在不运行代码的情况下,对代码的静态特征和功能模块进行分析的方法。静态分析代码的优势在于它能描绘程序的轮廓,包括控制流和数据结构。
静态混淆是指保留代码原有功能上的代码等价转换,使其难以阅读、分析和理解。难以阅读,分析是混淆的目的,“等价转换”是确保混淆后代码和源代码的功能保持一致。
对于代码混淆的分类,通常以 Collberg 的理论为基础,细分为布局混淆、数据混淆、控制混淆和预防混淆。
布局混淆原是指删除或混淆与执行无关的辅助文本信息,增加攻击者阅读和理解代码的难度,具体到高级语言中就是指源代码中的注释文本、调试信息、代码命名和格式等。布局混淆能够在引入更多计算的同时进行代码,虽然单独布局混淆保护强度并不强,但通常在实际应用中都会用到布局混淆。
布局混淆也包括采用技术手段处理代码中的常量名、变量名、函数名等标识符,增加逆向工作者对代码理解的难度。具体有如下 2 种体现
数据是构成语言代码的基本元素(注意:此数据并非应用程序的数据,而是代码中原有的数据),同时也是语义分析的重要依据。在数据混淆的过程中通常会引入更多的计算,不过好在数据混淆较于布局混淆会有更好保护的效果。
数据混淆较于布局混淆是个更大的话题,一方面是数据类型种类更多,另一方面类型不同可引入的保护措施也更多。所以将在下述中对于基础数据类型进行分别讨论其混淆方式。
基础类型包含:字符(串)类型、数值类型、布尔类型、对象类型等
字符是最常见的常量数据(在此,将字符、字符串和文本统一称之为字符)。而字符中通常包含重要信息,如类名、方法名、变量名、异常,错误甚至是关键文本。而字符混淆便是将有“业务语义”的代码通过编码、加密的方式,将包含关键语义的信息进行保护。
数字混淆即通过数字计算拆分、进制转换、公式拆解的方式,对代码中的数字进行保护。虽然数字本身包含的业务语义,并非像字符中体现的那么敏感。但数字又是在代码中随处可见,譬如循环迭代的索引、数组取值。对数字进行混淆保护有助于代码防护强度更上一层楼。
布尔类型在底层直接由 0、1 构成,所以使得既可以使用数字混淆的技巧,也可通过类型转换等方式完成布尔混淆。
代码块都是按照逻辑顺序有序划分与组合,并且将相关的代码放在一起。在不改变程序功能的前提下,可以通过拆分重组代码等方式打破这种常规逻辑,使代码间的关系变得模糊,以此来保护程序的源码。
控制混淆即改变控制流或将原有控制流复杂化,通常的方式有不透明谓词、插入冗余代码和控制流平坦化
预防混淆的目的不是通过混淆代码增加分析调试代码的复杂度,而是提高现有的反混淆技术破解代码的难度或检测现有的反混淆器中存在的问题,并针对现有的反混淆器中的漏洞设计混淆算法,增加其破解代码的难度。譬如花指令、调试器检测
动态混淆,主要因对动态分析。技术在实现上主要包括自修改代码技术和虚拟机保护技术。
VM 是一种通过将源代码按照自定义的编译器变成对应的字节码,然后在运行时在代码中运行自定义的 VM 解释器。可类比于 JVM
VMP 是一种通过增加了一层自定义指令集、解释器的一种保护方式。其主要特性是自定义的指令集(也称虚拟指令集),由于实际的代码均是 VMP 动态运行后得到的。故无法直接有效的分析
自修改代码(SMC,Self-Modified Code)是一类特殊的代码技术,即在运行时修改自身代码,从而使得程序实际行为与反汇编结果不符,同时修改前的代码段数据也可能非合法指令,从而无法被反汇编器识别,这加大了软件逆向工程的难度。
其主要利用了冯罗伊曼体系结构的存储程序的特点,即指令和数据存储在同一个内存空间中,因此指令可以被视作数据被其他指令读取和修改。程序在运行时向代码段中写数据,并且写入的数据被作为指令执行,达到自我修改的效果。自修改保护机制可以有效抵御静态逆向分析而且由于代码仅在需要时才以明文的形式出现,可以在一定程度上阻碍逆向工具获取程序所有的明文代码,从而抵抗动态分析。
虽然代码混淆、加密、编码能够有效的保护代码,但也存在一定的问题。因为保护一成不变的话,被破解终究仅是时间问题。
多态保护的特点是随机性和变化性。多态保护即对代码本身进行变种替换,从而产生大量不同形式但功能相同的代码,甚至还可以采用多种不同的实现方法。
可以将代码变形看作多态保护的衍生。代码变形在多态技术的基础上更进一步,在每次应用过程中不仅对解密代码,还对代码主体也进行变换,使不同代码实例的代码完全不同。
算法保护,也称加密算法保护。由于在软件保护中通常伴随着编码、加密和摘要等行为。其用途和必要性在此不再过多赘述,但目前所使用的标准的密码学算法或多或少均有相关明显特征,譬如 base64 的码表,又譬如 md5 的魔数。一旦识别到关键点,即可直接通过动态调试到方式,直捣黄龙。直接定位到附近。那么上述的应用保护,还是代码保护如形同虚设无异。
那么基于此,可以对算法进行相关保护,从而保证无法直接从密码学的角度直接切入。最常见的保护方式有 3 种,即算法魔改,TEE