首页
社区
课程
招聘
[原创]最适合新手的病毒分析——彩虹猫病毒
发表于: 2020-10-22 23:38 20473

[原创]最适合新手的病毒分析——彩虹猫病毒

2020-10-22 23:38
20473

彩虹猫是一款很有意思的病毒,结构也很简单,适合小白分析练手。这篇分析也是我的一个练习项目,不但展示了分析结果,也重点阐述了分析过程中遇到问题时的解决思路,尽量做到详细、多图、无死角,希望能对同样是新手的读者一点帮助。


样本名称:MEMZ.exe

MD5:     19DBEC50735B5F2A72D4199C4E184960

SHA1:    6FED7732F7CB6F59743795B2AB154A3676F4C822

实验平台:Windows 7 Ultimate With Service Pack 1

分析工具:PEiD v0.95、StudyPE x64 v1.11、IDA x32 v7.4、Bochs v2.6.9、VMWare WorkStation v15.56、Olly ICE v1.10

拿到样本不要一来就拖进IDA和OllyDbg,在没有任何参考信息的情况下很容易迷失方向,应该先做最基本的观察,为之后的分析提供思路。特别是这次的彩虹猫样本,有很明显的用户能直接观察的特征。让我们先把病毒在虚拟机中运行起来。



双击病毒样本后接连弹出两个警告弹窗,希望用户明白运行病毒的风险。点击确定后,桌面会慢慢出现一些诡异的现象,比如:

自动弹出多个浏览器搜索窗口

鼠标异常晃动

窗口颜色怪异

反复出现系统提示音

出现6个MEMZ进程

这6个名为MEMZ的进程,用户尝试关闭任意一个,或者手动关闭计算机,都会遭至大量弹窗随后蓝屏,接着Windows无法正常启动,只会循环播放一只彩虹猫附带欢快的背景音乐,无论你重启多少次都是如此。运行观察结束后再用一些基本的可执行文件信息查看工具进行勘察。


首先上场的是PEiD,监测到该样本可能使用了ASProtect v1.32进行加壳或加密,可能会给本次分析带来一点小困难


接着使用StudyPE+查看样本的导入表,发现了很多有意思的函数,这些导入函数能给我们提供大量的信息,我把这些函数大致分为三类:

第一类:很明显能和观察到的现象直接联系

自动弹出多个浏览器搜索窗口----------------------ShellExecute

鼠标异常晃动--------------------------------------SetCursorPos、GetCursorPos

桌面出现奇怪图标---------------------------------DrawIcon

窗口颜色怪异--------------------------------------BitBlt、StretchBlt

反复出现系统提示音-------------------------------PlaySoundA

出现6个MEMZ进程--------------------------------ShellExecute

第二类:不能直接关联,但常成对出现完成某些功能

OpenProcessToken、AdjustTokenPrivilege、LookUpPrivilegeValue    给进程提权

GetMessage、TranslateMessage、DispatchMessage                           建立消息循环

CreateToolhelp32Snapshot、Process32First、Process32Next              遍历进程

SetWindowHookEx、UnhookWindowHookEx、CallNextHookEx         给窗口下钩子

LoadLibrary、GetProcAddress                                                               加载库并导入函数

第三类:不确定干了什么事或者我根本不熟悉

SendInput、GetSystemMetrics、DefWindowProc……


对于复杂的样本,还可以使用监控类工具看看样本对文件、进程、注册表、网络进行了哪些操作,不过这次样本比较简单,以上信息已经足够指导接下来的分析工作了。


使用IDA载入样本,定位到入口函数Start并按下F5获得伪代码,该功能极大地提升了样本分析的效率,毕竟看高级语言比看汇编还是快得多。但是这些伪代码只是IDA根据现有信息猜测出来的,并不能保证100%准确,有的时候甚至会对我们的分析工作产生误导,一会你就能看到。现在让我们继续回到IDA生成的伪代码

暂时别着急深入细节,先从整体把握走向。在start函数的起始部分调用GetCommandLineW、CommandLineToArgvW获取进程参数,之后整个Start函数就根据进程参数分成了几大块,整理后其结构如下


