3.功能实现

在上面的章节中,对软件的功能进行了初步设定,接下来将要以编程的思路,将这些功能进行梳理、整合,确定模块化的编程实现方案,并编码实现。

本书中的编码中,会使用到VB、VC、ASP、汇编四种编程语言,主要功能由VB编写、某些功能需要使用DLL,DLL代码由VC编写,ASP语言用在云安全后台,汇编是在某些环节在DLL中内嵌使用。要深入理解本书内容,需要对此三种编程语言熟悉,最低要求是有一定的了解,能够读懂相关代码,本书非编程语言教程,不讲解编程基础知识。

也许有程序员会质疑:VB能写的了杀毒软件吗?
用事实说话往往是最有说服力的:微软发行的Microsoft AntiSpyware软件,便是用VB开发而成的。

规则约定:在本书代码中,对系统API函数,使用粗体字,复杂的API函数,会进行解释说明。对于重要的自定义的函数、变量,使用之处用加粗加大字体。

3.1.杀毒实现方案及编码实现

如上文所讲,本软件中将杀毒分为三种模式:快速扫描、全盘扫描、自定义扫描。

这三种扫描方式,展现在用户面前时是3种不同的操作,而在程序中的编码是非常相近的,不同在于扫描的目标文件对像不同。

快速扫描对加载在内存中的文件进行扫描;

全盘扫描对整个硬盘中的文件扫描;

自定义扫描对选中的目标文件进行扫描。

如下图所示:
病毒检测与杀毒技术大揭秘3-LMLPHP

在此,以快速扫描为例,进行编码说明 。

快速扫描要扫描内存中的文件,如何确定哪些文件在内存中被使用呢?这里我们通过遍历系统活动进程及进程相关模块文件的方式来实现。

编码如下:

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文件结构图:
病毒检测与杀毒技术大揭秘3-LMLPHP

 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杀毒软件”,作者授意,文章可自由转载,只需注明原出处即可,特此说明。

10-07 18:29