代码战争:伪装和狙杀——从“壳”到“病毒混淆器”

一、前言
“壳”(Packer)——在安全领域同APT一样,是一个宽泛的概念,即指代一类对可执行代码及数据进行包装已达到隐藏、压缩或保护等目的的运行时代码。
在病毒与安全软件的代码对抗过程中,“壳”一直扮演着极为重要的角色。早期反病毒引擎仅能够扫描可执行文件最外层的特征(代码和数据)。“加壳”后的病毒由于原始病毒特征(代码和数据)被“壳”改变(压缩、加密等),则可以躲避反病毒引擎的查杀。随着这种对抗的不断发展,虽然很多病毒的核心特征变化并不大,但是“壳”的“免杀”能力在不断增强,不断地更新换代,长时间与安全软件进行着对抗。所以,在现今互联网大环境中,如果一个反病毒引擎不具备强大的“脱壳”能力,就不能对病毒进行有效查杀,最终落后于整个安全行业的发展。
 
早期的“壳”通常以压缩目标程序为目的,如UPX、PECompact等经典的“压缩壳”等。随着软件知识产权意识的萌发,以保护程序代码及数据为目的的“壳”应运而生,此类“加密壳”、“保护壳”往往会利用花指令、反调试等手段干扰破解者对被保护代码和数据的“窥探”(包括静态和动态分析),Asprotect、VMProtect、Themida便是其中一些经典的“保护壳”。
上述“压缩壳”、“加密壳”和“保护壳”由于加壳程序公开(免费或收费),所以安全工程师可以对”加壳“的过程和结果进行分析,从而可以通过代码识别程序是否被“加壳”、加了何种“壳”,进而“脱壳”,并可以将这种“脱壳”逻辑嵌入到反病毒引擎的扫描逻辑中,从而在一定程度上“解决”了“壳”的对抗。
然而近10年来,很多病毒作者通过“壳”和感染型病毒常用的多态、变形等技术编写非公开的“私有壳”对病毒代码进行保护,这类“壳”往往不仅包括上述公开“保护壳”的功能,并且外层的“壳”代码还会通过各种手段干扰、误导、对抗反病毒引擎的识别,比较典型的手段包括代码变形、入口伪装、高级语言包裹(High-Level Language Wrapper)等。安全领域为此类“壳”起了个更为“高大上”的名字:“病毒混淆器”(Obfuscator)。安全领域较早的关于“病毒混淆器”的详细分析可以参考2010年RECon大会上Pierre-Marc Bureau(来自ESET)和Joan Calvet对于C2Lop病毒外层的Swizzor混淆器的分析https://recon.cx/2010/slides/Recon2010-UnderStaningSwizzorObfuscation.pdf)。
 
“病毒混淆器”的出现使得传统反病毒引擎“认壳”才能“脱壳”的机制完全失效。更有意思的是,在很长一段时间,很多安全厂商由于一直以来依赖对“壳”和编译器的识别,完全没有意识到“病毒混淆器”的崛起,甚至根据“大数据”的分析发现“加壳”样本的比例不升反降,思忖一定是“现在反病毒引擎都能‘脱壳’,所以病毒作者也就不再‘加壳’了”。就像曾有人对笔者说的,“‘壳’的问题是个轮回的过程,从2008年以后壳就变得很少了”。然而,根据我们对大量病毒样本的分析,我们坚定的认为“壳”的问题并不是轮回,传统的“壳”早已被“病毒混淆器”这个更为复杂的概念意义上的“后代”所取代,并持续的进化着。
病毒制造者在“病毒混淆器”的帮助下,可以在短时间内批量生成大量“伪装”(变形)过的病毒样本并迅速传播。为了应对不断变化的病毒样本,云引擎和大数据人工智能学习的方法逐渐走进了人们的视野。但长期看来,上述两种对抗策略从根本上存在着不可逾越的瓶颈和数据资源的限制。而“通用脱壳”技术相较于前两者,虽需要长期的技术投入,但却可以获得更为持久、有效的查杀回报,无疑将成为未来解决“病毒混淆器”类问题的优选方案。
 
下面我们针对这三种技术进行详细说明:
1.       云查杀和云引擎
如今很多安全厂商的“云引擎”,主要是希望通过缩短反病毒引擎特征更新周期来与病毒作者拼速度,从而达到快速抑制病毒扩散的效果。当然,这显然不是“云安全”的本质,但由于篇幅所限,本文并不对“云安全”这一话题进行讨论。
“云引擎”虽然可以实时同步云端的计算结果,但由于网络带宽的限制,在有限的扫描时间内,云引擎只能在本地提取高度抽象的数据特征发送到云端进行匹配,所以一般云引擎会选择哈希类特征(通常是全文哈希)。而哈希类特征的检出能力与样本基本是1:1的关系,即一条哈希特征通常只能检出一个样本。
由于“云引擎”采用哈希类特征,所以每个“伪装”过的病毒样本对于云引擎来说都是全新的,都需要进行“鉴定”。我们并不需要进行精确的计算,就可以得出如下结论:“云引擎”从采集到样本并传到云端分析,到云端分析出结果,再到客户端能够请求到结果,这个周期远远长于病毒作者通过“病毒混淆器”批量生成变形病毒样本的周期。
虽然“云引擎”能够缩短反病毒引擎特征更新周期、提高安全产品的反应速度,但是对于“病毒混淆器”批量生成的变形病毒,起到的作用只不过是“掩耳盗铃”罢了。
 
