如何讓程序自身防病毒

如何讓程序自身防病毒
masepu(本文已發表於黑客防線,轉載註明出處)
一、前言
計算機病毒(Computer Virus)在《中華人民共和國計算機信息系統安全保護條例》中被明確定義爲“編制或者在計算機程序中插入的破壞計算機功能或者破壞數據,影響計算機使用並且能夠自我複製的一組計算機指令或者程序代碼”。然而伴隨着互聯網和計算機的快速發展,病毒的也從單純破壞計算機功能和數據演變爲以最終經濟利益爲驅動的功能各異的變體。作爲病毒感染傳播的重要途徑,寄生在正常文件實現自身複製傳播的方式依然爲衆多病毒所採用,爲了避免我們自己編寫的程序被病毒所感染,成爲助紂爲虐的工具,在這裏我給大家介紹幾種在編程中防止病毒感染程序自身的方法,希望能夠拋磚引玉,讓病毒遠離我們的程序。
二、實現方法
程序防病毒的思路就是我們在編程的時候加入一些檢測程序自身是否被篡改的代碼,如若檢測到文件被改變,則向用戶提示計算機可能感染病毒的風險,然後退出程序。在這裏我採用以下幾種方式對程序進行自身校驗,以防止被非法篡改。
(1)判斷文件大小的方法
首先我想到的就是文件大小的判斷,如果我們程序文件的大小超過了本身正常的長度,那麼就可以斷定該程序已被感染,否則程序不會發生長度變化的。這時程序顯示“程序被感染”對話框後退出。寫程序時我們要先將可執行文件生成出來,看看正常的大小值是多少,然後再將文件大小寫入到程序代碼中作爲正常值進行比較,用來判斷程序文件的大小是否發生變化。以下是程序實現的關鍵代碼:
//獲取自身文件名
 ::GetModuleFileName(0, my_name, sizeof(my_name));
 struct _stat ST;
 _stat(my_name, &ST);
 //加入程序的最終大小,來判斷文件是否被感染
 if(ST.st_size > 188471)
 {
     MessageBox(NULL,"程序被感染","注意",MB_OK);
   exit(0);    //直接退出程序
 }
  
(2)通過文件副本比較法
檢查文件大小的方法雖然簡單實用,但是對於部分不改變宿主文件大小,直接把代碼插入到PE文件中可用空閒空間的病毒來說就失去了防護作用了。對於這種情況我想可以在程序發佈時提供一個拷貝的副本,後綴改成非可執行文件的後綴,在程序運行時去檢查自身與副本文件是否一樣,否則提示“程序被感染”對話框後退出。寫程序時先將可執行程序生成出來,獲得文件大小,然後再將文件大小寫入到程序代碼中作爲循環比較次數(當然也可以用其他方法比較)。以下是程序實現的關鍵代碼:
FILE *sfp,*bfp;
sfp=fopen(my_name,"rb");
bfp=fopen(backup_name,"rb");
int i;
 
