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

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

3.2.防毒實現方案實編碼實現

防毒功能實現需要攔截程序啟動行為,在一個程序啟動前,先對其進行掃描,判斷是否是病毒,如果是病毒則立刻進行阻止。這也就是許多防毒軟體宣傳的主動防禦功能。

為了實現攔截程序啟動功能,我們這裡要使用API Hook技術。攔截到程序時,獲取到它的檔案特徵碼,並在本地病毒庫、黑名單、雲病毒庫中進行匹配校驗,如果檢測到是病毒,直接阻止。其程式流程如下圖所示: 在這裡插入圖片描述 實現攔截程序啟動,可以由多種技術手段實現。可以修改SSDT表通過驅動實現,這是較底層的方式;也可以通過在使用者層進行全域性DLL注入實現。兩者各有優劣。驅動方式更加接近系統底層,控制權限更高,但容易對系統穩定性造成影響,並且防毒軟體大多采用驅動方式,這樣便容易引發同類軟體衝突、與同類軟體不相容等情況。DLL方式工作在使用者層,對系統穩定性及效能影響小,並不易有同類軟體相容性問題。本防毒軟體設計中採用DLL注入方式。

大多數防毒軟體的防護功能,不僅僅會攔截程序啟動,還會對登錄檔讀寫、檔案讀寫等眾多操作進行監管。而本設計中,只攔截啟動行為,這樣做的原因在於:防護的最核心最關鍵的就在於程序執行這一步,如果這一步被病毒通過,那麼病毒可能會讀寫檔案,可能會讀寫登錄檔,也可能會格式化磁碟,甚至會強行刪除檔案,一切都是不可預料的,做為防毒軟體,不可能接管系統所有的操作,也就是說,如果被病毒執行,等於防線已經崩潰了,系統已經被破壞了。而且,接管系統的各種操作,在系統中沒有病毒的情況下,也會給系統本身帶來很大的效能負擔和執行風險。可能會使系統性能降低一半甚至更多,可能會使系統經常性的宕機、藍屏。用一個為了防止未知的風險,而付出如此多的代價是划不來的,使用者也不願意接受的。因此,在本軟體中,僅最大化的加強執行防護這一個防毒門檻。帶給使用者安全的同時,又不影響使用者使用電腦。

3.2.1.雲安全實現方案的引入

雲安全的優勢在於:在具有廣泛雲客戶端的基礎上,可以及時、實時的獲取最新的病毒木馬資訊,並同時加入到雲安全病毒庫中。

為了進一步增強防護功能,在本防毒軟體的防毒功能設計中引入了雲安全防毒功能。從理論上來講,雲安全的實現,最少是需要一臺伺服器的,用於架設伺服器、儲存資料。架設伺服器,需要雲後臺程式,儲存資料,需要用安裝資料庫。

本設計中使用的雲安全後臺使用ASP編寫。資料庫可以用MS-SQL或Access。

後臺程式實現兩個功能。其一,接收使用者端防毒軟體傳送的資料,資料為使用者執行程式時獲取的檔案特徵碼,這個行為將使用者端視為雲的客戶端,實現了採集資料的功能;其二,根據客戶端請求進行查詢並反饋結果。請求中附帶的依然是特徵碼。根據雲資料庫中的資料,後臺程式接收特徵碼進行校驗查詢,並反饋查詢結果給客戶端,這個操作用於防毒功能。即上文所講的防毒方案中的查詢雲安全病毒庫過程。接收到的資料儲存在資料庫中,可由維護人員操作,或由雲後臺根據資料情況按一定規則返回查詢結果給客戶端軟體。 在這裡插入圖片描述

資料量和使用者量都較小的時候,可以使用Access,伺服器可以使用虛擬主機代替。

介紹了雲安全的服務端設定,再來看攔截程序啟動的程式設計實現:

系統在啟動一個程序時,在使用者層是通過呼叫API函式:CreateProcess實現的。攔截程序啟動行為,我們要做的就是Hook這個API。要實現API Hook,並在攔截後執行相應的操作,單純VB是做不到的。這時需要用VC開發一個DLL檔案供VB呼叫。軟體執行時,DLL將被注入到所有系統程序中,接管各程序中的CreateProcess函式。這樣,無論是哪個程式呼叫CreateProcess啟動程式,DLL都會將暫停這個啟動行為,並將被啟動的檔案資訊發給防毒軟體,由防毒軟體判斷安全性,根據判斷結果決定是否允許程式啟動。如果這時檢測到是病毒在啟動,防毒軟體會告訴DLL:這是病毒,不能讓它啟動。病毒的啟動行為就被阻止了。

來看程式碼:

DLL部分:

Dll入口函式:
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
hMod = (HINSTANCE)hModule;
if (ul_reason_for_call==DLL_PROCESS_ATTACH)
{	
hProcess=OpenProcess(PROCESS_ALL_ACCESS,0, CurrentProcessId());

獲取我們設計的防毒軟體的標題,以便傳送訊息進行通迅:
hExe = FindWindow(NULL, "防毒軟體Demo");

DLL被載入時對createprocess函式進行Hook:
HookCreateProcess();

}

if (ul_reason_for_call==DLL_PROCESS_DETACH) 
{
HookStatus(FALSE);
}
return TRUE;
}

Hookcreateprocess函式:
BOOL HookCreateProcess()
{
ULONG JmpAddr=0;
	
獲取CreateProcessW函式原始地址:
FunAddr=(ULONG)GetProcAddress(LoadLibrary("Kernel32.dll"), "CreateProcessW");

儲存CreateProcessW函式原始地址,以備恢復hook時使用:
memcpy(OldCode, (void *)FunAddr, 5);

構造彙編跳轉指令:
NewCode[0] = 0xe9;
JmpAddr = (ULONG)CreateProcessWCallBack - FunAddr - 5;

用我們自定義的CreateProcessWCallBack函式取代CreateProcessW函式的地址,這樣當CreateProcessW系統函式被呼叫時,我們自定義的函式可以接管相關的操作:
memcpy(&NewCode[1], &JmpAddr, 4);

啟用API Hook,此時,當有啟動程序操作時,由CreateProcessWCallBack進行管理:
HookStatus(TRUE);
return TRUE;
}

CreateProcessW的替代函式,擁有與CreateProcessW相同的引數:
BOOL WINAPI CreateProcessWCallBack
(
LPCWSTR lpApplicationName, 
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,  
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL b=FALSE;
		
獲取要啟動的程序:
char AppName[256]={0};
WideCharToMultiByte(CP_ACP, 0,(const unsigned short *)lpApplicationName, -1, AppName, 256,NULL, NULL); 
char* CopyData="";

構造字串,內容是:程序和命令列。用於傳送給上層防毒軟體:
strcpy(CopyData,AppName);
strcat(CopyData,"$");
COPYDATASTRUCT cds={0};
cds.lpData = CopyData;
cds.cbData = 1024;

取得dll所在程序pid:
DWORD dwProcessId;
dwProcessId=GetCurrentProcessId();
	
DWORD ret;

向防毒軟體傳送訊息,訊息中包含著正要啟動的程序的路徑,並等待返回結果:
ret=SendMessage(hExe, WM_COPYDATA, dwProcessId, (LPARAM)&cds);

如果返回值是915,這個值是自定義的,這個值表明防毒軟體已經檢驗過被啟動檔案,並且是安全的,可以啟動。
if(ret==915)
{

設定攔截狀態,暫不啟用API Hook。如果此處不做修改,CreateProcessWCallBack會一直有效,形成錯誤的遞規呼叫,任何程式都啟動不了。
HookStatus(FALSE);

啟動程序:
b = CreateProcessW
(
lpApplicationName, 
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);

啟動後立刻啟用API Hook,繼續監控程序啟動行為:
HookStatus(TRUE);
return b;
}	
else
{	

如果返回值不是915,表達在防毒軟體中檢測到被啟動檔案是病毒或是其它惡意檔案。直接返回,阻止程式啟動。這時軟體中也有相應的提示及操作。
return FALSE;
}		
return FALSE;
}