注意,这段体现start函数结构的伪代码并不是直接照搬自IDA,而是为了整体逻辑清晰而特意整理修改过的。接下来就要依次分析这三大部分,为了方便表述,这三个部分我称为“watchdog部分”、“/main部分”和“无参数部分” 。我们第一次双击样本时自然是不带参数的,所以我们先来看start函数的无参数部分。


此处先是用MessagexBox依次弹出两个警告信息提示框,这也正是我们运行病毒时所看到的。若两个弹窗都被用户确认,使用GetModuleFileNameW获取当前进程的路径并保存,当然所保存的空间由LocalAlloc提前申请。接着用do while执行5次循环,每次循环都调用一次ShellExecuteW,参数正是刚才得到的样本进程路径,以及字符串"/watchdog",即以“/watchdog”为参数,生成5个MEMZ.exe进程。


在最后调用了ShellExecuteExW以“/main”为参数生成了一个MEMZ.exe进程,一般带Ex后缀是加强版函数,表示在普通版函数基础上进行了功能的扩展,这里特意用了一个功能更强的函数一定有特别的目的。该函数接收一个SHELLEXECUTEINFOA结构体,结构体中设定了进程路径和执行参数,跟前面普通版函数传递的参数其实没什么区别。关键是紧接着调用了SetPriorityClass,当中对hProcess成员复制为0x80,查看MSDN得知该值意味着创建的进程将拥有最高的响应优先级,最后调用ExitProcess使当前进程结束。如果你不会使用MSDN我在文末备注有介绍。


现在我们知道在观察阶段看到的6个MEMZ.exe进程是怎么来的了:最开始双击生成了一个原始进程,原始进程生成了5个watchdog进程和1个/main进程,然后原始进程迅速结束了自己,等我们打开任务管理器查看时,还存在6个MEMZ.exe


IDA全称是交互式反汇编器(Interactive Disassembler),重点在于“交互式”,我们可以把手动分析后的任何想法和有用的信息都记录在IDA里,比如给IDA自动生成的变量和函数起一个有意义的名字,这些名字会在所有用到该变量或函数的地方生效。完成后应该像下图这样。


在后面的分析中,请大家自行完成注释的添加,为了后面的文章不剧透,我贴出来的图大部分还是未注释的状态。至此无参数部分分析完毕。

该部分的主体代码量很少,我一张图就全展示清楚了,但里面的坑可一点都不少。

首先是CreateThread创建一个线程,IDA给自动命名为sub_40114A。在这里给大家提一个醒,以watchdog为参数运行的MEMZ.exe进程是有5个的,也就是说这里看到的创建一个线程是“某个进程“的行为,5个进程一共应该创建了5个线程,在后面的分析中大家也要注意这一点。我们进入sub_40114A看看这个线程干了些什么事。

同样代码很少一张图概括完。前三个函数用来获取当前进程路径,顺便说一下这种多个函数配合起来做事的情况有很多,常常是最后一个函数完成关键功能,前面几个都是为它提供必要的参数,比如LocalAlloc申请空间用来存路径字符串,GetCurrentProcess获取当前进程句柄,这都是第三个函数要用到的。


接着一个大的while循环把后面的代码都包了进去,注意该while循环的条件永远为真,说明是个死循环。在循环内部使用CreateToolhelp32Snapshot给进程拍快照,再用Process32FirstW和Process32NextW进行遍历,这都是非常常见的操作,我们在观察阶段查看导入表时也发现了这三个函数,这里就用上了。


当然光做进程遍历是没意义的,肯定要干点活。每当遍历到一个进程时都要获取它的路径,并和我们之前获取的路径对比,如果相同就让计数器加1,很明显这就是在看当前有多少个MEMZ.exe的进程。当遍历完所有进程后,我们就统计出了MEMZ.exe的进程个数。然后最关键的地方来了,由于Sleep函数的存在,死循环每隔一段时间就能统计出当前MEMEZ.exe进程的个数并存放在v4变量中,然后先判断v4和v7的大小,再让v7保存v4的值。


