The duties of a professional virus analyst areProvide strong back-end support for the security operations team, and be able to deal with sudden large-scale infections and ransomware.
The following is a virus repair tool written by Virut. The tool does not traverse the files, but repairs a file in the C drive after being infected by a virus: hypertrm.exe.V. Those who are interested can study and exchange.
// VirutRemoveTool.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#define BYTELENGTH 1024
int RestorePE(LPCWSTR lpFileName);
DWORD Convert(wchar_t *lpBase, DWORD dwAddr, BOOL bFile2RVA);
int _tmain(int argc, _TCHAR* argv[])
{
RestorePE(L"C:\\hypertrm.exe.V");
getchar();
return 0;
}
int RestorePE(LPCWSTR lpFileName)
{
int i,j= 0;
int m = 0;
HANDLE handle;//新文件句柄
DWORD RandomKey = 0;
DWORD tail=0;
DWORD RSRCSIZE, RSRC=0;
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = GetFileSize(hFile, NULL);
wchar_t *pFile = new wchar_t[dwFileSize];//我们后续操作的全部都是这儿的读取出来的缓冲数据,最后再写入到文件中。
DWORD dwRubbish = 0;
ReadFile(hFile, pFile, dwFileSize, &dwRubbish, NULL);
if (((PIMAGE_DOS_HEADER)pFile)->e_magic != IMAGE_DOS_SIGNATURE)
return -1;//不是DOS头,返回
DWORD dwNewPos = (DWORD)pFile + ((PIMAGE_DOS_HEADER)pFile)->e_lfanew;
PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)(dwNewPos);
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
return -1;//不是NT头,说明不是PE文件,返回
PIMAGE_FILE_HEADER pFileHeader = &(pNTHeader->FileHeader);
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = &(pNTHeader->OptionalHeader);
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNTHeader);
while (j < pFileHeader->NumberOfSections)
{
//pSectionHeader = pSectionHeader+pFileHeader->NumberOfSections-1;//倒着输出
WCHAR Tag[8] = {
0 };//区段名字是个 BYTE [8]类名的字符串,需要转换成一个宽字符
MultiByteToWideChar(CP_ACP, 0, (LPCCH)pSectionHeader->Name, 8, Tag, 8);//转换为宽字符存储在Tag数组中
DWORD buffer1 = (DWORD)pSectionHeader - (DWORD)pFile;//!!!找到感染区段的物理偏移
if (!strcmp(".rsrc", (LPCCH)pSectionHeader->Name))
{
RSRCSIZE = pSectionHeader->SizeOfRawData;
RSRC = pSectionHeader->PointerToRawData;
//还需要获取Misc中的虚拟地址大小
}
if (strlen((const char *)pSectionHeader->Name) == 7 && pSectionHeader->SizeOfRawData == 0) //如果不是const char * 它的长度恒为8
{
printf("\n文件被感染,正在修复..........");
printf("标志区段:%s,物理偏移:%x", pSectionHeader->Name, buffer1);
//开始修复
//从感染区段截成两段重新写入,彻底消除异常区段信息。
if (buffer1 != NULL)
{
DWORD haveread = 0;
DWORD RealEntry;
BYTE *pEntry = new BYTE[BYTELENGTH]; //后期要改下稳定的
BYTE *prsrc = new BYTE[RSRCSIZE]; //0x15000
pFileHeader->NumberOfSections = pFileHeader->NumberOfSections - 1;//区段数减去1
memset(pSectionHeader, 0, 40); //感染区段信息填零
DWORD FOAEntry = Convert(pFile, pOptionalHeader->AddressOfEntryPoint, 0);//计算入口点的FOA
//用F0A找出偏移,用RVA地址计算!!!!!!!!!!!!
printf("\n原始文件入口位置点(FOA):%x", FOAEntry);
SetFilePointer(hFile, FOAEntry, 0, FILE_BEGIN); //从入口点处向后遍历1000字节寻找81 C5 add esp,xxx
ReadFile(hFile, pEntry, BYTELENGTH, &haveread, NULL);
for (i = 0; i < BYTELENGTH; i++)
{
if (pEntry[i] == 0xE8)
{
printf("\n第一个call:0x%x", i + 5);
break;
}
}
RealEntry = i + 5 + pOptionalHeader->AddressOfEntryPoint; //实际计算时必须用RVA地址!!!!!!!!!!!!
SetFilePointer(hFile, RSRC, 0, FILE_BEGIN);
ReadFile(hFile, prsrc, RSRCSIZE, &haveread, NULL); //读取整个资源节
/*
for (i = 0; i < BYTELENGTH; i++)
{
if (pEntry[i] == 0x81 && pEntry[i+1] == 0xC5)
{
for (m = 0; m < 4; m++)
printf("\n%x", pEntry[i+2+m]);
for (m = 3; m > -1; m--)
RandomKey = pEntry[i + 2 + m] + RandomKey * 0x100;
break;
}
}
*/
for (i = 0; i < RSRCSIZE; i++)
{
if (prsrc[i] == 0x81 && prsrc[i + 1] == 0xC5)
{
printf("\n%x", prsrc[i + 5]);
if (prsrc[i + 5] == 0xFF)
{
for (m = 0; m < 4; m++)
printf("\n%x", prsrc[i + 2 + m]);
for (m = 3; m > -1; m--)
RandomKey = prsrc[i + 2 + m] + RandomKey * 0x100;
break;
}
}
}
printf("\nRandomKey:%x", RandomKey);
RealEntry += RandomKey;
printf("\n原始文件入口点:%x", RealEntry);
pOptionalHeader->AddressOfEntryPoint = RealEntry; //入口点修复
//SetFilePointer(hFile, RSRC, 0, FILE_BEGIN);
//ReadFile(hFile, prsrc, RSRCSIZE, &haveread, NULL); //读取整个资源节
for (i = (FOAEntry - RSRC); i > 0; i--)
{
//向前遍历整个资源节
if (prsrc[i] == 0x0 || prsrc[i] == 0x47)//优化速度
if ((prsrc[i] == 0x0 && prsrc[i - 1] == 0x0 && prsrc[i - 2] == 0x0 && prsrc[i - 3] == 0x0 && prsrc[i - 4] == 0x0&&
prsrc[i - 5] == 0x0) || //连续6个0
( prsrc[i] == 0x47 && prsrc[i - 1] == 0x4E && prsrc[i - 2] == 0x49 && prsrc[i - 3] == 0x44
&& prsrc[i - 4] == 0x44 && prsrc[i - 5] == 0x41 && prsrc[i - 6] == 0x50)) //PADDINGX
{
printf("\n匹配到原始资源末尾:%x", i); //相对于资源段开始的位置偏移,即是资源段实际大小
tail = i;
break;
}
}
printf("\n向上物理大小矫正:%x", ((tail/0x200)+1)*0x200);
tail = ((tail/0x200)+1) * 0x200;
DWORD virussize = RSRCSIZE - tail; //大约是7000H
printf("\n虚拟大小矫正:%x", ((virussize/0x1000)+1)*0x1000);
pSectionHeader--;
pSectionHeader->SizeOfRawData = tail; //这个必须是200h的倍数
pSectionHeader->Misc.VirtualSize =tail;//union结构体不能用-> //改资源段的物理大小
DWORD VIRUSSIZE = ((virussize / 0x1000) + 1) * 0x1000;
pOptionalHeader->SizeOfImage = pOptionalHeader->SizeOfImage - VIRUSSIZE; //这个必须是1000h的倍数
handle = CreateFile(L"C:\\cleaner.exe.v", GENERIC_READ | GENERIC_WRITE, 0x3, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
WriteFile(handle, pFile, dwFileSize - virussize, &haveread, 0);//去除掉
}
}
else
{
++pSectionHeader;
j++;
if (j == pFileHeader->NumberOfSections)
{
printf("未发现感染文件!");
break;
}
continue;
}
pOptionalHeader->SizeOfImage = pOptionalHeader->SizeOfImage - pSectionHeader->SizeOfRawData;//Image大小更改
}
CloseHandle(hFile);
return 0;
}
DWORD Convert(wchar_t *lpBase, DWORD dwAddr, BOOL bFile2RVA)
{
DWORD dwRet = -1;
//读取该文件的信息(文件内存对齐方式以及区块数量,并将区块表指针指向区块表第一个区块头)
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBase;
//pDosHeader->e_lfanew,LONG AddressOfNewExeHeader定位到4550的50
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((unsigned long)lpBase + pDosHeader->e_lfanew);
//转换为long是操作实际物理地址值,再从unsigned long转换为指针
//printf("%x\n",*pNtHeader);//显示此时是不是4550
//printf("NumberOfSection:%x\n",*(pNtHeader+4));
DWORD dwMemAlign = pNtHeader->OptionalHeader.SectionAlignment;
DWORD dwFileAlign = pNtHeader->OptionalHeader.FileAlignment;
int dwSecNum = pNtHeader->FileHeader.NumberOfSections;
PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char *)lpBase + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
//用同样的方法定义到了段头部
DWORD dwHeaderSize = 0;
if (!bFile2RVA) // 内存偏移转换为文件偏移
{
//看需要转移的偏移是否在PE头内,如果在则两个偏移相同
dwHeaderSize = pNtHeader->OptionalHeader.SizeOfHeaders;//0x400
if (dwAddr <= dwHeaderSize)//0x400
{
delete lpBase;
lpBase = NULL;
return dwAddr;
}
else //不在PE头里,查看该地址在哪个区块中
{
for (int i = 0; i < dwSecNum; i++)//0x7200十进制29184
{
DWORD dwSecSize = pSecHeader[i].Misc.VirtualSize;
if ((dwAddr >= pSecHeader[i].VirtualAddress) && (dwAddr <= pSecHeader[i].VirtualAddress + dwSecSize))
//文件偏移 = 该区块的文件偏移 + (该偏移 - 该区块的内存偏移)
dwRet = pSecHeader[i].PointerToRawData + dwAddr - pSecHeader[i].VirtualAddress;
}
}
}
else // 文件偏移转换为内存偏移
{
dwHeaderSize = pNtHeader->OptionalHeader.SizeOfHeaders;
//看需要转移的偏移是否在PE头内,如果在则两个偏移相同
if (dwAddr <= dwHeaderSize)
{
delete lpBase;
lpBase = NULL;
return dwAddr;
}
else//不再PE头里,查看该地址在哪个区块中
{
for (int i = 0; i < dwSecNum; i++)
{
DWORD dwSecSize = pSecHeader[i].Misc.VirtualSize;
if ((dwAddr >= pSecHeader[i].PointerToRawData) && (dwAddr <= pSecHeader[i].PointerToRawData + dwSecSize))
//内存偏移 = 该区块的内存偏移 + (该偏移 - 该区块的文件偏移)
dwRet = pSecHeader[i].VirtualAddress + dwAddr - pSecHeader[i].PointerToRawData;
}
}
}
//delete lpBase;
//lpBase = NULL;
return dwRet;
}