服务热线
15527777548/18696195380
发布时间:2022-04-18
简要描述:
概述该样本是来自于VirusShare网站提供,于2022年3月15日在VirusTotal上被首次提交。拿到样本时只有一个dll文件,查看其资源节,发现有“XHtmlTreeTest.exe”等字符串信息,经网上...
该样本是来自于VirusShare网站提供,于2022年3月15日在VirusTotal上被首次提交。拿到样本时只有一个dll文件,查看其资源节,发现有“XHtmlTreeTest.exe”等字符串信息,经网上搜索后怀疑其是在一个叫XHtmlTreeTest的XML和HTML解析工具上附带的木马dll。
从该木马dll(因为该dll的原本名字未知,就先怎么称呼了)的DllMain函数开始分析,其通过sub_1007B390函数获取“kernel32.dll”、“ntdll.dll”,以及“msvcrt.dll”三个dll库的基址。该函数访问进程的ldr链表,遍历其中所有的导入库,并通过访问 LDR_DATA_TABLE_ENTRY 结构体中的DllBase可以获取到dll的基址。
GetProcAddress2 函数是我自己重命名的,其作用就是通过访问前面获得的导入库基址,然后访问其对应的导出表获得目标函数地址。其中函数最后一个参数就是hash化的函数名,上面sub_1007B390函数的dll库名同样hash化了。此外,该木马dll中DllMain函数里调用的几个自定义函数都加入了大量的花指令混淆,如下图中的 GetProcAddress2 函数所示:
花指令的特征:加或减 N*dword_100ABxxx 和 N*dword_100ABxxx << M。
发现访问了资源节,用ResourceHack打开看看,发现一个语言未知的项里保存了一个长达0x24000字节的二进制数据。用od调试一下这个dll,该dll获取到该资源后,申请了一个0x24000的空间,并通过memcpy函数拷贝资源的内容到该空间。发现拷贝的内容就是ResourceHack看到的二进制数据。
跳过一大堆CreateWindowExW函数后,sub_10072050函数使用密钥”D#PSqwp>oy@S#7dEfvT1uh?XI5X<$kuIz5qEdsD#GbBoy6I+fTJmEH$sG%kSLP_6m“进行扩展。sub_100707E0函数对该资源数据进行解密,此时会发现内存里多了一个叫”Y.dll“的模块。
对sub_100707E0函数去花后,加密代码如下:
unsigned int __cdecl sub_100707E0(int a1, int a2, unsigned int a3) { v11 = 0; v7 = 0; for ( i = 0; i < a3; ++i ) { v11 = ( + v11 + 1) % 21765; v7 = (v7 + *(unsigned __int8 *)(a1 + v11)) % 21765; v10 = *(_BYTE *)(a1 + v11); *(_BYTE *)(a1 + v11) = *(_BYTE *)(a1 + v7); v4 = v7; *(_BYTE *)(a1 + v4) = v10; v5 = (*(unsigned __int8 *)(a1 + v7) + *(unsigned __int8 *)(a1 + v11)) % 21765; v6 = v5; v8 = v6; LOBYTE(v4) = *(_BYTE *)(a1 + v8); *(_BYTE *)(a2 + i) ^= v4; result = i + 1; } return result; }
对sub_10070E70函数中仅调用sub_1007BE90函数,sub_1007BE90函数去花后,分析可知其主要是记录前面获得的几个函数地址以及计算出DllRegisterServer函数的地址,其代码如下:
DWORD *__cdecl sub_1007BE90(unsigned __int16 *a1, int a2, int (__cdecl *a3)(int, int, int, int, int), void (__cdecl *a4)(int, _DWORD, int, int), int a5, int a6, int a7, int a8) { v30 = 0; if ( !sub_100701B0( a2 , 64) ) return 0; v31 = a1; if ( *a1 != 23117) return 0; if ( !sub_100701B0( a2 , *((_DWORD *)v31 + 15) + 248 ) ) return 0; v22 = (char *)a1 + *((_DWORD *)v31 + 15); if ( *(_DWORD *)v22 != 17744 ) return 0; if ( *((unsigned __int16 *)v22 + 2) != 332 ) return 0; if ( (*((_DWORD *)v22 + 14) & ( 1 != 0 ) return 0; v9 = (int)&v22[ 24 + *((unsigned __int16 *)v22 + 10) ]; v21 = v9 ; v27 = *((_DWORD *)v22 + 14); v32 = 0; while ( v32 < (unsigned int)*((unsigned __int16 *)v22 + 3) ) { if ( *(_DWORD *)(v21 + 16) ) v20 = *(_DWORD *)(v21 + 16) + *(_DWORD *)(v21 + 12) ; else v20 = v27 + *(_DWORD *)(v21 + 12) ; if ( v20 > v30 ) v30 = v20 ; ++v32; v21 += 40; } v10 = &v25[0]; kernel32_GetNativeSystemInfo(&v10[0]); v28 = sub_100703D0( *((_DWORD *)v22 + 20), v26 ) ; v11 = sub_100703D0( v30 , v26 ); if ( v28 != v11 ) return 0; v18 = 4 ; v12 = 0x2000 ; v29 = VirtualAlloc( *((_DWORD *)v22 + 13) , v28, 4096 ) | v12, v18, a8); if ( !v29 ) { v19 = 4 ; v13 = 0x2000 ; v29 = VirtualAlloc( 0, v28 , 4096 ) | ( v13 ), v19, a8); if ( !v29 ) return 0; } v14 = kernel32_GetProcessHeap( 8 , 64 ); v15 = ntdll_RtlAllocateHeap(v14); v16 = v15 ; v24 = (_DWORD *)( v16 ); if ( v24 ) { v24[1] = v29; v24[5] = ( 0x2000 ) & *((unsigned __int16 *)v22 + 11)) != 0; v24[7] = a3; v24[8] = VirtualFree; v24[9] = a5; v24[10] = a6; v24[11] = a7; v24[13] = a8; v24[15] = v26 ; if ( sub_100701B0( a2 , *((_DWORD *)v22 + 21) ) && (v33 = a3( v29, *((_DWORD *)v22 + 21) , 4096 , 4 , a8), msvcrt_memcpy(v33,v31,*((_DWORD *)v22 + 21)),*v24 = v33 + *((_DWORD *)v31 + 15),*(_DWORD *)(*v24 + 52) = + v29, sub_10076230(a1,a2,v22,&v24[0]))&& ((v23 =*(_DWORD *)(*v24 + 52) - *((_DWORD *)v22 + 13)) == 0 ? (v24[6] = 1) : (v17 = sub_10074F00(&v24[0],v23),v24[6] = v17), sub_1006D090(&v24[0]) && sub_10079BD0(&v24[0]) && sub_1006E5C0(&v24[0])) ) { if ( *(_DWORD *)(*v24 + 40) ) { if ( v24[5] ) { dword_100AE7E0 = (int (__stdcall *)(_DWORD, _DWORD, _DWORD))(v29); v24[4] = 1; } else { v24[14] = *(_DWORD *)(*v24 + 40) + v29; } } else { v24[14] = 0; } result = v24; } else { sub_1006BFA0(v24); result = 0; } } else { VirtualFree(v29,0,0x8000,a8); result = 0; } return result; }
该木马dll只导出了两个函数,分别是DllRegisterServer函数和DllUnregisterServer函数。该木马dll的DllRegisterServer函数会去调用Y.dll的DllRegisterServer函数。sub_10073000函数的作用就是寻找Y.dll中DllRegisterServer函数的地址。
使用ollydump把Y.dll从内存中dump下来,用ida静态分析一下。Y.dll中导出函数只有一个DllRegisterServer函数,一个导入函数都没有,看来所有的导入函数都是动态获得的。DllRegisterServer函数里有两个函数,分别是sub_10021D63和sub_10004587。sub_10021D63函数明显是进行了控制流混淆,里面通过各种hash计算,套利多个switch分发。分析了部分分支的逻辑,发现其最后都会通过回调的形式访问Windows API,汇编中表现为”call eax“。所以,想到对Y.dll模块中所有的”call eax“指令下断点。又因为这些分支太多了,一个个下不实际,所以利用ollyscript来下断点。
所有分支的底层函数示例如下:
分享一下od下断脚本:
var target var add1 var add2 var count ask "输入hex,支持??,hex必须用##,比如查找#B8??00000001db#" mov target,$RESULT mov add1,eip start: mov add2,add1 mov $RESULT,target find add2,$RESULT cmp $RESULT,0 je over add count,1 mov add1,$RESULT add add1,1 bp $RESULT jne start over: add count,"次下断" msg count ret
经过调试发现,其通过 SHFileOperationW 函数在”C:\Windows\SysWOW64“目录下生成一个基于系统时间随机生成名字的子目录,并往该目录创建一个同样随机命名的文件。
这里说一下 SHFileOperationW 函数的作用,如下:
其中SHFILEOPSTRUCTA定义如下:
typedef struct _SHFILEOPSTRUCTA { HWND hwnd; UINT wFunc; PCZZSTR pFrom; PCZZSTR pTo; FILEOP_FLAGS fFlags; BOOL fAnyOperationsAborted; LPVOID hNameMappings; PCSTR lpszProgressTitle; } SHFILEOPSTRUCTA, *LPSHFILEOPSTRUCTA;
在od调试时断下,发现有一次 SHFILEOPSTRUCTA结构体中的pFrom指向木马dll的文件路径(这里我重命名该木马dll为virus.dll),pTo则是指向如上图所示的新路径。
通过CreateProcessW函数调用Regsvr32.exe以安静模式运行将上面创建的文件写入注册表。因为Regsvr32常用于dll注册,那么可以知道其目的就是换个文件名把 该木马dll注册到操作系统里。因为该木马dll是32位的,所以其选择了”C:\Windows\SysWOW64“目录。
image-20220319175122377
不过我在分析时每次调用 SHFileOperationW 去创建文件的时候都失败了,以管理员权限运行也还是一样,不知道是什么原因。进程在反复调用几次SHFileOperationW 失败后就会终止。用rundll32去加载运行,用procmon去观察结果,依然是如此。也尝试过把后缀改为.exe,其结果是无法运行。因为不想硬肝那个控制流混淆,所以分析就到此为止了。
文件名19c5b13e2635be7d9931a404ccbbce7015ea2414cc12d24fd27a2ab8ad7bbe3bMD501d91a225f23340268641f2a88eddf7fSHA1ec385cf0d5dc90bc2b27d31d217356271368030eSHA25619c5b13e2635be7d9931a404ccbbce7015ea2414cc12d24fd27a2ab8ad7bbe3b文件大小1,020,928 bytes文件类型PE32 executable (DLL) (GUI) Intel 80386, for MS Windows发现时间2022-03-15 02:07:01 UTC
如果您有任何问题,请跟我们联系!
联系我们