如此就形成了一个监测机制,v7永远只保存v4最大的值,一旦v4的值小于了v7就会被if语句监测到,并进入sub_401021子函数。这不正好和我们观察阶段的发现一致吗?只要MEMZ.exe进程数量减少,系统就会蓝屏重启,这就是它的监测原理。反过来我们可以推测sub_401021就是完成系统蓝屏重启功能的。

前面进展得很顺利,到这里就碰到麻烦了。在这个子函数的开头是一个do while循环,循环次数为20次,用CreateThread创建线程,这没什么特别的,像往常一样我们双击StartAddress准备去看这个线程做了什么事,但是双击过后却没有任何反应,这其实是IDA的代码识别出现错误,相关问题我在第七节“IDA的一些问题做详细解释。

在这里为了不影响我们的分析主线,直接键盘按G输入地址4010FE就可以跳到StartAddress的代码。该处的MessageBoxA用于弹出消息,有26条消息保存在lpText所指向的地址中。使用sub_401A55获取随机数保存在v3,v3对0x1A取余以实现在26条消息中随机选取一条并显示。

而SetWindowsHookEx和UnhookWindowsHookEx用于给窗口下钩子,干了什么事要到回调函数fn里去看。code==3表明目标窗口即将被创建,此时lParam表示该窗口的基本信息(坐标、大小等),修改这些信息可以在窗口真正创建之前生效,下方正是在随机修改窗口的位置。这些信息都可以在MSDN中查到。


sub_401021的后半部分是两种关机的方式:先主动诱发蓝屏关机,如果不成功则主动退出Windows。


前一种方式使用LoadLibrary加载ntdll库,再连续两次调用GetProcAddress获取两个函数地址,这两个函数是未公开的Windows API,只能用这种方式来隐式调用。随后依次调用这两个函数,主动引发蓝屏。顺便提一下,很多恶意程序在隐式调用时会对函数名字符串先加密,要用的时候再解密,或者干脆不使用函数名而用函数编号,显然我们分析的样本没有做这种“隐蔽处理”。


后一种方式先用一系列函数给当前进程提权,然后调用ExitWindowsEx主动关闭Windows。为什么要先提权?调用ExitWindowsEx关闭Windows需要相应的权限,详情见MSDN。

总结一下,我们确认了之前的猜想,sub_401021确实是一个强制关机的函数,先创建线程用于弹出大量位置和内容都随机的窗口,再使用蓝屏或退出Windows的方式强制关闭计算机。

我们依次分析了函数开头->sub_40114A->sub_401021,已经陷入了太多细节,是时候回到watchdog的主体部分。这张图之前出现过,为了大家方便看我再次贴出来。在创建sub_40114A后,调用RegisterClassExA注册了一个名为“hax”的用户自定义窗口类型,并用CreateWindowExA将其创建。此处有个小问题,传递给RegisterClassExA函数的结构体变量pExecInfo定义为了SHELLEXECUTEINFOW类型,而不是该函数所需要的WNDCLASSEXA类型,这是IDA的识别出现了错误,为了不干扰主线分析,其讲解安排在了第七节”IDA的一些问题“。

创建的窗口有一个回调函数sub_401000。经查询MSDN,常量值16和22分别对应窗口消息WM_CLOSE和WM_ENDSESSION,那么整体含义就很明了了:该窗口回调函数会对窗口消息进行过滤,若消息为WM_CLOSE或者WM_ENDSESSION,则调用sub_401021强制关机(这个函数前面刚分析过)。而WM_CLOSE或WM_ENDSESSION消息是在系统关机时,由操作系统发送给各个窗口。


若是其他消息,则并不做任何处理,丢给系统默认处理函数DefWindowProcW。

如此一来,sub_401021这个强制关机函数在两处被调用。第一个是监测watchdog进程数量,如有减少就调用。第二个是监测用户是否主动关机,如有也调用。这和我们观察阶段看到的完全一致。

紧接着的代码GetMessage、TranslateMessage、DispatchMessage被包进一个大的while循环,这是常见的操作,叫做“消息循环”。由于在前面创建了窗口,并且还对发送给窗口的消息进行了过滤,意味着我们必须自己写消息循环完成收取消息和派发消息的工作,否则创建的窗口是收不到消息的。Windows并不会自动帮我们完成消息循环,这在MSDN里有明确解释,给出两条参考链接:


