1. 程式人生 > >病毒檢測與防毒技術大揭祕5

病毒檢測與防毒技術大揭祕5

3.1.6.特徵碼載入

假設我們已經建產了病毒庫檔案,檔案共17個,檔名分別為:0.sig、1.sig、2.sig、3.sig、4.sig、5.sig、6.sig、7.sig、8.sig、9.sig、a.sig、b.sig、c.sig、d.sig、e.sig、f.sig、g.sig。

檔案字尾名sig,含意為signature,即特徵碼的意思。

檔案內容格式為:
“072B68E60B25FED5336235962D9C86E0Virus.OnlineGame-1376           ”
“0F5473AA8A8402561C40F050A784F47EVirus.Spy-62220                 ”
“0DC38A0DA3B57DBE5E14512DE7A67C72Virus.Spy-62252                 ”

特徵碼每個佔一行,以回車換車結束。特徵碼在病毒庫檔案中不使用雙引號“”。

程式中,讀取之前,首先要定義一個自定義格式。用於儲存單個特徵碼資訊:

Private Type Signature sHash As String * 32 sName As String * 32 sVbCrlf As String * 2 End Type

變數sHash是檔案特徵碼,佔32個位元組;變數sName是病毒名稱,佔32個位元組;此外,在病毒庫中每行儲存一個特徵碼,特徵碼後方還隱形的存在著一個回車換行符。回車換行符佔兩個位元組,因此,在定義格式時,還需要定義一個代表回車換行的標誌,上面的程式碼中變數sVbCrlf便是回車換行的意思,它佔兩個位元組。

然後定義一組公共變數陣列,用於儲存所有病毒庫特徵碼:

Public uSignature0() As Signature
Public uSignature1() As Signature

Public uSignature9() As Signature

中間省略

Public uSignatureA() As Signature
Public uSignatureF() As Signature

Public uSignatureG() As Signature

定義這些變數時,需要將他們放在公共模組中,並用Public標識,以便使它們可以在工程中的所有窗體和模組中被訪問到,方便在其它函式中呼叫進行特徵碼匹配校驗。

然後程式設計讀取病毒庫,將特徵碼內容填充進這些陣列中:

Dim sSignatureFile As String

Dim lFile As Long Dim lFileLen As Long

載入0~9.sig病毒庫檔案: Dim i As Long For i = 0 To 9 sSignatureFile = sAppPath & i & “.sig”

從病毒庫檔案載入特徵碼:
Select Case i
Case "0"
lFile = FreeFile
Open sSignatureFile For Binary As #lFile
lFileLen = LOF(lFile)

重新定義特徵碼陣列大小,這裡數字66的含意是: 前面定義的特徵碼自定義格式中,sHash和sName變數各佔32位元組,sVbCrlf佔2位元組,即一個自定義特徵碼佔66位元組,因此,使用檔案大小除以66,得到的是該病毒庫檔案中特徵碼的數量。  

ReDim uSignature0(1 To lFileLen / 66) As Signature

使用get方法獲取一次性獲取檔案所有內容內容,獲取後uSignature0陣列保已經得到了所有以字元0開頭的病毒特徵碼,使用uSignature0(?)即可訪問到具體的資料。

Get lFile, , uSignature0
Close lFile

接下來使用與上面類似的方法載入A~F.sig病毒庫檔案,數字65~70使用Chr()函式轉化後可以得到字母A~F。

Dim j As Long
For j = 65 To 70
sSignatureFile = sAppPath & Chr(j) & ".sig"
Select Case UCase(Chr(j))
Case "A"
lFile = FreeFile
Open sSignatureFile For Binary As #lFile
lFileLen = LOF(lFile)
If lFileLen <> 0 Then
ReDim uSignatureA(1 To lFileLen / 66) As Signature
Get lFile, , uSignatureA
End If
Close lFile
End Select
Next

有了這些前期準備,現在可以正式介紹掃描病毒的部分了,這些部分即相當於概念上的防毒引擎,比較重要,請讀者朋友請仔細理解。

Private Function ScanSections(hFile As Long) As String

ScanSections = ""
Dim uDos As IMAGE_DOS_HEADER
Dim uFile As IMAGE_FILE_HEADER
Dim uOptional As IMAGE_OPTIONAL_HEADER
Dim uSections() As IMAGE_SECTION_HEADER

Dim lBytesRead As Long
Dim i As Long
Dim bTempMemory() As Byte

把檔案內容讀取指標移動到檔案頭,讀取PE檔案DOS頭資訊:
SetFilePointer hFile, 0, 0, 0
ReadFile hFile, uDos, Len(uDos), lBytesRead, ByVal 0&

根據DOS頭的lfanew值,使檔案內容讀取指標指向PE頭+4的地方(即檔案頭開始地址):
SetFilePointer hFile, ByVal uDos.lfanew + 4, 0, 0

讀取檔案頭內容:
ReadFile hFile, uFile, Len(uFile), lBytesRead, ByVal 0&