2.       利用大数据和“人工智能“,猜测病毒的“伪装
近些年,国内一些安全软件发布了各自的基于大数据或“人工智能”的反病毒引擎。这些引擎本质上都是基于统计学算法,通过对海量样本以固定方法抽取特征,并对特征进行统计、分析,进而产生计算模型。依照计算模型对待扫描样本进行分类,进而预测新样本是否属于恶意分类。
在没有数据为依托的前提下,我不能对此类引擎的效果妄加揣测。但无论算法如何、样本如何选取,都逃不过一个重要条件,那就是对特征的抽取。我们不妨做如下假设:我们通过外貌、服饰、声音、举止等方面可以基本准确地判断一个人的性别,这就像扫描明文的恶意代码。如果将人关进房间,只有房间的颜色、外观等特征可见,而人本身相关的特征完全看不到,这就像是被混淆过的恶意代码。将大量的人随机安排进不同颜色,不同外观的房间,试问仅通过房间的颜色和外观,通过统计、分析,产生计算模型,并推测某一房间内人的性别是否可靠?我想答案是否定的。
无论是统计学或是人工智能算法算法本身并没有任何问题,但如果不能戳穿病毒的“伪装”,不能抽取到有效的特征,一切都只会是徒劳。如果仅抽取样本的外层特征,“病毒混淆器”作者可以很轻易“伪装”出正常的程序结构、代码特征以及数据特征来躲过统计模型的预测,甚至可以”误导”并“污染”统计模型从而使此类反病毒引擎导致严重的误报。
 
3.       通过“通用脱壳”戳穿病毒的“伪装
“通用脱壳”(Generic Unpacking)就是最典型的用于戳穿病毒“伪装”的技术。简单来说,“通用脱壳”就是不对程序进行精确的“壳识别”,而是通过某些启发式逻辑评估待扫描是否可能被“加壳”(或者完全不评估是否可能“加壳”),并在虚拟沙盒中运行待扫描样本,使其在虚拟环境中还原被保护的代码、数据、行为,从而进行查毒。关于虚拟沙盒相关介绍可以参考《动若脱兔——火绒虚拟沙盒简介》一文相关章节,此处不再赘述。后文中,如无特别说明,“虚拟机”均指代虚拟沙盒。
“通用脱壳”技术对于处理“病毒混淆器”显然具有极大的帮助,如果反病毒引擎构造的虚拟环境足够逼真、虚拟沙盒的执行效率足够高,那么对于批量产生的变形病毒样本,反病毒引擎仅需要捕获其中部分样本则可以查杀后续的全部变种。纵观国内外也只有部分反病毒引擎在不同程度上实现了“通用脱壳“技术,国外包括MSE、ESET、Kaspersky、BitDefender、VIPRE在内的多款反病毒引擎,很早就引入了“通用脱壳”技术,并且多年来从未停止过对这方面的技术投入。火绒也在2013年实现了“通用脱壳”技术,并至今仍然持续保持着火绒虚拟沙盒的高速更新。关于“通用脱壳”相关介绍可以参考《反病毒”芯“技术——火绒反病毒引擎简介》一文相关章节,此处不再赘述。
在对大量样本的分析基础上,我们将“病毒混淆器”的大致进化过程进行了简单梳理,如下表所示。后续,本文将以”病毒混淆器“的“进化”过程为主轴,回溯“病毒混淆器”与反病毒引擎的对抗过程。后文中,如无特殊说明,“混淆器”均指代“病毒混淆器”。
代码战争:伪装和狙杀——从“壳”到“病毒混淆器”
表1、混淆器进化过程总表
 

二、经典案例             1. 指令级混淆器——Conficker

Conficker的出现时间在2008年,作为最早使用混淆器的病毒家族之一,其对于整个病毒的“黑色产业链”发展有着里程碑式的意义。MS08-067这个漏洞利用门槛低、成功率高是这个病毒能够快速流行的主要原因,但能使该病毒能够长期处于“免杀”状态显然归功于其使用的变形混淆器。在该病毒流行的早期,国内外安全厂商在没有“通用脱壳”技术的状态下,只能采用批量入库的方式处理,从而很难有效通杀整个病毒家族。在Conficker“一战成名”之后,使用混淆器的病毒样本开始逐渐增多,“病毒混淆器”的“进化”也就此展开。
 

1.1    病毒行为

该病毒会尝试创建许多用于其进行病毒传播的网络连接,并禁用系统安全设置、阻止本地安全软件的正常升级。其主要传播途径为:U盘、网络共享、弱口令密码的共享计算机等。当病毒在一个局域网内部大范围传播后,会造成严重的网络阻塞。
通过火绒行为沙盒我们可以看到一些主要的病毒行为:
代码战争:伪装和狙杀——从“壳”到“病毒混淆器”
图1、火绒行为沙盒运行效果图
 

1.2    混淆器结构

Conficker家族的样本结构极为统一。最外层为混淆器,其主要功能为解密原有数据、对抗虚拟沙盒和反调试。经过混淆器的数据解密后,再向内一层是经过UPX压缩的Conficker病毒主体。如下图所示:
代码战争:伪装和狙杀——从“壳”到“病毒混淆器”
图2、Conficker病毒整体结构
虽然混淆器的逻辑并不十分复杂,但是其主要逻辑代码被切分成了很多小的逻辑块,随机打乱顺序后分布在整个混淆器代码中,逻辑块与逻辑块之间用jmp指令进行连接,jmp指令的跳转位置全部被存放在混淆器的跳转表中。
代码战争:伪装和狙杀——从“壳”到“病毒混淆器”