病毒檢測與防毒技術大揭祕3
3.功能實現
在上面的章節中,對軟體的功能進行了初步設定,接下來將要以程式設計的思路,將這些功能進行梳理、整合,確定模組化的程式設計實現方案,並編碼實現。
本書中的編碼中,會使用到VB、VC、ASP、彙編四種程式語言,主要功能由VB編寫、某些功能需要使用DLL,DLL程式碼由VC編寫,ASP語言用在雲安全後臺,彙編是在某些環節在DLL中內嵌使用。要深入理解本書內容,需要對此三種程式語言熟悉,最低要求是有一定的瞭解,能夠讀懂相關程式碼,本書非程式語言教程,不講解程式設計基礎知識。
也許有程式設計師會質疑:VB能寫的了防毒軟體嗎? 用事實說話往往是最有說服力的:微軟發行的Microsoft AntiSpyware軟體,便是用VB開發而成的。
規則約定:在本書程式碼中,對系統API函式,使用粗體字,複雜的API函式,會進行解釋說明。對於重要的自定義的函式、變數,使用之處用加粗加大字型。
3.1.防毒實現方案及編碼實現
如上文所講,本軟體中將防毒分為三種模式:快速掃描、全盤掃描、自定義掃描。
這三種掃描方式,展現在使用者面前時是3種不同的操作,而在程式中的編碼是非常相近的,不同在於掃描的目標檔案對像不同。
快速掃描對載入在記憶體中的檔案進行掃描;
全盤掃描對整個硬碟中的檔案掃描;
自定義掃描對選中的目標檔案進行掃描。
如下圖所示:
在此,以快速掃描為例,進行編碼說明 。
快速掃描要掃描記憶體中的檔案,如何確定哪些檔案在記憶體中被使用呢?這裡我們通過遍歷系統活動程序及程序相關模組檔案的方式來實現。
編碼如下:
Dim lProcess As Long
lProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
Dim uProcess As PROCESSENTRY32
uProcess.dwSize = Len(uProcess)
If Process32First(lProcess, uProcess) Then
Dim lModule As Long lModule=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, uProcess.th32ProcessID) Dim ModEntry As MODULEENTRY32 ModEntry.dwSize = LenB(ModEntry) ModEntry.szExePath = ""
If Module32First(lModule, ModEntry) Then
Dim sFile As String
sFile = TrimNull(ModEntry.szExePath)
Dim sScanResult As String
sScanResult = ScanFile(sFile)
Loop While Module32Next(lModule, ModEntry)
CloseHandle lModule
Loop While Process32Next(lProcess, uProcess)
CloseHandle lProcess
以上是遍歷系統程序及相關模組的程式碼,也就是檢測系統記憶體中載入著哪些檔案。其中,使用APi函式CreateToolhelp32Snapshot獲取系統程序快照,然後使用Process32First和Process32Next函式獲取程序列表、使用Module32First和Module32Next獲取程序使用的各個檔案,取得了檔案列表就可以對檔案逐個進行掃描了。
這裡沒有讀取配置檔案區分是否掃描執行檔案或是全部檔案,配置檔案的使用功能放在後面部分單獨講述。
上面的程式碼中ScanFile函式的sFile引數變數中存放的是載入在記憶體中的各檔案完整路徑,在獲取到檔案路徑後,使用ScanFile函式對檔案進行掃描,ScanFile函式是實現檔案病毒掃描的功能。編碼如下:
Public Function ScanFile(sFile As String) As String
ScanFile = ""
Dim lFileHwnd As Long
lFileHwnd = CreateFile(sFile, ByVal &H80000000, 0, ByVal 0&, 3, 0, ByVal 0)
Dim bBuffer(12) As Byte
Dim lBytesRead As Long
Dim uDosHeader As IMAGE_DOS_HEADER
ReadFile lFileHwnd, uDosHeader, ByVal Len(uDosHeader), lBytesRead, ByVal 0&
CopyMemory bBuffer(0), uDosHeader.Magic, 2
檢查PE檔案標誌
If (Chr(bBuffer(0)) & Chr(bBuffer(1)) = "MZ") Then
設定偏移量,指向PE頭結構
SetFilePointer lFileHwnd, uDosHeader.lfanew, 0, 0
ReadFile lFileHwnd, bBuffer(0), 4, lBytesRead, ByVal 0&
再次檢查是否是PE檔案
If (Chr(bBuffer(0)) = "P") And (Chr(bBuffer(1)) = "E") And (bBuffer(2) = 0) And (bBuffer(3) = 0) Then
再進一步呼叫下級函式掃描檔案是否是病毒,如果是病毒則返回病毒名,否則返回空值
Dim sScanSectionResult As String
sScanSectionResult = ScanSections(lFileHwnd)
如果返回不為空,表示檢測到是病毒,則關閉檔案,返回
If sScanSectionResult <> "" Then
ScanFile = sScanSectionResult
CloseHandle lFileHwnd
Exit Function
End If
End If
End If
CloseHandle lFileHwnd
End Function
上面已對部分關鍵程式碼做了註釋,這裡再對程式碼功能做具體介紹。此函式中,sFile是傳入的待掃描檔名,先使用CreateFile函式開啟檔案,然後檢測兩處檔案標識對檔案型別進行判斷,確定是PE檔案(也就是可執行檔案)後,再呼叫下級函式對檔案進行掃描,因為病毒木馬都是可執行檔案,而不可能是文字檔案,所以只有掃描可執行檔案才有意義,只有可執行檔案才可能是病毒木馬。
也許有人注意到,到之前介紹掃描方式時,方案中設計了兩種掃描方式,一種為掃描可執行檔案,一種為掃描所有檔案,那這裡為何又要對檔案格式進行判斷只掃描可執行檔案呢?這是因為:在windows系統中,檔案的字尾名是可以自由更改的,前面所提到的是從表面上根據字尾名進行分類,這種分類較為直觀,但不夠嚴謹,這裡再次進行判斷是為了彌補根據字尾名判斷的不足。
這部分程式碼中,要掃描PE檔案,涉及了PE檔案結構的知識,那麼就需要對PE檔案結構有一定的瞭解。
PE檔案是Windows系統上的可執行檔案,PE的全稱是Portable Execute,即:可移植的執行體。常見的EXE、DLL、OCX、SYS、COM都檔案都是PE檔案。PE檔案結構較為複雜,但在此只需瞭解部分相關知識,沒有必要詳盡說明,我們在程式中需用到的知識點是:判斷一個檔案是否是PE檔案、獲取PE檔案的節內容,如節的大小、節的雜湊值。
首先來看一下PE檔案結構圖:
PE檔案是由多個節組成的,每個節都是一個相對獨立的部分,相互之間又有著關聯。在檔案的開始部分,首先是三個頭部分,分別是:DOS頭、PE檔案頭、可選檔案頭,儲存著PE檔案的各種屬性資訊,比如檔案的入口點、節的個數、節的屬性、節的位置等等。後面跟著的是節頭和各節的資料,比如程式碼節、資源節等。每個節頭對應一個節,一個檔案可以有多個節頭和節,節頭中儲存著節的名稱、地址等重要資訊,根據這些資訊可以定位到節的地址,進而可以對節的內容進行讀取。在後面的內容中,會根據這些基本資訊和思路獲取檔案節的內容、節的特徵碼。
要操作PE檔案,就要在程式中定義與之相同的檔案格式。首先在程式中需要定義的是DOS頭,需如下編碼:
Private Type IMAGE_DOS_HEADER
Magic As Integer
cblp As Integer
cp As Integer
crlc As Integer
cparhdr As Integer
minalloc As Integer
maxalloc As Integer
ss As Integer
sp As Integer
csum As Integer
ip As Integer
cs As Integer
lfarlc As Integer
ovno As Integer
res(3) As Integer
oemid As Integer
oeminfo As Integer
res2(9) As Integer
lfanew As Long
End Type
DOS頭中除了最後的lfanew和e_magic兩個變數,其它內容對我們都不重要。這個部分,我們關心的僅為lfanew,該值指向了PE頭結構在檔案中的偏移。假設該值為0x40,那麼檔案中0x40位置開始就是PE頭的開始,標誌為"PE"兩個字元。判斷檔案是否是PE檔案,就是依靠這個。
PE結構第二部分:檔案頭:
Private Type IMAGE_FILE_HEADER
Machine As Integer
NumberOfSections As Integer
TimeDateStamp As Long
PointerToSymbolTable As Long
NumberOfSymbols As Long
SizeOfOtionalHeader As Integer
Characteristics As Integer
End Type
這部分沒有實際操作意義,但要定位到PE檔案的相應位置,它在程式中編碼時必須存在。
PE結構第三部分:可選檔案頭:
Private Type IMAGE_DATA_DIRECTORY
DataRVA As Long
DataSize As Long
End Type
DataRVA表示指向的地址是虛擬地址,就是程式載入後該地址在記憶體中相對於ImageBase的偏移,帶Virtual的地址是這樣:在檔案中的偏移為:Addr - 對應Section的VirtualAddress + 對應Section的檔案內起始偏移。
PE結構第四部分:節頭:
Private Type IMAGE_SECTION_HEADER
SectionName(7) As Byte
VirtualSize As Long
VirtualAddress As Long
SizeOfRawData As Long
PointerToRawData As Long
PointerToRelocations As Long
PLineNums As Long
RelocCount As Integer
LineCount As Integer
Characteristics As Long
End Type
變數SectionName中儲存的是節的名稱,比如.Text、.Res,佔8個位元組;變數的VirtualAddress是節的RVA,即虛擬地址;變數SizeOfRawData是節的大小;變數PointerToRawData是節的檔案偏移量。這幾個變數很重要,我們要依靠它們來獲節的內容,進而取得特徵碼。
這裡提到了特徵碼,在接下來進一步掃描檔案時,我們使用特徵碼匹配技術對檔案進行檢測,判斷是否是病毒木馬。為了便於理解之後的程式碼,我們先對本書中設計的防毒軟體的特徵碼、病毒庫規範進行一些講解。
注:作者:wing qq:6465660 本書理論及功能、程式碼源於防毒軟體:“Ty2y防毒軟體”,作者授意,文章可自由轉載,只需註明原出處即可,特此說明。