Ramnit感染病毒分析报告

Ramnit感染病毒分析报告 简介 本文主要涵盖对Ramnit家族的3个变种(Ramnit.X,Ramnit.AS和Ramnit.A)的分析以及修复方案。其中Ramnit.X变种和Ramnit.AS变种

Ramnit感染病毒分析报告

Ramnit感染病毒分析报告

简介

本文主要涵盖对Ramnit家族的3个变种(Ramnit.X,Ramnit.AS和Ramnit.A)的分析以及修复方案。其中Ramnit.X变种和Ramnit.AS变种感染文件的方式与之前分析的06—感染型病毒是一样的,所以本文将以Ramnit.A为主要分析对象来进行说明,并会说明Ramnit.A与Ramnit.X变种和Ramnit.AS变种的不同点。由于本文主要是对Ramnit家族的分析与修复方案,将不会特别细致的阐述每一个点,若想看更加详细的分析或想看Ramnit.X变种和Ramnit.AS变种的详细分析,可看Ramnit感染病毒分析报告02。

基本信息

Ramnit变种名

感染母体文件

文件对应MD5

Ramnit.A

WaterMark.exe

BA4610E9CA3EBF61EC5800955A797C13

Ramnit.X,AS

DesktopLayer.exe

FF5E1F27193CE51EEC318714EF038BEF

样本详细分析病毒主体复制自身运行病毒样本后,会检索当前文件的路径,并判断该目录是否是C:\Program File\Microsoft\,若不是便将自身复制到该目录下,并执行该目录下的病毒母体文件,然后退出当前进程。代码如图:

Ramnit.A变种与 Ramnit.X,AS 变种的不同点在于释放的感染母体文件不同,Ramnit.A变种释放名为WaterMark.exe, Ramnit.X,AS 变种释放名为 DesktopLayer.exe 。

inline Hook先将出主线程外的其他线程挂起,再对ntdll模块中的函数ZwWriteVirtualMemory进行Hook,如图:

inline Hook的实现过程,如图:

创建傀儡进程由于之前Hook了函数ZwWriteVirtualMemory,恶意样本通过调用CreateProcess创建进程时,执行恶意样本自己写的Hookmain函数。

Hookmain函数主要行为是:往子进程中远程开辟虚拟空间,将PE文件展开并写入远程开辟的虚拟空间中,还写入3了个函数(修复IAT函数,修改区段属性函数,主行为逻辑函数),然后再修改子进程的入口点,从而创建傀儡进程,代码如图:

Ramnit.A变种与 Ramnit.X,AS 变种的不同点

Ramnit.A变种

Ramnit.A变种Hook了两次ZwWriteVirtualMemory(分别进行了脱钩),并创建了两个Svchost.exe的傀儡进程,且注入的PE文件不是同一个文件,其中一个为感染傀儡进程,另一个简单看了一下维持进程当互斥体不在时,便遍历进程并注入所有进程。

Ramnit.X,AS 变种

Ramnit.X,AS 变种只Hook了一次,创建了一个浏览器(IE、Chrome等)傀儡进程。

感染傀儡进程Ramnit.A变种创建了两个进程,其中只有一个是进行感染的,并且与Ramnit.X,AS 变种差不太多。

感染傀儡进程主要行为:

创建线程1,将感染母体文件(WaterMark.exe或DesktopLayer.exe)添加到注册表Winlogon表中的userinit项中,使其开机自启。

创建线程2,与google、bing、Yahoo进行连接,获取时间,来记录样本运行了多长时间。

创建线程3,将线程2中获取的样本运行的时间,记录到文件dmlconf.dat 来记录样本运行了多长时间。

创建线程4,进行网络通信,按照一定时间,将获取到的数据发送到C&C服务器 ==poopthree.com== 或 ==fget-career.com==。

感染PE文件和网页文件。

感染傀儡进程的主要行为代码,如下图:

添加自启动项将感染母体文件(WaterMark.exe或DesktopLayer.exe)添加到注册表Winlogon表中的userinit项中,使其开机自启。代码如图:

加工感染数据先读去感染母体的PE文件数据,然后根据函数GetTickCount获取到的自操作系统启动后的时间作为密钥,来加密感染母体PE文件数据,然后在将VBscript与加密后的感染母体PE文件进行拼接。代码如图:

感染固定介质遍历固定介质(硬盘),当磁盘大小大于512K时,开始遍历磁盘文件,通过匹配文件后缀名的方式,来判断要感染的文件。Ramnit.X,AS 变种和Ramnit.A变种都只感染4种文件后缀的文件,分别为:==.exe,.dll,.htm,.html==文件,如图:

感染PE文件判断感染标志感染==.exe和.dll==文件,先判断感染标志是否存在。

Ramnit.A变种的感染标记为文件附加数据的最后24个字节,通过将最后24个字节每4个字节与第一个4字节进行异或,然后与特定的值进行比较。将最后24个字节进行异或解密,如图:

与特定值进行比较,来判断文件是否被感染,如图:

Ramnit.X,AS 变种的感染标记,遍历节表,查看文件是否有rmnet节,来判断文件是否被感染。

判断是否感染判断目标文件是否是32位程序,是否是.net,是否有签名,当不是32位程序,不是.net,没有签名时,便不感染,代码如图:

遍历目标文件OFTs表遍历目标文件的OFts表,获取LoadLibrary和GetProcAddr函数的FTs(IAT)的RVA,以及遍历导入表获取模块名为kernel32.dll的导入表位置,然后将获取到的RVA存到全局变量中,后面会跟着写入函数一同写到被感染文件中,如图:

添加新节添加新区段,并设置感染区段的可读可写可执行属性,代码如图:

Ramnit.A变种添加的区段名为.text。

Ramnit.X,AS 变种添加的区段名为.rmnet。

修改OEP先获取到目标文件的OEP,然后将==添加的新节RVA与OEP的差值存储在全局变量==中,后面会写入被感染的文件中,在将添加的新节的VA设置为新的OEP,代码如图:

Ramnit.A变种的OEP存储在感染区段偏移为 ==0x771== 的地址处。

Ramnit.X,AS 变种的OEP存储在感染区段偏移为 ==0x328== 的地址处。

写入加密后的感染母体及解密函数由于之前将感染母体PE进行了加密处理,所以在感染的时候还需要将==解密函数==同==加密后的感染母体文件==同时写入被感染文件中去。下图中进行了两次写入,分别是写入解密函数,和加密后的感染母体文件(WaterMark或DesktopLayer),如图:

写入密钥和感染标志这里Ramnit.A变种就与 Ramnit.X,AS 变种不太一样了,Ramnit.X,AS 变种并没有这些操作。

之前在加工感染数据时提到过,Ramnit.A变种根据获取到的自操作系统启动的时间作为密钥,来加密感染母体PE文件,因此,Ramnit.A变种便将密钥经过加工写入被感染文件的附加数据中,也还会写入Ramnit.A变种的感染标志(文件末尾24字节),代码如图:

修复文件修复文件校验和,文件时间以及文件属性,代码如图:

Ramnit.A变种会修复文件校验、修改时间和文件属性

Ramnit.X,AS 变种只会修复文件校验和(但好像修复不成功)

感染网页文件判断感染标志Ramnit.A变种的网页文件的感染标志与PE文件差不多,唯一区别为PE文件是直接读取最后24字节,而网页文件读取文件末尾倒数第27字节往后的24字节,其他的都一样,代码如图:

Ramnit.X,AS 变种的网页感染标志为文件末尾的9个字节是否是

写入感染脚本Ramnit.A变种先写入感染脚本,再写入解密感染母体需要的密钥和感染标志,代码如图:

Ramnit.X,AS 变种就相对简单,直接写入感染脚本

修复文件Ramnit.A变种会修复修改时间和文件属性,代码如图:

Ramnit.X,AS 变种对与网页文件并没有任何修复操作。

感染可移动介质Ramnit.A变种和Ramnit.X,AS 变种对于可移动介质的感染操作,都是将恶意PE写入可移动介质的==autorun.inf==文件中,使当可移动介质插入计算机时自启动。

修复分析修复目标:

判断当前操作系统感染类型

关闭创建的傀儡进程svchost(Ramnit.A变种)和默认浏览器IE,chrome等(Ramnit.X,AS 变种),防止边修复遍感染。

删除感染母体文件,文件路径为C:\Program File\Microsoft\,目录下的感染母体文件WaterMark(Ramnit.A变种)或是DesktopLayer(Ramnit.X,AS 变种)

删除自启动项,修改注册表HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon

根据当前感染类型,修复.exe文件、.dll文件、.htm文件、.html文件

文件修复判断文件是否被感染Ramnit.A变种对于.exe和.dll文件根据文件末尾最后24字节与特定特征去比较,对于.htm和.html文件根据文件倒数第27字节读取到的24字节与特征去比较,本人写的判断Ramnit.A变种是否被感染的代码如下:

123456789101112131415161718192021222324252627282930313233343536373839404142//判断文件末尾的感染特征 返回true,说明该文件被感染//flag = 0 为PE判断//flag = 3 为网页判断BOOL JudgeCharacteristic(char filename[MAX_PATH], int flag){ DWORD Buffer[30]; HANDLE hfile; DWORD NumberOfBytesRead; DWORD FileSizeHigh; DWORD characteristic = 0xFA1BC352; hfile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, 3u, 0x80u, 0); if (hfile != INVALID_HANDLE_VALUE) { DWORD filesize = GetFileSize(hfile, &FileSizeHigh); if (filesize > flag + 24 && filesize != -1 && !FileSizeHigh) { SetFilePointer(hfile, filesize - (flag + 0x24), 0, 0); ReadFile(hfile, Buffer, 0x24, &NumberOfBytesRead, 0); CloseHandle(hfile); int j = 9; for (DWORD i = Buffer[0];; Buffer[j] ^= i) { --j; if (!(j * 4)) break; } if (Buffer[1] == characteristic && Buffer[2] == 5 && Buffer[3] == 0 && Buffer[4] == 0x0D) return true; //返回true,说明该文件被感染 else return false; //返回false,说明该文件没有被感染,或是不属于该文件类型 } else { printf("打开文件失败!"); CloseHandle(hfile); return false; //文件大小不满足条件,没有被感染 } } else return false; //文件打开失败}

