反病毒引擎设计之可控制引擎流
作者:nEINEI (neineit_at_gmail.com)
来源:安全焦点
反病毒引擎设计之可控制引擎流
作者 : nEINEI
邮箱 : [email protected]
完成于 :07-07-26
一. 现有引擎体系架构
二. 现有引擎体系存在的不足
三. 针对引擎要做的改进
四. 可控制引擎流的思想
五. 关于未来
引言
杀毒软件的核心是反病毒引擎,引擎的优秀与否决定了杀软的品质。当实时监控,抢先杀毒,主动防御等令人心动的技术逐一步入我们视野时,我们发现,在安全得到一定保障的同时,威胁却并未因此而远离我们。
我们并不渴求杀软百分之百的预防未知病毒,但我们要求杀软的底线是快速响应,有效处理。但就目前来看,这方面仍需提高,从根本上讲是引擎的架构设计需要提高。
一、 现有反病毒引擎体系
在谈及现有引擎体系时,让我们先看一下反病毒引擎的定义。
反病毒引擎就是指依赖于一个特定的数据描述集合来完成计算机病毒检测和清除的一组程序模块。当然,这个特定的数据描述集合,就是病毒库。两者互相依赖,相辅相成。【注1】该定义中明确地指出了引擎的目的是完成计算机病毒的检测和清除,引擎的要求是,依赖于特定的数据描述集合(病毒库)。
现有引擎的体系是在80年代中后期逐步发展起来的。从最初的单一型对抗,发布免疫程序或专杀工具(确切地说那时还不是引擎),到对简单专杀工具的集成,而后串联若干个病毒检测流程,再到对病毒的共性提取,形成规范的病毒数据结构供检测模块使用(此时可看作是引擎的雏形)等一系列处理方式中形成的。
当引擎与病毒库的分离时,则标志着关注引擎的时代到来了。此时的引擎所承载的不止是病毒的检测与清除,还有稳定性、高效性、可扩展性和可移植性等方面。这时的引擎已不再是简单的流程处理,而是由一系列的专用模块所组成的病毒分析检测体系。因此反病毒引擎的设计是复杂的,需要全面细致的分析,深入的木马、病毒特的研究,及良好的技术前瞻力并充分考虑到日后的扩展。
目前来看,现有的反病毒体系一般包含以下模块。
1 文件格式识别模块。
2 壳识别模块
3 解压缩模块
4 脱壳模块
5 office文件预处理模块
6 脚本预处理模块
7 特征匹配模块
8 未知病毒检测模块
可以把现有的反病毒引擎看作是由这些模块组成树形系统,并且个别模块之间存在递归关系。如壳识别模块与脱壳模块,当一个加了多层壳的程序送入引擎后,识别、并脱壳后,仍需要回退到壳识别模块,以此来决定是继续脱壳,还是按未知壳或已知编译器处理。
这样的递归处理在引擎的其他模块中也存在,如解压缩模块,当然解压的层次是需要控制的,可以通过配置来决定引擎在这方面的工作粒度,现有大多数引擎都是在这样的体系下工作的。
二、 现有引擎体系存在的不足
虽然现有引擎体系已经是精细、复杂、严谨的。但对于灵活性,快速响应等方面仍然存在不足,表现在以下几个方面。
1 引擎检测流程的固化性。
2引擎对病毒库的高度依赖性。
对于问题1,引擎的检测流程一般是由反病毒引擎设计师预先设计好的。这就对引擎设计师有非常高的要求,要充分考虑到多种未知情况,以应对未来病毒的种种威胁。一个好的引擎架构可以使用多年而不用做大的调整,但一个不成熟的引擎架构在投放市场后很快就会因种种需求而改的千疮百孔。
但这里的问题是,即便再成熟的引擎架构也是流程固化的。它无法对有意识的恶意穿透做出及时的响应,它只能在现有的引擎结构的基础上做修改,以此来加强对新近威胁的处理。可以想象引擎设计师面对一次次的if else 流程修改的无奈吧,无法预知这样的修改是否又会引入新问题。而有些问题的修改则是可能会影响整个引擎架构的,这样连修改的机会都没有了。
这里举一个听说的例子,在2000年时,曾有人发现Mcafee Virus Scan 引擎有个严重的BUG,如果解压文件自身可执行部分感染了病毒,那么用户选择扫描压缩包时是不能被检测的。将这个问题提交给NAI办事处却长时间不能得到响应,或许NAI早就发现这个问题了,但看上去很小的调整,都会对整个引擎结构产生影响。
对于这样的问题只能等到下一版引擎去解决了,但下一版解决了这个问题,却无法预知新产生问题的解决办法,因此引擎检测流程的固化是导致引擎不能快速响应的一个主要因素。
对应于问题2,引擎对病毒库的依赖本身是算不上问题的。因为反病毒引擎走到今天本身就是工程化的结果。病毒库是对病毒特性的归一化描述,引擎在依赖于病毒库的基础上完成检测。
因为反病毒技术说到底是一个精确匹配的过程,只有精确的知道是什么病毒才能做出相应的处理,而对于检测多态、变形的病毒则更是如此。
但我们反向思考一下就可以发现,如果一种病毒的检测手段所需要的特征,不能加入到病毒库当中,那么我们现有的引擎是无论如何也检测不出来这种病毒的(这里不讨论启发式一类的检测)。
当这种病毒肆意侵袭用户机器时我们的引擎也只能是爱莫能助了。或许有的朋友会说,只要病毒库设计的足够合理,能够描述出任意特征码的表达方式就可以了。可惜事实并非如此。检测是要求效率的,在硬盘容量迅速增长,病毒库日趋庞大的今天,我们更多的要求是设计一个合理简洁的病毒库。
因为在病毒库达到一定量级时,分段特征的增多都会引起效率的迅速下降。同时复杂的病毒库结构也不利于维护,更不利于自动特征提取机制的处理。
所以病毒库仅是对病毒共性描述的一个集合,它不能涵盖所有病毒特性的描述,这样不能选取特征进入病毒库的病毒就成了引擎的敌人,但面对这样的敌人引擎却无能为力,除了写专杀工具辅助处理外,暂时别无他法。因此引擎对病毒库的高度依赖导致检测能力的下降。
三、 针对引擎要做的改进
此时让我们把目光投入到整个反病毒体系当中吧。AV技术走的是样本分析、特征入库、升级处理这样的环节。
维持这样的流程仅是增加了检测能力,在不升级引擎的条件下,并没有投入新的技术。也就是说在技术的对抗当中,我们只能寄希望于引擎的检测流程实用性与病毒库的样本足够齐全,除此之外我们并没有契机参与到与病毒技术的对抗当中。
而AV技术实际上是易获取资源,地下的VXER只要不断的修改病毒程序,直到绕过AV软件的检测为止就可以了。这一点并非难于做到,因为无论多强大的引擎都无法对抗有目的的病毒技术的穿透。
说到这里又回归到了我们开始时的初衷,我们并不需要打造一个固若金汤般的引擎(实际上也不可能实现这样的引擎),我们只需要一个能快速面对变化,迅速参与到技术对抗当中的引擎。
让我们想想,产生这种对抗、变化的源头是什么吧?是新的病毒变种、或是新产生的病毒?都有可能。但这种病毒要想使现有引擎变得哑火,它一定要满足这样的条件,要么对引擎的检测流程构成穿透,要么对病毒库构成穿透。除此之外它很难对引擎构成威胁,因为病毒分析员只要提取这种病毒的特征、入库、引擎就可以处理这种病毒了。
现在我们的思路已经很明确了,那就是打破固化的引擎检测流程,降低对单一病毒库的依赖程度。这样才能增强引擎工作的灵活性与处理力度。
四、 可控制引擎流的思想
可控制引擎流(Controllable Engine Stream,以下简称CES)的思想是,以更小的功能模块组成检测体系的基础环境,以动态的方式搭建起一组新的引擎检测流程。
下面说明CES的体系结构:
CES对外来说,关注两个方面,一个方面是用户的请求,一个方面对抗virus、trojan、worm等恶意程序的攻击。用户的请求是询问检测结果,恶意程序的攻击则使我们面临着新的威胁和引擎被穿透的可能。
CES对内部来说,主要由以下几部分组成:
1 核心管理调度(KMA)
2 基础功能模块集合(BFM)
3 ES(Engine Script)解析器
4 ES(Engine Script)调度器
KMA负责整个引擎的调度处理。对外负责接收待检测数据,输出检测结果,对内指挥ES调度器,完成新引擎的组建,及控制流的传输。
BFM 主要是由各个小功能模块组成的合集,每一个小的功能模块都以DLL文件方式存在。比如文件格式识别模块可以为rcgfm.dll,这个集合主要包括, rcgshell.dll、unpack.dll 、loadlib.dll、killtrojan.dll,killvirus.dll等,可以根据需求的不同加入不同的处理模块。
ES解析器主要是解析出由病毒分析员所编写的检测脚本,并根据脚本的处理流程组建出模块节点树。该脚本是针对新近流行的恶意程序的检测流程描述。当然编写脚本的条件是该恶意程序对已有引擎体系产生了穿透。否则现有引擎流程足够处理这种恶意程序了。
ES调度器主要是根据ES解析器解析出的结果组成一组引擎流,因ES解析器已经描述出了BFM集合中各个功能模块的调用流程,那里的工作就是把这种动态的程序调用流程用一种数据结构表达出来,存储在模块节点树中。这样每一个模块节点树的头节点都是一个检测引擎的入口。控制这些模块节点树如何工作就由ES调度器完成。
下面我们通过一次恶意程序对引擎的穿透,来系统描述一下CES的工作原理,与其快速响应的机制。
对于像Viking这样的病毒传播初期时,原有的引擎是可以很容易的检测,走捕获样本、入库、升级的流程就可以。但随后Viking病毒的发展则对引擎产生了威胁,以原有Viking为基础,通过加新壳、修改个别字符重新加壳、制作免杀等手段,逃避原有检测方式,一时间产生了大量的病毒变种并开始疯狂传播。
此时的引擎受到两个挑战:
1:短时间内无法获取所有Viking病毒变种样本,导致检出率不高的威胁。
2:很多样本的不同之处仅为个别字符的修改,或重新加一种新壳,当识别能力与脱壳能力不够完善时,采用单一特征将导致病毒库里的特征激增。
归纳问题1与问题2,可以看出此时的情况是对病毒库的一种穿透。一般安全类软件引擎所需要的库都为一组数据结构,特征码包含在这个结构当中,特征码可能是在病毒体上提取的一段128字节(或者更大)的数据,也可能是对病毒体的全文件的散列值。库的结构因厂商的不同而不同,但都遵循一个原则就是特征码是对病毒体的一个简单有效描述。原有的杀毒引擎在依赖于这样的库的基础上工作着。在病毒库被穿透的情况下有效的办法是推出专杀工具,因为专杀工具不依赖与原有库,它可以有更为灵活的库结构,针对特定情况甚至可以以路径名作为识别方式,因为它的目的是对抗近期流行病毒,有效识别强力清除是它的目的。
在CES架构体系下的引擎,应针对该情况出现,要做的事情有:
1 分析出有效检测手段:
2输出新的自定义病毒库:(针对需要库的情况,有些情况下不需要库)
3编写检测脚本:
对于有效检测手段,在不脱壳的情况下,二次特征应该是很好的一个解决办法。 也就是在程序EOP处提取若干字节(可以是16,32个字节都可以)作为一次特征,在加壳后的程序的某个偏移位置处取若干个字节(可以是128个字节或更大)作为二次特征。
这样的好处是EOP处的识别可以定位到某种具体壳或未知壳,根据这种壳的特性完全可以分析出加壳前的代码在经过加密或压缩后放在加壳后的程序何处偏移位置,提取若干字节作为二次特征。即便是未知壳也一样可以分析出它的二次特征,当然这种情况并不多。
这样分析样本所得到特征码质量较高,相比全文件散列的特征要有好很多,可以对抗简单修改文件字符重新加壳的情况。这样程序员可以很快的编写一个检测Viking的模块并封装成DLL文件投入到BFM中,例如:
#define FirstSignLen 16
#define SecSignLen 128
Bool CheckVirus(PBYTE pBuff,DWORD dwSize)
{
…
DWORD dwEop = GetEop(pBuff,dwSize); // 获得程序Eop偏移位置
If((dwEop + FirstSignLen) > dwSize) // 特征超出文件长度,非法
{
Return false;
}
PSIGNSTRUCT pSign = vSignVector.begin(); //取特征库第一条记录
While(pSign != vSignVector.end()) // 遍历特征
{
if(0 == memcpy(pSign->FistSign,pBuff+dwEop,FirsSignLen)) //匹配一次特征成功
{
if((pSign->SecOffse + SecSignLen)> dwSize)
{
Return false;
}
If(0== memcpy(pSign->SecSign,pBuff+pSign->SecOffset,SecSignLen)) //匹配二次特征成功
{
Return true; // 发现病毒
}
}
pSign++;
}
Return false;
}
以上仅是说明处理流程,实际代码并非如此简单。将该代码及修复代码(修复代码只要分离捆绑式的PE文件即可)封装成k_viking.dll加入BFM中即可,库可以由提取二次特征的工具生成。剩下的工作就是编写处理脚本了,这一步最简单仅是处理描述的流程。这样我们增加了k_viking.dll、 k_viking.lib、k_viking.ces(处理流程的脚本)文件,就完成了对viking的检测,后期维护仅是k_viking.lib文件。
在这样的引擎体系下,可以针对库的穿透、检测流程的穿透做出及时的响应,不会因空有好的有检测手段和处理方法而不能加入引擎中。病毒分析员只需针对特殊处理的部分,编写小的功能模块,投放到基础环境当中。由外部的控制脚本重新组织一个新的检测引擎即可。这样病毒技术的穿透并没有打乱我们已有的检测体系,我们仅是在原有的引擎调度队列中新增加一个引擎而已。即便有新问题的引入也只是在新加入的小功能模块当中,不会影响原有功能。
五、 关于未来
为了验证这一想法笔者编写了一个简单的CES框架,其灵活性、快速响应性都要优于笔者原有的设计体系。虽然开发CES的其前期工作较为烦琐,可一旦框架设计完成,后期的维护及应急处理则是非常方便。但CES体系也有先天性的不足,对于向引擎这样要求高效率的模块,动态生成模块节点树的处理流程效率上要落后于静态编译好的处理流程,具体的效率差异上笔者没有做具体的比较,但一下步的研究方向将是对CES引擎处理流程的优化。
一个新的想法或技术上的创新是否具有商用性,是需要不断实践的来检验的,CES的问题还很多,但希望它能帮助我们解决一些问题。
以上的想法仅是一家之言,错误或纰漏在所难免,在此还恳请大家指正,欢迎与交流^_^ !!
【注一】 摘自《浅析反病毒引擎》
附参考文献:
[1] 江海客.《浅析反病毒引擎》 2002-12