反病毒攻防研究第015篇:病毒感染標志的添加
來源:程序員人生 發布時間:2014-11-05 08:17:04 閱讀次數:3755次
1、前言
對感染型病毒而言,如果對同1個目標文件屢次進行感染,有可能致使目標文件破壞,使得沒法履行。所以病毒程序常常會在第1次感染時對目標文件寫進1個感染標志,這樣在第2次遇到該文件時,首先判斷1下該文件中是不是包括有感染標志,如果有,則不再感染,如果沒有感染標志則進行感染(關于文件的感染,可參見《反病毒攻防研究第004篇:利用縫隙實現代碼的植入》和《反病毒攻防研究第005篇:添加節區實現代碼的植入》)。所謂的感染標志其實就是在PE文件中無關緊要的位置寫入的1個字符串,所以感染標志的添加、讀取與判斷操作,其實就是基本的文件讀寫操作。
2、感染標志的添加
在PE文件結構中存在著許多不實用的字段,比如在IMAGE_DOS_HEADER中,只有e_magic與e_lfanew這兩個字段才是重要的,前者用于驗證本文件是不是為PE文件,后者保存著PE文件的偏移位置。因此可以從第2個字段,即e_cblp(Bytes on last page of file)開始,寫入我們的感染標志。這里將該標志設定為“Hack”這4個字符。需要注意的是,將感染標志添加到文件中,應當首先將“Hack”轉化為106進制數值,然后再反向寫入(小端顯示),代碼以下:
#define VIRUSFLAG 0x6b636148 // 感染標志,這里為“Hack”
// 感染標志的寫入。3個參數分別為:欲感染文件的句柄、欲寫入感染標志的位置
// 和感染標志
BOOL AddSig(HANDLE hFile, DWORD dwAddr, DWORD dwSig)
{
DWORD dwNum = 0;
// 在文件中設置讀寫位置
SetFilePointer(hFile, dwAddr, 0, FILE_BEGIN);
// 寫入感染標志
if(WriteFile(hFile, &dwSig, sizeof(DWORD), &dwNum, NULL))
{
MessageBox(NULL, "感染標志添加成功!", "提示", MB_OK);
return TRUE;
}
else
{
MessageBox(NULL, "感染標志添加失敗!", "提示", MB_OK);
return FALSE;
}
}
然后編寫檢測感染標志的代碼:
// 感染標志檢測
BOOL CheckSig(HANDLE hFile, DWORD dwAddr, DWORD dwSig)
{
DWORD dwSigNum = 0;
DWORD dwNum = 0;
SetFilePointer(hFile, dwAddr, 0, FILE_BEGIN);
ReadFile(hFile, &dwSigNum, sizeof(DWORD), &dwNum, NULL);
if(dwSigNum == dwSig)
{
return TRUE;
}
return FALSE;
}
我們需要令“病毒”程序在每次感染前先調用CheckSig()函數,根據其返回值來判斷目標文件是不是已被感染過,然后再決定是不是需要進行感染。主函數代碼以下:
#include <windows.h>
#define FILENAME "helloworld.exe" // 欲添加感染標志的文件名
#define offsetof(struct_t,member) (size_t)&(((struct_t *)0)-> member)
int main()
{
HANDLE hFile = NULL;
hFile = CreateFile(FILENAME,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(CheckSig(hFile, offsetof(IMAGE_DOS_HEADER, e_cblp), VIRUSFLAG))
{
MessageBox(NULL, "本文件已被感染過!", "提示", MB_OK);
return ⑴;
}
AddSig(hFile, offsetof(IMAGE_DOS_HEADER, e_cblp), VIRUSFLAG);
return 0;
}
程序將感染標志寫入了IMAGE_DOS_HEADER中的e_cblp位置,它不會對程序的履行產生任何影響。這里需要說明的是,程序中我們使用了offsetof()這個宏,它本來是被定義在stddef.h中的,這里我將其拿出來專門定義。這個宏的作用是求某個結構體的特定成員在結構體里面的偏移量,對本程序來講就是求e_cblp在IMAGE_DOS_HEADER中的偏移,也就是2(由于它之前的e_magic占用了兩個字節)。
這里給大家分析1下(size_t)&(((struct_t*)0)-> member)的意義。首先,(struct_t *)0是1個指向struct_t類型(本程序中為IMAGE_DOS_HEADER)的指針,其指針值為 0,所以其作用就是把從地址 0 開始的存儲空間映照為1個struct_t類型的對象。((struct_t *)0)-> member是訪問類型中的成員member(本程序中為e_cblp),相應地 &((struct_t *)0)->
member) 就是返回這個成員的地址。由于對象的起始地址為 0,所以成員的地址其實就是相對對象首地址的成員的偏移地址。最后再通過類型轉換,轉換為 size_t 類型(32位是unsigned int,64位是long unsigned int)。
3、程序測試
為了測試我們的程序,這里照舊使用《反病毒攻防研究第004篇:利用縫隙實現代碼的植入》
中所編寫的“helloworld.exe”程序。將兩個程序放在同1個目錄下,感染前先用Hex
Editor Neo查看1下“helloworld.exe”程序的DOS頭部份:
圖1 感染前的DOS頭
然后運行本程序,再次查看DOS頭部:

圖2 感染后的DOS頭
可見,我們的感染是成功的。
4、小結
給文件添加感染標志對文件來講不會產生任何影響,很多感染型病毒都會給目標文件添加感染標志,比如“熊貓燒香”就會在程序中添加“WhBoy”標志。因此當時李俊所編寫的“熊貓燒香”病毒專殺工具(李俊版)就是通過檢測文件中是不是有“WhBoy”標志來判斷文件是不是被感染,但是這類檢測方法卻過于粗糙了。
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