病毒木马查杀实战第019篇:病毒特征码查杀之编程实现
前言
上次我們已經簡單介紹過了病毒特征碼提取的基本方法,那么這次我們就通過編程來實現對于病毒的特征碼查殺。
定義特征碼存儲結構
為了簡單起見,這次我們使用的是setup.exe以及unpacked.exe這兩個病毒樣本。經過上次的分析,我們對setup.exe樣本的特征碼提取如下:
x2ax2ax2axcexe4x2axbaxbax2axc4xd0x2axc9xfax2axb8
xd0x2axc8xbex2axcfxc2x2axd4xd8x2axd5xdfx2ax2ax2a
為了方便起見,這里同時也將該特征碼的文件偏移保存下來,即0x0c040。然后是unpacked.exe的特征碼:
x13x8bx45xf0xe8x00x00x00x00x81x04x24xd7x86x00x00
xffxd0xebx11x6ax10x68x30x80x40x00xffx75xfcx53xff
它的文件偏移為0x1921。
有了以上的信息,就可以開始進行編程了。首先需要定義一個數據結構,用于保存特征碼和文件偏移。該結構如下:
#define NAMELEN 20
#define SIGNLEN 32
typedef struct SIGN
{
char szVirusName[NAMELEN];
LONG lFileOffset;
BYTE bVirusSign[SIGNLEN + 1];
}_SIGN, *PSIGN;
利用該數據結構定義一個全局變量,該全局變量保存有上述兩個病毒的特征碼,定義如下:
SIGN Sign[2] =
{
{
// setup.exe
“setup.exe”,
0x0c040,
“x2ax2ax2axcexe4x2axbaxbax2axc4xd0x2axc9xfax2axb8”
“xd0x2axc8xbex2axcfxc2x2axd4xd8x2axd5xdfx2ax2ax2a”
},
{
// unpacked.exe
“unpacked.exe”,
0x1920,
“x13x8bx45xf0xe8x00x00x00x00x81x04x24xd7x86x00x00”
“xffxd0xebx11x6ax10x68x30x80x40x00xffx75xfcx53xff”
}
};
至此,病毒特征碼的基礎定義部分就到這里。上述程序中,我是將病毒特征碼保存在一個全局變量中,大家也可以另外創建一個文件,類似于PEiD的userdb.txt文件,從而專門保存病毒的特征碼。現實中的殺毒軟件的特征庫也是以專門的文件的形式,保存在本地計算機中的。這里我為了簡單起見,選擇以全局變量的形式進行保存。
主體程序的編寫
首先需要編寫一個函數用于對目標程序指定位置處的十六進制代碼進行檢測:
BOOL CheckSig(char* FilePath)
{
DWORD dwSigNum = 0;
DWORD dwNum = 0;
BYTE buffer[SIGNLEN+1];
int i;
HANDLE hFile = NULL;
hFile = CreateFile(FilePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
for(i=0; i <= 1; i++)
{
// 將待檢測程序的文件指針指向特征碼的偏移位置
SetFilePointer(hFile, Sign[i].lFileOffset, NULL, FILE_BEGIN);
// 讀取目標程序指定偏移位置的特征碼
ReadFile(hFile, buffer, sizeof(buffer), &dwNum, NULL);
// 特征碼的比對
if(memcmp(Sign[i].bVirusSign, buffer, SIGNLEN) == 0)
{
printf("發現病毒程序:%s
", FilePath);
CloseHandle(hFile);
return TRUE;
}
}
CloseHandle(hFile);
return FALSE;
}
然后就是main函數的編寫:
int main()
{
WIN32_FIND_DATA stFindFile;
HANDLE hFindFile;
char *szFilter = "*.exe"; // 保存搜索的篩選條件(所有exe文件)
char szFindFile[MAX_PATH]; // 保存欲檢測的程序的路徑
char szSearch[MAX_PATH]; // 保存完整篩選路徑
int ret = 0; // 搜索的返回值
lstrcpy(szFindFile, "E:\");
lstrcpy(szSearch, "E:\");
lstrcat(szSearch, szFilter);
hFindFile = FindFirstFile(szSearch, &stFindFile);
if(hFindFile != INVALID_HANDLE_VALUE)
{
do
{
// 組成完整的待檢測程序的路徑
lstrcat(szFindFile, stFindFile.cFileName);
// 利用特征碼檢測目標程序是不是病毒程序
if(!CheckSig(szFindFile))
{
printf("%s不是病毒程序
",szFindFile);
}
// 刪除程序名稱,只保留“E:”
szFindFile[3] = '';
ret = FindNextFile(hFindFile, &stFindFile);
}while( ret != 0 );
}
FindClose(hFindFile);
return 0;
}
上述程序僅僅是檢測E盤根目錄下所有后綴為exe的程序是否為病毒程序,其實還可以進行修改,使其可以全盤搜索,大家可以參考“熊貓燒香專殺工具”的相關代碼部分。另外為了慎重起見,僅僅通過后綴進行exe程序的檢測是不嚴謹的,常用的檢測一個程序是不是exe程序的方法,就是解析目標程序中的相應位置是否為“MZ”以及“PE”。我這里為了簡單起見,就不采用該方法,有興趣的朋友可以自行編程實現。
程序的測試
這里我使用的是Code::Blocks13.12這款開源并且免費的開發環境,因為這款軟件可以自動計算程序的執行時間,便于我們之后的對比操作。為了進行測試,我已經在E盤的根目錄下放置了10個程序,其中4個程序是我們之前講過的病毒樣本,還有6個程序是我們以前也曾使用過的一些工具軟件:
圖1
上圖中前方帶有小方塊的就是病毒樣本。然后我們在Code::Blocks中編譯運行程序:
圖2
可見程序已經很成功地識別出了setup.exe以及unpacked.exe這兩個病毒樣本。事實上,cf.exe以及OSO.exe也是病毒程序,但由于我并沒有把這兩個樣本的特征碼加入我們程序的特征庫,因此并沒能識別出來。最后,Code::Blocks還顯示出了本次程序的運行時間,當然每次的運行時間可能都不一樣,包括在不同的計算機上運行的結果應該也是不同的。但是通過多次運行進行觀察,基本上是0.016秒,也就是16毫秒。
與CRC32病毒識別方式的對比
我們以前的程序使用的CRC32算法來識別病毒,那么我們這里可以對比一下,看看這次我們所講的方法和CRC32算法在程序運算時間上的優劣。CRC32病毒特征識別的程序如下:
#include "stdio.h"
#include "windows.h"
DWORD CRC32(BYTE* ptr,DWORD Size)
{
DWORD crcTable[256],crcTmp1;
//動態生成CRC-32表
for (int i=0; i<256; i++)
{
crcTmp1 = i;
for (int j=8; j>0; j--)
{
if (crcTmp1&1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;
else crcTmp1 >>= 1;
}
crcTable[i] = crcTmp1;
}
//計算CRC32值
DWORD crcTmp2= 0xFFFFFFFF;
while(Size--)
{
crcTmp2 = ((crcTmp2>>8) & 0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];
ptr++;
}
return (crcTmp2^0xFFFFFFFF);
}
//
// 計算程序的CRC32值,輸入為文件路徑,輸出為DWORD類型的CRC32值
//
DWORD CalcCRC32(char* FilePath)
{
HANDLE hFile = CreateFile(FilePath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Create Error");
return FALSE;
}
DWORD dwSize = GetFileSize(hFile,NULL);
if (dwSize == 0xFFFFFFFF)
{
printf("GetFileSize Error");
return FALSE;
}
BYTE *pFile = (BYTE*)malloc(dwSize);
if (pFile == NULL)
{
printf("malloc Error");
return FALSE;
}
DWORD dwNum = 0;
ReadFile(hFile,pFile,dwSize,&dwNum,NULL);
DWORD dwCrc32 = CRC32(pFile,dwSize);
if (pFile != NULL)
{
free(pFile);
pFile = NULL;
}
CloseHandle(hFile);
return dwCrc32;
}
int main()
{
WIN32_FIND_DATA stFindFile;
HANDLE hFindFile;
char *szFilter = "*.exe"; // 保存搜索的篩選條件(所有exe文件)
char szFindFile[MAX_PATH]; // 保存欲檢測的程序的路徑
char szSearch[MAX_PATH]; // 保存完整篩選路徑
int ret = 0; // 搜索的返回值
lstrcpy(szFindFile, "E:\");
lstrcpy(szSearch, "E:\");
lstrcat(szSearch, szFilter);
DWORD dwTmpCRC32;
hFindFile = FindFirstFile(szSearch, &stFindFile);
if(hFindFile != INVALID_HANDLE_VALUE)
{
do
{
// 組成完整的待檢測程序的路徑
lstrcat(szFindFile, stFindFile.cFileName);
// 利用CRC32算法檢測目標程序是不是病毒程序
dwTmpCRC32 = CalcCRC32(szFindFile);
// 匹配setup.exe的CRC32值
if(dwTmpCRC32 == 0x89240FCD)
{
printf("發現病毒程序:%s
",szFindFile);
}
// 匹配unpacked.exe的CRC32值
else if(dwTmpCRC32 == 0xC427A090)
{
printf("發現病毒程序:%s
",szFindFile);
}
else
{
printf("%s不是病毒程序
",szFindFile);
}
// 刪除程序名稱,只保留“C:”
szFindFile[3] = '';
ret = FindNextFile(hFindFile, &stFindFile);
}while( ret != 0 );
}
FindClose(hFindFile);
return 0;
}
相比而言,程序的主體部分還是基本一致的,只不過是對于病毒特征的驗證方式稍有不同。因為關于CRC32算法我們已經在前幾次的課程中運用過,所以這里不再贅述。看一下運行結果:
圖3
可見,利用CRC32算法提取出來的病毒特征碼的檢測方式,在結果上與上一個程序是一樣的,同樣是發現了兩個病毒,沒有特征碼的病毒就沒能識別。然后再看一下用時,我的測試結果是0.063秒,也就是63毫秒,是之前的時間的3.9375倍,那么也就說明了,CRC32算法在效率上是不如傳統的特征碼查殺方式的。
小結
本文討論了病毒特征碼查殺的編程實現,并與CRC32算法在效率上進行了對比。由于我們只有兩個特征碼,為了便于課程的講解,我采用的是直接利用if…else語句進行特征碼的對比。如果病毒的特征碼的數量非常龐大,那么有多少特征碼就需要使用多少個if語句,那么這顯然是很沒有效率的。是否能夠利用一定的算法來優化大量的特征碼的比對工作,不是我們討論的重點,有興趣的朋友可以研究一下。
總結
以上是生活随笔為你收集整理的病毒木马查杀实战第019篇:病毒特征码查杀之编程实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ 3039: 玉蟾宫( 悬线法
- 下一篇: 轻量级文本编辑器,Notepad最佳替代