一篇关于消息循环的博客

https://www.cnblogs.com/zxjay/archive/2009/06/27/1512372.html


MSDN中关于创建消息循环的官方回答

https://docs.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues


由于开头部分代码和MBR有极强的联系,我索性把它划到MBR章节了,到时候再一块说。让我们把窗口往下拉一拉,这里有一个do while 循环,/main部分的分析就从这儿开始


此处的do while循环以v8为计数器,内部用CreateThread创建线程,循环跑10次(v8 < 0xA),一共创建10个线程。这不由地让人好奇,创建这么多线程是要做什么?先别慌跳进去看,它还附带一个参数v9,并被赋予初始值off_405130,每次循环自增2。以off开头表明是某个静态地址,我们双击过去看看。

off_405130地址所指向的是一块数据区,并且这里的数据还有非常强的规律性。每组都由1个dd类型的数据加4个db类型的数据组成,一共有10组,由于篇幅原因我只截取了部分组,为了方便大家观察,组与组之间我用了两种颜色隔开。


类似dd、db这样的符号在IDA里很常见,它们表示该地址所对应的数据大小,详细解释如下:

db:data byte       1字节数据

dw:data word     1字数据

dd:data dword    双字数据

dq:data qword    四字数据


1字 = 2字节

再回过头来看看前面的do while,v9被赋值为off_405130并作为参数传入。Sleep函数里也用到了v9,它将v9看作了数组首地址,并每次取数组的第1项(数组项数从0开始)。每次循环v9都会自增2,由于v9的数据类型是DWORD *,每次自增2就是自增两个DWORD*的大小,即8个字节。那么随着循环的进行,v9和v9[1]的值将会按如下所述变化:

v9的值指向的都是不同的静态地址,如果你挨个双击跳过去看,会发现它们其实都是函数地址。而v9[1]是Sleep函数的参数,所指向的肯定是时间量(毫秒)。v9是4个字节的DWORD * 类型,那它指向的当然就是4字节的DWORD类型,所以这些时间量都是4字节的DWORD类型。我们在IDA中右键单击地址就可以选择其解析长度,我们用4字节来解析它。


把所有时间量都这样操作,并在最后用右键选择数据展示的进制,选择10进制,毕竟我们更习惯用10进制来表示时间量。


所有步骤完成后,IDA应该显示为下图这样:

看到这样的结构,很容易让人想到病毒作者写代码时应该用了结构体数组,结构体的构成如下:

10个这样的结构体在一个数组中

现在我们明白了,创建了10个进程,并且每次创建都会有延迟时间,这个延迟时间和进程的函数地址是放在一起的,一共有10组,都在一个大数组off_405130里。

这个子函数的代码很短:用大数组中的函数地址依次创建线程。由于是函数指针,要用它调用函数当然得声明其参数、返回值、调用方式之类的,这都是C语言里很常见的用法。

这10个函数做的事很简单,我以sub_40156D为例。它调用GetCursorPos获取鼠标指针坐标,然后用各种方式修改这个坐标的值,比如这里调用sub_401A55生成随机数并返回,用这些随机数与鼠标指针坐标做运算,最后调用SetCursorPos来设置鼠标指针坐标,用户就会发现鼠标指针随机晃动了。所有这10个函数都干的类似的事,这里不再依次分析。

不过提醒一句,一些含有大量计算的部分,如果不是很重要,没必要完全了解算法细节。比如这个函数sub_00401AA0,它的参数是窗口名的字符串,进行的操作是把这些字符串前后颠来倒去,最后把打乱的字符串返回。你已经知道它就是为了打乱窗口名字符串,给这个函数起个合适的名字就可以先溜了,不用管它是怎么算的,如果后面有的分析必须要我们完全理解这个算法,再回过头来看也不迟。

当你把大数组里的函数分析完后,最好给他们一个统一的命名,我以MessUp为开头命名了这10个函数。

