_ _ (_) | | __ ____ __ _ _ _ _ __ ___ _ __ _ __ ___ | |_ \ \ / /\ \/ /| || | | || '_ ` _ \ | '_ \ | '_ \ / _ \| __| \ V / > < | || |_| || | | | | || |_) |_ | | | || __/| |_ \_/ /_/\_\| | \__,_||_| |_| |_|| .__/(_)|_| |_| \___| \__| _/ | | | |__/ |_| /---------------------------------------------------------------------------------------\ |>...................[ Clam AV引擎分析v0.84版 ]...................<| |>......................[ by nEINEI/vxjump.net ]......................<| |>......................[ 2008-05-30 ]......................<| \>...................... [ neineit@gmail.com ] ......................= 0x612c && vsize >= 0x612c && ((vsize & 0xff) == 0xec)) 4)取 int_32 bw = min(rsize , 0x7000);跳到LastSec. PointerToRawData + rsize – bw位置。 5)在给定位置读取4k的数据,搜索是否包含\xe8\x2c\x61\x00\x00 数据,如果是,则报告W32.Magistr.A。 针对W32.Magistr.B的检测方案是: 与A变种类似,不同步骤是 3)if(rsize >= 0x7000 && vsize >= 0x7000 && ((vsize & 0xff) == 0xed))取 int_32 bw = min(rsize , 0x8000) 4)在给定位置读取4k的数据,搜索是否包含\xe8\x04\x72\x00\x00 数据,如果是,则报告W32.Magistr.B。 2 cli_scanmschm: 该函数用来对微软的CHM格式文件进行解码处理。 3 cli_scanscrenc: 针对微软的Script Encoder脚本加密的解密处理流程,解码后按目录文件方式扫描。 4 cli_scan_mydoom_log: 该函数用来处理Worm.Mydoom.M.log ,走入该检测流程分支依赖格式识别,返回CL_TYPE_UNKNOWN_DATA 类型, 显然这样的处理方式并不合理。该类型定义如下: {-1, NULL, 0, NULL, CL_TYPE_UNKNOWN_DATA} -1 为相对于特征处的偏移数据,这样当其它格式特征不匹配时就走如该检测分支。 [0x05] 总结 1增加的新特征格式是什么? 新增加库扩展有,*.db3、*.hdb、*.ndb,但在实际的库加载中仅是*cvd 格式。 这版中加入了匹配灵活性的处理, 像在解析特征时: ? 可以表示匹配一些字符 * 可以忽略任何字符 n 可以匹配n个字节 -n 可以匹配n个字节或多于n个字节 a|b 可以匹配a或者是b 特征 同时在扫描的过程中加入扫描类型控制: static int targettab[TARGET_TABLE_SIZE] = {0, CL_TYPE_MSEXE, CL_TYPE_MSOLE2, CL_TYPE_HTML, CL_TYPE_MAIL, CL_TYPE_GRAPHICS } 这样,待扫描文件的类型不符特征码所指定的类型,即使特征匹配成功,也不认为匹配到一条 病毒信息。显示这种方式也不是非常合理。 2 作者使用了BM算法,同时保留了AC算法,BM算法用在何处,具体检测方式? Clamav 对所有的文件类型进行扫描调用后,都转换为内存buffer扫描。根据库的格式的不同, 特征分别加载到BM与AC的算法结构中,在内存扫描过程中,BM与AC依次被调用,如有任何一个检测出结果, 则扫描过程完毕。同时对于BM算法,针对中间字符做了优化处理,加快了匹配速度。 从加载库方面来看,由BM,AC 承担库的加载任务,限于AC算法消耗大量内存,而将一部分特征加入到BM 算法,可以降低一部分内存的消耗。 该版是个多模式的BM匹配算法,前缀是基于两个字节的HASH,在将特征加入BM结构时取特征串的3个字节, 通过自定义的hash(a,b,c)函数,来保证将3字节的数据映射成2个字节数据,且尽量散开、随机分布。当hash值 发生冲突时,采用拉链法,把同值的特征链接成链表,后进入的特征排在前方。 在进行匹配时,以该BM算法定义的最小长度(10字节)开始遍历,不断求得待匹配数据的 idx = hash(a,b,c)(3字节映射2字节的方式)值,如果该idx存在,BM的bm_shift表中存在该idx的shit值, 则赋值shit = bm_shift[idx],这样在匹配失败的情况下,将以shit为长度跳过shit个字节,以此来加快匹配速度, 否则设置shit = 1,继续匹配。 3 增加对文件脱壳查毒,其具体流程是怎么样的? 目前clamav能处理三种加壳方式,FSG,UPX,Petite。检测流程是走到PE分支才处理的,首先保证PE格式的合法性。 然后读取EOP处168个字节,进行短特征的比较,依次进行FSG、UPX、Petite情况的脱壳处理。 FSG v2.0 : 先比较入口点的特征是否是0x87,0x25,是否符合引擎配置(如maxfilesize,这是待检测文件的大小范围)的情况, 再进行FSG2.0壳特点数据校验后(一些列针对数据的严格校验),调用unfsg进行脱壳处理。 FSG v1.33,v1.31:入口特征buffer[0] == 0xbe buffer[5] == 0xbf buffer[10] == 0xbe的验证,在进行数据校验后, 调用unfsg_133进行脱壳处理。 UPX :针对UPX壳,先进行可壳名称(”UPX0”,”UPX1”)的比较,而后依据内置的几条版本特征进行比较,比较的位置是EOP+0x69。例如: #define UPX_NRV2B "\x11\xdb\x11\xc9\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9\x11\xc9\x75\x20\x41\x01\xdb" 当比较后符合UPX_NRV2B特征的文件,记录下特征类型的函数,在后面进行脱壳处调用。 Petite: 主要进行EOP处匹配,buffer[0] == 0xb8 ,而后进行该壳特征数据的验证,如 cli_readint32(buff + 0x80) == 0x163c988d),在相关验证完毕后,调用petite_inflate2x_1to9函数进行脱壳。 下面的代码作者仍然写着 /* to be continued ... */ 目前看脱壳部分作者仍然会加入PE流程分支中。 4 如何处理下面四种方式,具体处理过程是怎样的?其中对HTML的处理是否有可学习借鉴的地方? HTML 文件处理方面,现在看分两部分,1是正常的网页,2是ScrEnc 加密部分的网页,针对ScrEnc加密部分靠 ”#@~^” 特征字符来识别,解密后继续调用正常页面解析函数cli_html_normalise 来处理HTML文件。 页面解析部分比较复杂,采用的是逐行读取数据,利用状态机的方法,把解析页面分解成若干个可能状态, 其中包括针对jsdecode功能,进行base64的解码处理。将处理的数据写入临时文件,之后依次调用BM,AC,MD5 进行库匹配。 分析页面数据以如下字符为分隔进行的: !、 < 、 > 、 = 、 & 等,其中像 COMMENT ,JSDECODE 这种则直接在该分支处解析处理了。 从目前的clamav理html方式上来看,对html的特征提取应该是页面中一部数据。从扫描的效率角度看不是太合理, 毕竟html文件,主要是以文本内容为主,参与二进制的库匹配既不合理也不高效。但从作者的角度来看,似乎更愿意将各 种文件类型的扫描最终都归成对内存扫描函数cli_scanbuff的调用,这样对库及扫描处理本身都比较方便。而html文件中 也确实存在一些插入二进制数据在里面的恶意网页。 从目前的情况来看,恶意的脚本文件以加密的情况居多,这样clamav解析出的数据参与匹配将无明显效果。 5 对脱壳后的PE文件支持重建,具体重建方法是什么。 重建的基础是,脱壳函数把脱壳后数据,以节为单元,排序后保存到了内存中,而看clamav作者的思路是 经过 ”变形” 的任何文件(PE、 rar、srencode等)都要当作新文件再次进入格式识别流程,这样,以节为为单元 的数据是不能直接进入内存buffer扫描的流程的。所以需要重建一个PE文件,用以继续扫描。 重建的过程如下: 1 首先根据脱壳后的信息,计算出重建的PE文件的大小,拷贝一个伪造的PE头结构过去。 2 填充重要的PE信息结构,如NumberOfSections、AddressOfEntryPoint、ImageBase等数据,以保证PE解析本身不会出错。 3 将新的节信息写入重建PE的内存中。 4 将脱壳后的数据拷贝到重建的PE文件缓存中。 5 组合成新的文件,继续走入cli_scandesc扫描流程。 扫描引擎支持MS05002、MS04028,具体走入分支的方法和检测思路是怎么样的? 1 Clam av 并没有具体针对漏洞的检测方案,MS05002、MS04028 都是通过格式识别函数识别出文件类型 CL_TYPE_RIFF、和CL_TYPE_GRAPHICS,而走入检测分支的。 2 针对ms05002的检测方案是: 定义近似的一个文件头, Struct riff_head { U_32 chunk_id; U_32 chunk_size; U_32 form_type; }; 读取文件头部数据到riff_head内,比较chunk_id是否是 "RIFF" 、"RIFX". 比较form_type 是否是“ACON“。 根据chunk_id的类型转换chunk_size,如果是“RIFF”,则对chunk_size做高低位转换,否则不变。 然后按文件块的结构依次递归读取数据,设置递归的层数是1000,并记下每次读取的位置偏移offset = cur_offset + chunk_size; cur_offset为递归解析riff_head后的位置。如果此时的offset小于 (int64_t)chunk_size) ,则认为是恶意构造的riff文件,是一个Exploit.W32.MS05-002。 针对MS04028的检测方案是: 首先进行简单头部的验证if ((buffer[0] != 0xff) || (buffer[1] != 0xd8)) 而后循环读取数据,每次读取4个字节,记录下读取的偏移位置,在经过多次数据变换验证后, offset = ((unsigned int) buffer[2] << 8) + buffer[3]; 如果offset < 2 ,责任是一个恶意构造的jpeg格式的文件,直接返回报告是Exploit.W32.MS04-028。 从上面的检测可以看出,检测的思路是,依据文件格式寻找溢出的时利用的关键数据,如果该数据不符个正常 文件格式的数据,或超过某一设置的值,则认为是一个可造成溢出的文件。对溢出的检测本身不使用特征匹配的方式, 这点和我写的脚本引擎中检测ANI漏洞很相像。 参考文献: [1] clam av 0.84 src. thanks killer,一起讨论研究引擎设计及提出若干问题。