讀取可選頭內容:
ReadFile hFile, uOptional, Len(uOptional), lBytesRead, ByVal 0&
ReDim uSections(uFile.NumberOfSections - 1) As IMAGE_SECTION_HEADER

讀取節的個數:
ReadFile hFile, uSections(0), Len(uSections(0)) * uFile.NumberOfSections, lBytesRead, ByVal 0&

遍歷PE檔案的每個節:
For i = 0 To UBound(uSections)
ReDim bTempMemory(1 To uSections(i).SizeOfRawData) As Byte
SetFilePointer hFile, ByVal uSections(i).PointerToRawData, 0, 0                    
ReadFile hFile, bTempMemory(1), uSections(i).SizeOfRawData, lBytesRead, ByVal 0&
Dim sTargetStr As String

構造特徵碼,特徵碼為:節的大小+節的雜湊值: sTargetStr=uSections(i).SizeOfRawData&":"&LCase(HashFileStream(bTempMemory))

Dim bTempStrByte() As Byte
bTempStrByte = sTargetStr
                
用雜湊演算法進行加密,得到加密的最終特徵碼:
sTargetStr = HashFileStream(bTempStrByte)
                
使用特徵碼匹配檢測,判斷此特徵碼是否在病毒庫中存在,如果存在,表示是病毒,則會返回病毒名稱:
Dim sMatchResult As String
sMatchResult = MatchSignature(sTargetStr)
                
如果函式返回不為空表示檢測到是病毒:
If sMatchResult <> "" Then
ScanSections = Trim(sMatchResult)
Exit Function
End If
Next i

End Function

這個函式的主要能兩有兩處:

一、分析PE檔案,獲取檔案每個節的特徵碼;

二、再次呼叫下級函式對節特徵碼進行檢測,判斷是否是病毒。

也許有的朋友會問:這已經多次使用下級函式進行特徵碼判斷了,為什麼不把程式碼順序寫下去,那樣程式碼會更為工整,邏輯會更清晰。這是因為以上呼叫的每個下級函式都是一個相較獨立的功能,且會在程式碼的其它地方被調次使用,寫為獨立函式從全域性角度看會使函式呼叫關係更為合理,這了是一個良好的程式設計習慣。

接下來且看HashFileStream和MatchSignature兩個關鍵函式:

Public Function HashFileStream(ByteStream() As Byte) As String

HashFileStream = ""
Dim lCtx As Long
Dim lHash As Long
Dim lFile As Long
Dim lRes As Long
Dim lLen As Long
Dim lIdx As Long
Dim bHash() As Byte
Dim bBlock() As Byte
    
Dim bStream() As Byte
ReDim bStream(1 To UBound(ByteStream)) As Byte
    
CopyMemory bStream(1), ByteStream(1), UBound(ByteStream)
    
CryptAcquireContext函式功能:得到系統CS,即密碼容器,為接下來使用雜湊演算法做準備。
引數說明:
引數1 lCtx : 返回CSP控制代碼
引數2 : 密碼容器名,為空連線預設的CSP
引數3 : 為空時使用預設CSP名(微軟RSA Base Provider)
引數4 PROV_RSA_FULLCSP : 型別
lRes = CryptAcquireContext(lCtx, vbNullString, vbNullString, PROV_RSA_FULL, 0)

If lRes <> 0 Then
    
CryptCreateHash函式功能:創雜湊物件,以便可以進行雜湊運算。
引數說明:
引數1: CSP控制代碼
引數2: 選擇雜湊演算法,比如CALG_MD5等
引數3: HMAC 和MAC演算法時有用
引數4: 保留,傳入0即可
引數5:返回hash控制代碼
lRes = CryptCreateHash(lCtx, ALGORITHM, 0, 0, lHash)
        
If lRes <> 0 Then
Const BLOCK_SIZE As Long = 32 * 1024&
ReDim bBlock(1 To BLOCK_SIZE) As Byte
Dim lCount As Long
Dim lBlocks As Long
Dim lLastBlock As Long
            
將傳入的資料塊分隔為多個部分:
lBlocks = UBound(ByteStream) \ BLOCK_SIZE

得到最後一個數據塊:
lLastBlock = UBound(ByteStream) - lBlocks * BLOCK_SIZE
    
For lCount = 0 To lBlocks - 1
CopyMemory bBlock(1), bStream(1 + lCount * BLOCK_SIZE), BLOCK_SIZE
            
CryptHashData函式功能:對資料進行雜湊運算。
引數說明:
引數1: 雜湊物件
引數2: 被雜湊的資料
引數3: 資料的長度
引數4: 微軟的CSP這個值會被忽略
lRes = CryptHashData(lHash, bBlock(1), BLOCK_SIZE, 0)
Next
            
If lLastBlock > 0 And lRes <> 0 Then
ReDim bBlock(1 To lLastBlock) As Byte
CopyMemory bBlock(1), bStream(1 + lCount * BLOCK_SIZE), lLastBlock
lRes = CryptHashData(lHash, bBlock(1), lLastBlock, 0)
End If
        