設定API Hook啟用狀態狀態:
BOOL HookStatus(BOOL Status)
{
BOOL ret=FALSE;
if (Status) 
{	

將CreateProcessW入口地址寫為我們自己定義的函式,使API Hook啟用:
ret = WriteProcessMemory(hProcess, (void *)FunAddr, NewCode, 5, 0);
if (ret) return TRUE;
}
else 
{

將CreateProcessW入口地址寫為原始函式CreateProcessW的地址,API Hook停止
ret = WriteProcessMemory(hProcess, (void *)FunAddr, OldCode, 5, 0);
if (ret) return TRUE;
}
return FALSE;	
}

LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam) { return(CallNextHookEx(hHook,nCode,wParam,lParam) ); }

這是輸出函式,用於在應用程式中呼叫,關閉API HOOK:
BOOL WINAPI ActiveDefenseOFF()
{
return(UnhookWindowsHookEx(hHook));
}

這是輸出函式,用於在應用程式中呼叫,啟用API HOOK:
BOOL WINAPI ActiveDefenseON()
{
設定系統鉤子,向各活動程序注入此DLL
hHook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)HookProc, hMod, 0);
if (hHook) 
{
return TRUE;
}
else
{
return FALSE;
}
}

在此DLL中,使用SetWindowsHookEx函式向所有程序注入此DLL,DLL初注入後,接管各程序中的CreateProcessW系統函式,此函式可以控制程序啟動操作,當檢測到程序啟動後,暫停啟動行為,獲取程序的完整路徑,並傳遞給上層執行檔案,也就是我們的防毒軟體主程式。傳遞到主程式後,主程式中對傳來路徑的檔案進行掃描,此時DLL中還在等待上層主程式的處理結果,當在主程式中掃描完成後,反饋資訊給DLL:如果是病毒,會中止此程序的啟動行為,否則讓程式完成啟動。這樣就完成了對病毒的主動防禦。

在主程式中,為了與DLL呼應,完成相應的功能,需要兩方面的操作,一方面接管視窗訊息,以實現DLL傳送訊息的響應;另一方面,完成檔案掃描並向DLL迴應訊息。

編碼如下:

用SetWindowLong函式,將把程式的訊息權交給SubClassMessage函式,這樣就接管了程式的訊息管理:
SetWindowLong hWnd, -4, AddressOf SubClassMessage

這樣我們就可以在SubClassMessage函式中獲取DLL發來的資料。如下:

Public Function SubClassMessage(ByVal lHwnd As Long, ByVal lMessage As Long, ByVal lParentPID As Long, ByVal lParam As Long) As Long
...
Dim sTemp As String
Dim uCDS As COPYDATASTRUCT

判斷是否是DLL傳來的訊息,上文中DLL傳送訊息時的引數WM_COPYDATA值等於&H4A
If lMessage = &H4A Then
   
CopyMemory uCDS, ByVal lParam, Len(uCDS)
sTemp = Space(uCDS.cbData)
CopyMemory ByVal sTemp, ByVal uCDS.lpData, uCDS.cbData
    
獲取攔截到的正要啟動的程式
Dim sFile As String
sFile = Left(sTemp, InStr(1, sTemp, "$") - 1)
sTemp = Right(sTemp, Len(sTemp) - InStr(1, sTemp, "$"))

end if
End function

獲取到要啟動的程式後,便可使上前面介紹過的掃描病毒程式碼對檔案進行檢測。並根據掃描結果向DLL返回訊息。編碼如下:

Dim sScanResult As String sScanResult = ScanFile(sFile) If sScanResult ="" then SubClassMessage =915 else SubClassMessage = 0 End if

