加壳是对软件内核一种很有效的保护方式。目前Linux系统下的加壳方法,多是直接继承Windows 程序的加壳理论和方法, 在传统加壳工具上进行了有限的扩展, 单纯在LINUX 系统下实现的加壳工具还是很少的。如何在Linux 系统下尽量减少内核信息的暴露,增加有难度的反逆向手段来提升破解难度,对加壳保护程序进行很好的隐藏,都是目前主要攻克的难题。
根据加载外壳程序方式的不同将现有软件加壳技术分为:嵌入式、附加式和包含式。嵌入式中最经典的是Upx,支持多种文件类型加壳,且压缩算法先进。但该方法缺少反动态跟踪功能,破解者可用动态跟踪调试方法对Upx 进行破解。下文描述了附加式加壳(SELF 加壳):在原elf 文件格式中添加处理安全操作的代码段,但不内置解压缩,容易暴漏壳的位置。包含式结合了压缩和保护两种类型的壳,但壳加载的过程中会改变入口地址。结合上述三种加壳方法的优缺点, 文章提出了在Linux 系统下一种改进的加壳保护方法—加壳并重构可执行文件———SRELF[3]。改进后的算法将壳程序和目标可执行文件中代码段, 数据段等关键部分相结合,引入多态变形技术,使程序呈现出多态性,提高了壳程序的反破解能力,同时还很好的隐藏了加壳的信息。通过实验证明, 改进后的方法解决了加壳中入口地址易被改变的问题,使得加壳的程序以多态的形式出现,很大程度上提高了反破解的难度。
1 加壳原理
加壳实质上就是把一段特殊程序附加到应用程序中,并把程序的执行入口指向附加的特殊壳程序。壳的加载过程如图1 所示, 首先壳程序需要获得应用程序编程接口———即API 地址。在加壳程序的代码中用显示链接方式动态的加载所需的API 地址。通过壳程序后,对各个区块的数据按照定义进行解密;若加壳时用到了压缩技术,那么在解密之前先要进行解压缩,然后将解压文件映像到指定内存地址中。修改原程序文件的输入表后填充HOOK—API 表中的代码地址,间接的获得程序的控制权,进行校验和测试完后跳转到原入口点(OEP)[3]。
现有的软件加壳技术方法大多都改变了原有文件结构,在重定位的过程中改变程序的入口地址,加载的过程中把部分程序映射到地址空间中,若破解者知道如何在一个加壳程序中寻址,那么当文件被加载进内存时就可以找到加壳程序的信息。文章针对上述问题, 提出了一种改进的加壳方法SRELF。
2 SRELF 加壳方法
2.1 SRELF 方法原理
SRELF———加壳并重构可执行文件,最大特点是重构变形使得重构的程序呈现多态性。首先将目标elf 文件中的核心部分提取出来,然后与准备好的解密或解压缩程序,反静态分析和反动态跟踪程序结合在一起,让加壳程序呈现出多态变形性,最后遍历整合程序,按照elf 文件标准格式重新构造一个全新的elf 可执行文件。
2.2 SRELF 方法实现
如图2 所示:SRELF 加壳方法实现的基本程序框图。首先了解应用程序二进制接口文件(即扩展名为elf 文件)结构。图3 所示为标准elf 文件的结构图。从图中可以看到:一个ELF 头在文件的开始,保存了路线图描述了文件组织情况。随后是一些段(segments)或者一些区段(secTIons)。段中包含文件运行所需的信息, 而保存着object 文件的信息,用于链接和重定位。
第一步:提取目标文件中的核心部分
提取核心部分的可执行指令、动态链接表、段或节等信息。由于汇编程序中存在间接跳转使得反汇编生成控制流图中断,影响了提取核心代码的准确性,这里引入了一种间接跳转程序方式。
1)从壳运行到原始程序的OEP 进行单步向下跟踪,遇到抛出异常后,修改溢出标志;
2)分析操作数的类型。若是直接寻址类型,进入4);否则进入3);
3)找到定义的语句或函数入口,设置CREAT_SUSPENDED计数器,若计数器加一,则将宿主进程作为一个挂起的子进程打开,调用GetThreadContext()获取子进程初始化线程的上下文;
4)判断后的程序进行输入锁定,在语句序列上模拟执定义位置与目标间接跳转运算,对获取寄存器内容进行复制。使用间接跳转语句获取elf 头文件中的信息后, 定位到GetElfCore 的头表,然后定位到所需代码段的核心位置,将其中的数据复制出来。
第二步:对提取的核心代码加密并结合多态变形技术
采用进程注入加密技术,逆向阻止脱壳软件附加在受保护的进程上。先让CONTEXT.EBX 获取子进程的PEB 地址,读出PEB 子进程的映像地址后,将基础地址参数指向检索到的映像基址,最终完成对各种外部中断的监视工作。对加密后的核心代码进多态变形技术改进,让其改变自身代码,从而使搜索字符串的识别技术失去效果。文章采用的代码变换加密压缩来隐藏自身,为了防止搜索字符串的方法检测到,解密代码引动代码模块,程序将其插入其本身,调用代码模块中包含的函数,移动例程周围的指令,随机增加无用的指令,使用不同的寄存器和操作码,使得解密例程对应的二进制代码在不同的感染体内完全不一样。多态变形引擎过程如图4 所示。
第三步:将目标文件的核心部分和加壳部分整合
整合目标文件的核心部分和加壳部分需要进行精确的计算,设置正确的elf 文件头、程序头表、节头表等的数据。这样新生成的elf 文件才能正常运行。在整合elf 等数据时采用遍历整合的方法,对前面提取的的核心代码块逐一进行分析。采用循环回绕整合加壳后的核心代码。
1)先设立一个状态表,记录每个变量的状态类型。记录物理文件的大小和载入内存所占内存的大小。首先执行函数的初始状态, 将除去入口函数的所有函数都设置为untainted。若一个变量在多个正向前序代码段的状态表中出现,则重新计算其大小,并更新状态表。
2)内部执行:按照顺序逐一执行其内部的指令,更改变量的状态表, 将目标文件核心部分内容续写到加壳部分的后面,当执行到段中最后一步时形成输出状态表。同时检验程序变形状态的安全性,传递接口函数参数。
3)循环回绕整合部分:在核心代码全部执行完毕时,检查其后继代码段中是否有包含变形的循环头。如果存在,则该代码块是满足条件的最外层的循环头, 将程序头表中属性、大小的参数设置成新计算值,并检查其输入、输出状态表中是否有变量的类型状态发生变化,如果存在,则重新开始回绕执行,直到状态表停止更新。
3 相关算法性能对比
表1 给出了新的加壳算法SRELF 与ASProtect 算法、tElock 算法、Armadillo 算法的比较,表2 给出相关符号定义。
1)安全性提升
由于SRELF 采用的是二进制代码进行加密或压缩,并且没有对加壳功能程序大小进行限制,所以在代码中可以插入足够的花指令[7]。再加上高复杂度的加密变形压缩算法,保证了加壳程序的高安全性。此外变形重构了elf 文件, elf 文件中的内容全部改变,对表头文件进行静态分析脱壳又增加了难度。而且在SRELF 中加入了充分的反动态跟踪指令,防止被保护文件被动态跟踪。通过上几方面安全性分析,可以证明加壳后的elf 文件安全性得到了大幅度提高。
2)运算量降低
3)伪装性增强
加壳后的elf 文件结构并未改变, 而且SRELF 加壳方法不需要改变程序入口地址,很好的隐藏了壳程序。
4)扩展性提升
由于SRELF 方法从理论上没有对壳程序大小进行限制,使其具备了很好的扩展性。因此后期可以同步更新SRELF 中的加密方法,反静态分析方法,反动态跟踪方法,对其进行完善和坚固。
综上所述SRELF 克服了现有加壳方法中所暴露出来的问题, 解决了改变elf 文件结构和改变程序入口地址的重大缺陷,让加壳后的程序呈现出多态变形性,在反脱壳中增加了难度。因此,SRELF 方法是一个既具有很高的安全性同时具备良好的可行性的加壳方法。
4 结束语
文中研究了现有加壳软件在反破解中存在的普遍难题———出现完全不符合所有已知模式的新型安全缺陷[8],总结出现有加壳方式的不足,针对不足问题提出了一种改进的加壳方法———重构变形SRELF 加壳算法。软件加壳对重点代码进行加密、变形、反静态分析和反动态跟踪相结合,同时具备较为精简的运算量,提高加密部分的反破解能力。文中只考虑了加壳过程中引入程序变形性,使其不易被脱壳软件脱掉,下一步工作将解决在加壳过程中程序压缩的问题。此外,还会将改进的方法进一步应用到Linux 系统软件中。
技术专区
- Excelpoint世健基于Microchip平台的气体检测参考设计方案
- 基于STM32的三轴数字罗盘HMC5883L模块的测试方案
- 直流电源的测试与规范
- 如何重置LMC555定时器延迟硬件来收集调试数据
- 基于MSO的混合信号系统测试与调试[图]