Virut infection virus check and kill tool

  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;
}

Guess you like

Origin blog.csdn.net/qq_43312649/article/details/108220904