Ramnit.X,AS 变种的判断逻辑相对简单,对于.exe和.dll文件遍历PE文件的节表,查找是否有.rmnet节,若有便是被感染的,对于.htm和.html文件,则匹配最后9个字节的数据,本人写的代码(没有封装好)如下:

1234567891011121314151617181920212223242526对于.exe和.dll文件//判断.rmnet节表是否存在 //返回1,说明rmnet节存在//返回0,说明rmnet节不存在BOOL JudgeSection(){ char* sectionName = (char*)sectionHeader[numOfSection - 1].Name; if (strcmp(sectionName, ".rmnet") == 0) { return 1; //返回1,说明rmnet节表存在 } return 0; //返回0,说明节表不存在}对于.htm和.html文件 FILE* fp;BOOL JudgeHtm(){ char buffer[15] = { 0 }; fopen_s(&fp,fileName, "r+"); fseek(fp, -14, SEEK_END); fread(buffer, 14, 1, fp); if (strcmp(buffer, "//-->") == 0) return 1; //返回1,说明感染类型为Ramnit.x或Ramnit.AS else return 0; //返回3,该文件没有被感染}

获取原始OEP根据Ramnit.A变种的OEP存储在感染区段偏移为 ==0x771== 的地址处。Ramnit.X,AS 变种的OEP存储在感染区段偏移为 ==0x328== 的地址处。

代码如下:

123456789101112131415161718192021222324Ramnit.X,AS 变种的OEPvoid GetOEP(){ //声明 DWORD median; DWORD RAW; RAW = sectionHeader[numOfSection - 1].PointerToRawData; median = *(PDWORD)((PBYTE)dosHeader + RAW + 0x328); OEP = optionalHeader->AddressOfEntryPoint - median; printf("|成功获取到原始OEP:%x\n",OEP);}Ramnit.A变种的OEP void GetOEP(){ //声明 DWORD median; DWORD RAW; RAW = sectionHeader[numOfSection - 1].PointerToRawData; median = *(PDWORD)((PBYTE)dosHeader + RAW + 0x771); OEP = optionalHeader->AddressOfEntryPoint - median; printf("|成功获取到原始OEP:%x\n",OEP);}

设置感染标记对于Ramnit.A变种,在文件末尾添加任意被感染文件末尾24字节的感染标记即可。

1EF DA DA 59 BD 19 C1 A3 EA DA DA 59 EF DA DA 59 E2 DA DA 59 3D EF 0C 15 65 9B 0F 04 8D D4 DF 59 EA DA DA 59

对于Ramnit.X,AS 变种,添加.rmnet节表

删除感染节表删除感染节表

123456789101112//删除感染节表void FixSection(){ //删除多余节表数据 optionalHeader->SizeOfImage -= sectionHeader[numOfSection - 1].Misc.VirtualSize; //修正sizeofimage memset(sectionHeader[numOfSection - 1].PointerToRawData + (PBYTE)dosHeader, 0, sectionHeader[numOfSection - 1].SizeOfRawData); //修正DOS头 fileHeader->NumberOfSections--; //节表个数-1 memset(§ionHeader[numOfSection - 1], 0, 0x28); //将最后一个节的数据全置0 printf("|成功修复多余节表!\n");}

删除感染数据删除感染PE数据Ramnit.A变种和Ramnit.X,AS 变种删除.exe和.dll文件都可用如下方法删除感染数据

1234567void deldata(){ DWORD Distance = (sectionHeader[numOfSection - 2].SizeOfRawData + sectionHeader[numOfSection - 2].PointerToRawData); HANDLE hFile1 = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); SetFilePointer(hFile1, Distance, 0, FILE_BEGIN); SetEndOfFile(hFile1);}

删除感染网页数据删除网页的感染数据稍微不同,因为Ramnit.A变种网页数据后面还有随机长度的密钥以及感染标志,因此删除网页感染数据代码如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546//修复Ramnit.Avoid repairHtml2(char filepath[MAX_PATH]){ char Buffer[600]; HANDLE hfile; DWORD NumberOfBytesRead; SetFileAttributesA(filepath, FILE_ATTRIBUTE_NORMAL); hfile = CreateFileA(filepath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hfile != INVALID_HANDLE_VALUE) { DWORD filesize = GetFileSize(hfile, 0); if (filesize != -1) { SetFilePointer(hfile, filesize - 539, 0, 0); //539为附加随机数据的最大长度 ReadFile(hfile, Buffer, 539, &NumberOfBytesRead, 0); //读取文件末尾539字节的数据到buffer中 const char strcharacteristic[20] = "