同樣是呼叫前面講過的ScanFile函式對檔案進行掃描,如果返回為空說明沒有檢測到病毒,檔案是安全的,則給SubClassMessage賦值915。這句程式碼程式碼的意思是:SubClassMessage函式是主程式負責訊息處理的函式,DLL中將訊息傳送到主程式後,將由此函式負責處理,此函式獲取賦值後,相當於是函式的返回值,也就是DLL中使用SendMessage函式傳送訊息後要等待的訊息返回結果。這裡的賦值,會直接傳遞給DLL。而915這個數字我們在前面已經提過,是自定義的一個數值,如果DLL中接收到此值,表明檔案沒有掃描到病毒,檔案是安全的。反之返回0的含意與此類似,只是功能相反。

這是防毒功能中使用傳統特徵碼掃描識別的實現,我們前面講過要在防毒中使用雲安全技術。接下來進行相關介紹。

當本地病毒庫特徵碼校驗沒有掃描到病毒後,時此可以使用雲安全掃描,進行二次檢測。編碼如下: sCloudAskRet=CloudMatch(“url/”&HashFile(sFile))

我們約定:如果返回值不為空說明是病毒:
If sCloudAskRet <> "" Then
Dim sCloudName As String

此處是獲取返回值中對病毒檔案描述,返回的資料中用“sCloudName=Right(sCloudAskRet,Len(sCloudAskRet)InStr(1,sCloudAskRet,&quot;”符號做為判斷分隔符: sCloudName = Right(sCloudAskRet, Len(sCloudAskRet) - InStr(1, sCloudAskRet, &quot;"))

用與上面同樣的方法向DLL迴應訊息:                        
SubClassMessage = 0
End If

這裡需要說明的是,使用雲安全檢測,同樣是需要提取檔案的特徵碼,並將特徵碼傳送給雲安全伺服器。在這裡我們使用不同於病毒庫中的特徵碼格式,雲安全中將檔案整體的雜湊值做為特徵碼。這樣做是為了儲存和查詢的方便。上面程式碼中HashFile函式,用於獲取檔案的雲安全特徵碼。編碼如下:

Function HashFile(ByVal sFilename As String) As String

HashFile = ""
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

If Len(Dir(sFilename)) = 0 Then
Exit Function
End If

lRes=CryptAcquireContext(lCtx,vbNullString,vbNullString, PROV_RSA_FULL, 0)
If lRes <> 0 Then
lRes = CryptCreateHash(lCtx, ALGORITHM, 0, 0, lHash)
If lRes <> 0 Then
lFile = FreeFile
Open sFilename For Binary As #lFile
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 = LOF(lFile) \ BLOCK_SIZE
lLastBlock = LOF(lFile) - lBlocks * BLOCK_SIZE
For lCount = 1 To lBlocks
Get lFile, , bBlock
lRes = CryptHashData(lHash, bBlock(1), BLOCK_SIZE, 0)
Next
If lLastBlock > 0 And lRes <> 0 Then
ReDim bBlock(1 To lLastBlock) As Byte
Get lFile, , bBlock
lRes = CryptHashData(lHash, bBlock(1), lLastBlock, 0)
End If
Close lFile
If lRes <> 0 Then
lRes = CryptGetHashParam(lHash, HP_HASHSIZE, lLen, 4, 0)
If lRes <> 0 Then
ReDim bHash(0 To lLen - 1)
lRes = CryptGetHashParam(lHash, HP_HASHVAL, bHash(0), lLen, 0)
If lRes <> 0 Then
For lIdx = 0 To UBound(bHash)
HashFile = HashFile & Right("0" & Hex(bHash(lIdx)), 2)
Next
End If
End If
End If
CryptDestroyHash lHash
End If
End If
CryptReleaseContext lCtx, 0
End Function

此函式跟前面講過的HashFileStream函式程式碼非常接近,上文中已對HashFileStream函式做過詳細講解,在此不再綴述。

上面的程式碼中獲取了檔案的雲安全特徵碼後,使用CloudMatch函式進行雲安全校驗。CloudMatch函式中將連線雲安全伺服器,向雲後臺傳遞雲安全特徵碼,通過呼叫雲安全後臺程式判斷檔案是否是病毒,編碼程式碼如下:

Public Function CloudMatch(sUrl As String) As String

Dim lInternetOpenUrl As Long
Dim lInternetOpen As Long
Dim sTemp As String * 1024
Dim lInternetReadFile As Long
Dim lSize As Long
Dim sContent As String
sContent = vbNullString
      
lInternetOpen = InternetOpen("Cloud", 1, vbNullString, vbNullString, 0)
      
If lInternetOpen Then
    
連線雲後臺,sUrl變數中儲存的是雲後臺程式的網路地址:
lInternetOpenUrl = InternetOpenUrl(lInternetOpen, sUrl, vbNullString, 0, INTERNET_FLAG_NO_CACHE_WRITE, 0)
    
If lInternetOpenUrl Then
Do

讀取雲後臺反饋的內容:
lInternetReadFile = InternetReadFile(lInternetOpenUrl, sTemp, 1024, lSize)
sContent = sContent & Mid(sTemp, 1, lSize)
Loop While (lSize <> 0)
End If

如果雲安全後臺檢測到病毒,返回的內容格式為:Cloud$(雲安全標識)+病毒名稱/病毒描述

If UCase(Left(sContent, 6)) = UCase("Cloud$") Then
    CloudMatch = Right(sContent, Len(sContent) - 6)
Else
    CloudMatch = ""
End If

End Function

3.2.2.雲安全服務端程式設計

上面的程式碼中執行雲安全查詢時,是這樣呼叫的:“sCloudAskRet = CloudMatch("http://xxx.com/ask.asp?Hash=" & HashFile(sFile))”

不難看出,“w2sft”此地址便是雲安全的後臺程式,在本例中,後臺程式由asp寫成,負責處理防毒軟體發起的查詢,並返回雲安全掃描結果。編碼如下:

<%

取得防毒軟體中傳遞的檔案特徵碼:

dim sHash
sHash=trim(request("Hash"))
	
dim conn
dim connstr
dim rs
dim sql

連線資料庫,雲安全的特徵碼儲存在資料庫,資料庫可以使用MsSql、MySql等各種資料庫,甚至是Access,為了演示編碼方便,這裡我們採用最簡單的Access資料庫 :

set conn=server.createobject(“ADODB.CONNECTION”) onnstr=“DBQ=”+server.mappath(“cloud.mdb”)+";DefaultDir=;DRIVER={Microsoft Access Driver (*.mdb)};" conn.open connstr set rs=createobject(“adodb.recordset”)

進行SQL查詢,病毒庫中是否有傳特來的特徵碼:

sql="select * from Cloud where Hash='" & sHash & "'"
rs.open sql,conn,1,3

if rs.recordcount=1 then
response.write "Cloud$雲安全檢測到病毒"
end if

rs.close
set rs=nothing 
set conn=nothing 
%>

到此,防毒和防毒兩個最核心功能的基本講解已完成。也許有讀者會疑惑、會意猶未盡:為什麼這麼簡單?是的,確實很簡單,到此,本書使用較少量的程式碼已經完成了這兩個最重要功能的介紹和編碼實現演示,之所以使用能由如此少量的程式碼完成,原因在於省略掉了其它部分程式碼量非常大的會影響功能實現直觀性的編碼,只保留了相關的核心程式碼。這是一種模組化的功能演示編碼,也只有這樣,才能明瞭的完成這兩個功能的講解。其它相關部分,如黑白名單的使用、設定選項的關聯、與介面的互交程式碼等等會單獨進行講解。本書後面章節中講述其它功能時,也會採用類似的方法。