sub_401A2B大致做了什么我们清楚了,就是依次调用这10个函数。这里还有一个小细节,10个函数每个的返回值部分都有不同,有的是返回一个随机值,有的是返回一个固定值。返回值被保存在v1,每次循环都会在if语句中判断!v1--是否为真,也即v1自减1后是否为0,控制v1的值就能决定循环的次数,再配合后面的Sleep函数就能决定这10个函数的激活时间。这10个函数中,有的需要一个固定的激活时间,有的需要一个随机的激活时间,只需要控制返回值即v1就可以了。

终于到了最后的重头戏了,MBR部分完成了该样本的核心功能。


在继续分析之前,得要清楚什么是MBR。MBR全称主引导记录(Master Boot Record),整个硬盘最开头的512字节就是它。计算机启动后会先运行MBR里的代码进行各种状态的检查和初始化的工作,然后再把控制权转交给操作系统(简单地讲就是一个JMP指令跳到操作系统的起始代码),Windows就加载启动了。


MEMZ病毒干了一件很可恶的事,它直接把整个MBR覆盖掉了,变成了它自己的代码,那么它想干什么都行了,只要它不主动交出代码执行流程,Windows绝没有启动的机会。

MBR里既有代码也有数据,开头的0-446字节是代码,紧接着就是数据,数据部分记录着硬盘的分区信息,结尾以固定的0x55 0xAA作为结束符,如果你了解过PE文件结构,应该对这种做法不陌生。

对MBR有个初步了解后,我们回头来看代码。现在先回到/main未分析的部分,我们前面提到过,它和MBR有紧密关联。


首先使用CreateFileA以文件形式打开了主硬盘,也即PhysicalDrive0,这样方便后续的覆盖操作。紧接着用LocalAlloc分配一段以0为初始值的内存空间,并拷贝两段神秘数据到分配的内存空间。这两段数据中第一段byte_402118大小304字节,第二段byte_402248大小1952字节,拷贝时中间跳过了206字节。最后将内存空间的数据覆盖到主硬盘(PhysicalDrive0)的开头部位,原始MBR遭到破坏。


第一段正好就覆盖了MBR的代码区域,所以几乎可以肯定这304字节是代码。第二段数据已经不属于MBR的范围,而且高达1952字节,很可能是用于显示动画的图像数据和音频数据。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2021-4-10 16:42 被谁的狗哥编辑 ,原因: 修改bochs读音
上传的附件:
收藏
免费 21
支持
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  大煞笔   +1.00 2020/10/23 不错不错
最新回复 (13)
雪    币: 148
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
太强了,这就是带佬吗
2020-10-24 17:23
0
雪    币: 405
活跃值: (2280)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
1,彩虹猫是用自己的数据覆盖,没有备份,也不准备还原,所以不存在修复一说。只能自己用工具,搜索恢复分区表。
2,搞清楚bios-mbr和UEFI-GPT的区别,完全2套不同体系。
2020-10-26 10:37
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
收藏了!感谢提供!
2020-10-26 16:21
0
雪    币: 588
活跃值: (367)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这个病毒还挺有意思的,哈哈哈哈
2020-12-28 19:59
0
雪    币: 588
活跃值: (367)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
为啥你发了帖还没有转正
2020-12-28 20:00
0
雪    币: 300
活跃值: (2472)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
nice
2020-12-28 21:37
0
雪    币: 59
活跃值: (178)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
太厉害了
2021-2-18 12:04
0
雪    币: 16
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
狗哥 MBR 使用 bochsrc 这一步可以详细,说下嘛,老是失败,这块复现不了。
2021-3-13 17:08
0
雪    币: 16
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
大家在实验MBR的时候在内存里把1000h字节完全提取出来,,别用文本那个,我自己提取成功运行。
2021-3-13 18:59
0
雪    币: 307
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
我们用同一个款压缩软件哈哈
2021-12-28 16:44
0
雪    币: 243
活跃值: (3311)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
病毒摧毁系统的时间很短,怎么相对完整的收集到样本行为呢
2022-11-30 23:35
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
刚开始学习exe逆向,尝试复现了下,在分析过程中发现无参数部分ShellWxecuteExW函数应该是接受了一个SHELLEXECUTEINFOW结构体,而不是接受了一个SHELLEXECUTEINFOA结构体
2023-7-10 13:35
0
游客
登录 | 注册 方可回帖
返回
//