标签:blog 文件路径 研究 seh 后缀 code eof arc 筛选
上次我们已经简介过了病毒特征码提取的基本方法,那么这次我们就通过编程来实现对于病毒的特征码查杀。
为了简单起见。这次我们使用的是setup.exe以及unpacked.exe这两个病毒样本。经过上次的分析,我们对setup.exe样本的特征码提取例如以下:
\x2a\x2a\x2a\xce\xe4\x2a\xba\xba\x2a\xc4\xd0\x2a\xc9\xfa\x2a\xb8
\xd0\x2a\xc8\xbe\x2a\xcf\xc2\x2a\xd4\xd8\x2a\xd5\xdf\x2a\x2a\x2a
为了方便起见,这里同一时候也将该特征码的文件偏移保存下来。即0x0c040。
然后是unpacked.exe的特征码:
\x13\x8b\x45\xf0\xe8\x00\x00\x00\x00\x81\x04\x24\xd7\x86\x00\x00
\xff\xd0\xeb\x11\x6a\x10\x68\x30\x80\x40\x00\xff\x75\xfc\x53\xff
它的文件偏移为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, “\x2a\x2a\x2a\xce\xe4\x2a\xba\xba\x2a\xc4\xd0\x2a\xc9\xfa\x2a\xb8” “\xd0\x2a\xc8\xbe\x2a\xcf\xc2\x2a\xd4\xd8\x2a\xd5\xdf\x2a\x2a\x2a” }, { // unpacked.exe “unpacked.exe”, 0x1920, “\x13\x8b\x45\xf0\xe8\x00\x00\x00\x00\x81\x04\x24\xd7\x86\x00\x00” “\xff\xd0\xeb\x11\x6a\x10\x68\x30\x80\x40\x00\xff\x75\xfc\x53\xff” } };
至此。病毒特征码的基础定义部分就到这里。
上述程序中,我是将病毒特征码保存在一个全局变量中,大家也能够另外创建一个文件。相似于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\n", 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不是病毒程序\n",szFindFile); } // 删除程序名称,仅仅保留“E:\” szFindFile[3] = ‘\0‘; 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毫秒。
#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\n",szFindFile); } // 匹配unpacked.exe的CRC32值 else if(dwTmpCRC32 == 0xC427A090) { printf("发现病毒程序:%s\n",szFindFile); } else { printf("%s不是病毒程序\n",szFindFile); } // 删除程序名称,仅仅保留“C:\” szFindFile[3] = ‘\0‘; ret = FindNextFile(hFindFile, &stFindFile); }while( ret != 0 ); } FindClose(hFindFile); return 0; }
相比而言,程序的主体部分还是基本一致的,仅仅只是是对于病毒特征的验证方式稍有不同。由于关于CRC32算法我们已经在前几次的课程中运用过,所以这里不再赘述。看一下执行结果:
图3
可见,利用CRC32算法提取出来的病毒特征码的检測方式,在结果上与上一个程序是一样的,相同是发现了两个病毒,没有特征码的病毒就没能识别。
然后再看一下用时,我的測试结果是0.063秒,也就是63毫秒,是之前的时间的3.9375倍,那么也就说明了,CRC32算法在效率上是不如传统的特征码查杀方式的。
由于我们仅仅有两个特征码,为了便于课程的解说。我採用的是直接利用if…else语句进行特征码的对照。
假设病毒的特征码的数量非常庞大,那么有多少特征码就须要使用多少个if语句,那么这显然是非常没有效率的。能否够利用一定的算法来优化大量的特征码的比对工作,不是我们讨论的重点。有兴趣的朋友能够研究一下。
标签:blog 文件路径 研究 seh 后缀 code eof arc 筛选
原文地址:http://www.cnblogs.com/yangykaifa/p/7258833.html