for (i=0;i<180273;i++)
{
 if (fgetc(sfp) != fgetc(bfp))
 {
  MessageBox(NULL,"程序被感染","notice",MB_OK);
  exit(0);
 }
}
(3)通過程序的MD5值檢查法
對文件做HASH摘要後進行比較,可以快速準確的發現文件哪怕是最細微的改變。MD5作爲一種流行的信息摘要算法在用於確保文件信息完整一致性有着非常廣泛的應用。在通過文件副本比較的方法中我們可以將文件的相互比較改成比較文件的MD5值,如果該值發生變化則可以斷定程序被非法篡改了。程序編寫前我們首先要找到對文件進行MD5摘要的函數。我在網上找了一圈沒找到直接對文件進行MD5的C程序代碼,只找到了一個對字符串做MD5摘要的類,所以我們在編程時需要引用該類,然後對要做MD5值比較的文件內容進行轉換,方法是以共享只讀的方式將自身文件打開,然後通過CreateFileMapping函數創建文件映射對象,再將文件映射對象映射到當前應用程序的地址空間,最後我們就可以直接調用MD5類的方法進行摘要,像處理字符串一樣來處理文件的映像了。關鍵代碼如下:
fHandle=CreateFile(filename,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
filesize=::GetFileSize(fHandle,NULL);
mHandle=CreateFileMapping(fHandle,NULL,PAGE_READONLY,0,0,NULL);
lpHandle=(DWORD)MapViewOfFile(mHandle,FILE_MAP_READ,0,0,0);
char *lpFileData=(char *)lpHandle;
MD5_CTX   md5T;  
unsigned   char   digest[16];  
md5T.MD5Update   ((unsigned   char*)lpFileData, filesize);
md5T.MD5Final   (digest);
char s[16];
for(int  i=0;i<16;i++)  
{   sprintf((s+2*i),"%2X",digest[i]); } //格式化MD5值
通過以上代碼,我們可以先後獲得程序自身的MD5值和備份文件的MD5值(這裏的備份文件和程序處於同一文件夾下,名稱爲backup),再對獲得的兩個文件的MD5值進行相互比較,如果值相等則說明程序正常,未被感染,可以繼續向下執行,否則立即退出程序。我也考慮過是否可以不用副本的方式進行文件的MD5值比較,將正常的MD5值存在程序中當比較時直接使用,比如事先將程序自身的MD5值存到程序中,程序運行後直接計算自身的MD5值然後與存在程序中的MD5值進行比較。可仔細想想我如何實現在編代碼還沒有生成可執行文件的時候就獲得最終程序的MD5值呢?這不和先有雞還是先有蛋的問題一樣了麼?我需要在程序編寫時就知道生成的可執行文件的MD5值,可是生成的可執行文件的MD5值與我編程寫入的值又密切相關,根本無法獲得。所以只能使用對備份文件進行MD5摘要比較的方法來進行檢測了。
(4)使用MapFileAndCheckSum函數法
PE可執行文件頭中有一個CheckSum雙字,用於存放PE可執行文件的校驗和。我們可以利用這個文件頭參數來檢測文件自身是否被改變過。一般的可執行文件不需要校驗和,因此在普通可執行文件中CheckSun通常是0。而Windows的驅動程序則需要校驗和,如果此處的值與驗證的校驗和不同,就會提示出錯,提醒你重新下載驅動程序。根據這個原理,我們可以利用MapFileAndCheckSum函數來編寫出具有防止程序被感染更改的程序。該函數原型如下:
ULONG MapFileAndCheckSumA(
IN LPSTR Filename,
OUT LPDWORD HeaderSum,
OUT LPDWORD CheckSum
);
參數意義:
Filename 指定文件名的字符串的指針,就是自身文件名
HeaderSum原始校驗和變量的指針,有錯誤時它是零值。
CheckSum計算後的校驗和的變量指針。
返回值:
如果函數成功, 函數返回值是CHECKSUM_SUCCESS ;
如果函數失敗,函數返回值爲下列值之一:
CHECKSUM_OPEN_FAILURE:不能打開文件;
CHECKSUM_MAP_FAILURE:不能創建文件的映射文件;
CHECKSUM_MAPVIEW_FAILURE:不能映射文件的觀看;
CHECKSUM_UNICODE_FAILURE :不能將文件名稱轉換爲UNICODE。
程序代碼如下:
//取程序的文件名
GetModuleFileName(GetModuleHandle(NULL),buf,512);
//調用MapFileAndCheckSum 取該文件的校驗和if(CHECKSUM_SUCCESS!=MapFileAndCheckSum(buf,
&head_check_sum,&check_sum))
{ MessageBox(0,"檢查出錯,檢查未完成","錯誤",MB_OK);
return 0;}
// 分析結果
if(head_check_sum!=check_sum)
{MessageBox(0,"這程序已經被非法改動,可能您的系統已經被病毒感染","出錯提示",MB_ICONWARNING);}
return 0;}
需要注意的是,如果想使普通的PE可執行文件具有這種防止病毒感染的能力,需要在程序編寫時作以下設置:連上imagehlp.lib這個庫,然後在Project->Settings->Link中去掉Generate debug info和Link incrementlly選項,再選擇Category組合框的Customize,去掉Use program database選項,最後在Project Options中加入"/release",這樣這個程序就具有防病毒感染和修改的功能了。

三、總結
   通過以上四種方法,我們可以有效地防止病毒對我們程序的修改和感染。每種方法都有其各自的特點和適應的情況,在編程中可以根據不同情況選擇合適的方法。這些方法比較簡單,哪位朋友有更好的方法希望能夠共享,大家多多交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章