分析一个email病毒

2012-6-28 Nie.Meining Debug

某邮箱很久没登陆了,打开一看,收到一堆来自 jfkoofia@yahoo.com的病毒邮件:
点击查看原图

随便打开一封,把样本下载下来,发现样本只有25k。用PEID查,加壳了:
点击查看原图

不过这个壳很简单,esp定律轻松脱掉(壳难脱就不玩了)。再查:
点击查看原图

PEID显示样本采用VC6编写,心情大好!写代码的都懂,通常VC写的样本都脉络清晰短小精干。生活就是这样,你无聊了就有人发玩具给你玩。先回一封感谢信:
点击查看原图

可以看到邮件服务器提示 “发送不成功”,jfkoofia@yahoo.com 果然是伪造的地址,推测这一堆病毒的真实来源不止一个同学,不过这种样本只会伪造这个固定的假地址作为来源。
接下来就把样本扔IDA里了,放进IDA发现果然脉络清晰,动态跟踪都用不着了。从WinMain函数开始看:
点击查看原图

首先创建一个互斥量来保证程序实例唯一化(互斥量的名字SkyNet略眼熟),接下来调用VirusSetup做点安装工作,VirusSetup大致如下:
点击查看原图

把自己复制到C:\windows\winlogon.exe,然后写到自启动里。冒充系统进程是很多病毒的常用伎俩,话说XP的任务管理器是通过进程名来识别系统进程的,结束名字为winlogon.exe的进程时,任务管理器会提示该进程是系统关键进程,不让结束。写完自启动后,该样本紧接着会去尝试删掉一大堆可能存在的别的木马创建的自启动键值。作者很有良心,还能帮忙杀毒。
执行完VirusSetup后,该样本创建了两个线程,GatherEmailAddresses和MakeNoise,不过创建线程时句柄泄露了。这两个线程一个负责收集本机上的email地址(看来我就是这样“被中招”的,哪个哥们中的毒自己站出来吧),另一个线程负责发点杂音。先看发杂音的线程,该线程的主要工作是在特定时间出点声儿:
点击查看原图

低级幼稚不解释。接下来看看收集信息的GatherEmailAddresses线程:
点击查看原图

该线程知道自己效率低,为了不让CPU占用率飙升导致被人发现,先设置了一下优先级,这个技巧不错。接下来对各个非CDROM的分区进行遍历。这个g_strDriverNames是一个盘符字符串数组:
点击查看原图

这种实现方法浪费空间并且特征容易暴露,不如自己运行时累加,咱们别学他。
接下来的TraverseFiles函数是一个很标准的文件遍历框架:
点击查看原图
可以看到,最后没有忘记调用FindClose释放资源,估计是因为这个遍历文件的框架是作者抄来的。不过CharLowerBuff的应用可以表扬一下,作者没有自己去转换大小写。上面这段代码的主要功能是,遍历文件时,如果遇到目录,并且目录名中含有”shar”字符串,就调用CopyToDirectory把自己复制到该目录中(这个“shar”字符串的选择是个亮点,特征不突出并且覆盖面广,比如某些敏感的共享文件夹名字中带有share单词就会中招),如果遇到文件就调用XXXFile去分析这个文件,提取出里面可能包含的email地址。我们一个一个的来看,首先是CopyToDirectory:
点击查看原图

给自己选一个文件名,然后复制过去。候选文件名在g_strFakeNames里头:
点击查看原图

非常邪恶,我算知道那堆邮件中千奇百怪的病毒名字从哪儿来的了。
接下来看XXXFile,XXXFile会先取出文件的扩展名,然后根据扩展名判断是否需要提取信息:
 点击查看原图

该样本关心的扩展名主要有以下这些:
点击查看原图

都是便于提取字符串的文件格式。对于关心文件类型,样本把文件内容读入内存后,根据不同的扩展名,还会进一步进行处理:
点击查看原图

类似txt等简单类型的纯文本格式,直接XXXFileInternal进行分析就好了,对于其它格式可能会调用EncodeBuffer_1或EncodeBuffer_2先对编码进行一些转换。转换编码无非是替换掉一些非打印字符已经空字符,便于接下来的XXXFileInternal。类似这样:
点击查看原图

新行符替换成了空格。不过处理得并不好,遗漏了换行符“\r”,由此再联想之前的一些细节,例如文件操作用古怪的hread,API调用用低效率的ASCII版,以及创建线程时的句柄泄漏等等,八成这个作者是搞Linux的,对Windows的机制很不熟悉。在这里忍不住吐槽一下Linux,前段时间由于项目需要,在Linux上做了点开发,被Linux乱七八糟的API帮助文档折服了,随便man一个不会用的API,它就会充满激情地告诉你“这个函数好啊!比如你可以这么用,又可以这么用”,废话连篇,最后连参数和返回值是个啥都说不清楚。给Linux写文档的那帮人真该好好去学习一下MSDN。
回到正题,XXXFileInternal的工作就是从文件中通过字符匹配采集email地址保存到全局数组中,这里就不细讲了。
回到WinMain,创建完GatherEmailAddresses和MakeNoise线程后,就开始装备发email传播自己了:
点击查看原图

这段代码值得表扬的地方是通过调用InternetGetConnectedState来判断本机的网络连接状态,这个技巧很有用。代码中的g_strEmalAddresses就是之前GatherEmailAddresses采集到的email地址,看看交叉引用就清晰了:
点击查看原图

对于g_strEmalAddresses中的每一条记录,也就是每一个email地址都会新建一个SendEmail线程来对该地址发送邮件,并且每个SendEmail线程结束后才会创建新的SendEmail线程,因此作者在这个地方使用多线程并没有实现并发操作,仅仅是在泄露句柄。猜测作者本来想并发,结果发现总是崩溃,毕竟全局变量的同步问题作者完全没有考虑,于是就改成这样了。另外,判断线程是否结束使用WaitForSingleObject会比GetExitCodeThread好一些,不过作者似乎不理解Windows中的句柄,连关闭都不敢更别说wait了。
最后,SendMail函数通过连接邮件服务器,以smtp协议发送邮件,并把自身作为附件一并发送。代码片段如下:
点击查看原图

具体原理看看smtp协议就明白了,这里不细说了。
至此该样本分析结束。总的来说这个东西功能比较简单而且写得不好,不过也有一些很实用的小技巧值得学习。另外由于本人技术有限,分析错误之处还望包容指正。

发表评论:

Powered by emlog