If lRes <> 0 Then
CryptGetHashParam函式功能:獲取雜湊資料大小。
引數說明:
引數1:雜湊對像
引數2:取雜湊資料大小(HP_HASHSIZE)
引數3:返回雜湊資料長度
lRes = CryptGetHashParam(lHash, HP_HASHSIZE, lLen, 4, 0)
            
If lRes <> 0 Then
ReDim bHash(0 To lLen - 1)
                
CryptGetHashParam函式功能:取得雜湊運算資料結果。
引數說明:
引數1:雜湊對像
引數2:取雜湊資料(值)(HP_HASHVAL)
引數3:雜湊陣列
lRes = CryptGetHashParam(lHash, HP_HASHVAL, bHash(0), lLen, 0)
If lRes <> 0 Then
For lIdx = 0 To UBound(bHash)

構造雜湊值,2位一組,不足補0:
HashFileStream = HashFileStream & Right("0" & Hex(bHash(lIdx)), 2)
Next
End If
End If
End If

CryptDestroyHash lHash
End If
End If
CryptReleaseContext lCtx, 0

End Function

以上使用的是微軟提供的一組用於雜湊計算的API函式,在我們的程式中用於算運算出可執行檔案各節的雜湊值,用途是得到檔案節的雜湊碼,以進行特徵碼匹配 ,檢測掃描檔案是否是病毒。而用於匹配演算法的MatchSignature程式碼編碼如下:

Public Function MatchSignature(ByVal sHash As String) As String
MatchSignature = ""
If sHash = "" Then
Exit Function
End If

取雜湊碼值的第一位為標誌,確定使用特徵碼型別:
Dim sFlagWord As String
sFlagWord = UCase(Left(sHash, 1))

Dim i As Long
Select Case sFlagWord
Case "0"
For i = 1 To UBound(uSignature0)
If uSignature0(i).sHash = sHash Then

匹配成功,在病毒庫中找到了該特徵碼,返回病毒名稱:
MatchSignature = uSignature0(i).sName
Exit Function
End If
Next i

Case "1"
For i = 1 To UBound(uSignature1)
If uSignature1(i).sHash = sHash Then

匹配成功,在病毒庫中找到了該特徵碼,返回病毒名稱:
MatchSignature = uSignature1(i).sName
Exit Function
End If
Next i

End Function

此處程式碼的功能是用於檢測實現從PE檔案中運算出的節特徵碼與病毒庫中的特徵碼是否匹配,如果匹配則返回病毒名稱。

到此,掃描病毒的功能的編碼都已實現。

這裡實現的僅僅是非感染型病毒木馬的檢測。在查殺時,僅需要簡單的結束病毒程序、刪除病毒檔案即可完成。在近年來,絕大多數的病毒木馬都是這種非感染型的,本世紀初及上世紀未DOS時代流行的感染型病毒已並不多見,當然並不是說完全不存在。對於感染型病毒的檢測方式,與此大至相同,各位讀者可以觸類旁通使用類似的方法進行檢測,只是在查殺方法上與此有所不同。並不能只是簡單的進行檔案刪除操作,還需要對已感染的檔案進行重寫。

啟發式病毒掃描技術是與此種特徵碼匹配檢測完全不同的病毒木馬檢測技術。啟發式也可以理解為行為分析,即根據病毒木馬對系統入侵的行為進行檢測,比如木馬進入系統後,為了能夠長期獲得對系統的控制權,最基本的一點是要實現開機自啟動,實現的方式可以通過寫登錄檔自啟動鍵值、關聯檔案型別、向系統新增服務、使用計劃任務等等。木馬的這些行為,會使系統配置、設定發生變化,在對系統相關資訊熟悉的情況下,通過檢查相關配置位置的內容,便可以發現木馬入侵的蛛絲馬跡,進而揪出木馬本身。這一套操作,通過程式設計的方法進行實現便形成了啟發式防毒。當然,這只是實現啟發式的一種方式,除此之外,還有其它多種實現方法,比如通過檢測PE檔案的函式匯入列表中是否含有某種組織的函式、通過內建反彙編引擎判斷PE檔案的反彙編程式碼等等。

在近年來,有的病毒木馬也會使用Rootkit技術,通過SSDT HOOK、IAT、API HOOK等技術隱藏自身程序、檔案、登錄檔項。使使用者在日常的操作和使用電腦過程中根本無法意識到病毒的存在。因此檢測和清除Rootkit病毒的難度較大,好在魔與道總是並存的,有Rootkit也就有與之對應的Anti Rookit工具的存在,專門用於檢測和清除Rootkit,破掉了隱身外衣,其查殺方式就與普通病毒木馬無異了。

注:作者:wing qq:6465660 本書理論及功能、程式碼源於防毒軟體:“Ty2y防毒軟體”,作者授意,文章可自由轉載,只需註明原出處即可,特此說明。