現(xiàn)在有一種流行的防病毒軟件被稱作HIPS,中文名字為主機入侵防御系統(tǒng),比如EQ。該軟件可以在進程創(chuàng)建時、有進程對注冊表進行寫入時或有驅(qū)動被加載時,給用戶予以選擇,選擇是否攔截進程的創(chuàng)建、是否攔截注冊表的寫入、是否攔截驅(qū)動的加載等功能。
HIPS純粹是以預防為主,比如有陌生的進程在被創(chuàng)建階段,就可以讓用戶禁止,這樣就避免了特征碼查殺的滯后性。對于殺毒軟件的特征碼查殺而言,如果殺毒軟件不更新病毒數(shù)據(jù)庫,那么依賴病毒特征碼的殺毒軟件就無法查殺新型的病毒,對新型的病毒就成為一個擺設。
行為監(jiān)控的原理主要就是對相關的關鍵API函數(shù)進行HOOK,比如進程攔截。當一個木馬程序要秘密啟動的時候,對CreateProcessW()函數(shù)進行了HOOK,在進程被創(chuàng)建前,會詢問用戶是否啟動該進程,那么木馬的隱秘啟動就被暴露出來了。對于沒有安全知識的大眾來說,使用HIPS可能有點困難,也許仍然會讓木馬運行。因為不是每個使用計算機的人都對計算機有所了解,計算機對于他們而言可能只用來打游戲或看電影。這該如何做呢?現(xiàn)在通常使用的方法就是使用白庫和黑庫,也就是所謂的白名單和黑名單。在進程被創(chuàng)建時,把要創(chuàng)建的進程到黑白庫中去匹配,然后做相應的動作,或者放行,或者攔截。
下面來實現(xiàn)一個應用層下的簡單的進程防火墻、注冊表防火墻的功能。
1. 簡單進程防火墻
進程防火墻指的是放行/攔截準備要創(chuàng)建的進程。進程的創(chuàng)建是依靠CreateProcessW()函數(shù)完成的。只要HOOK CreateProcessW()函數(shù)就可以實現(xiàn)進程防火墻的功能。對于注冊表來說,要對非法進程進行刪除或?qū)懭胱员礞I值進行管控,因此需要HOOK兩個注冊表函數(shù),分別是注冊表寫入函數(shù)RegSetValueExW()和注冊表刪除函數(shù)RegDeleteValueW()。由于使用了HOOK,那么就必然要涉及DLL的編寫。這里分DLL和EXE兩部分來進行詳細的介紹。
2. 實現(xiàn)HOOK部分的DLL程序的編寫
因為要對目標進程進行HOOK,因此要編寫DLL程序。創(chuàng)建一個DLL程序,并加入已封裝的ILHook.h頭文件和ILHook.cpp的實現(xiàn)文件。
為了能在所有的基于消息的進程中注入自己的DLL,必須使用Windows鉤子,這樣就可以將DLL輕易地注入基于消息的進程中。代碼如下:
#pragma data_seg(“.shared”)
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment (linker, “.shared, RWS”)
extern “C” __declspec(dllexport) VOID SetHookOn(HWND hWnd);
extern “C” __declspec(dllexport) VOID SetHookOff();
HWND g_ExeHwnd = NULL;
LRESULT CALLBACK GetMsgProc(
int code, // 鉤子編碼
WPARAM wParam, // 移除選項
LPARAM lParam // 消息
)
{
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
VOID SetHookOn(HWND hWnd)
{
g_ExeHwnd = hWnd;
SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInst, 0);
}
VOID SetHookOff()
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
以上函數(shù)用來定義導出函數(shù),用于加載完成HOOK功能的DLL文件。這里利用WH_ GETMESSAGE鉤子類型。
定義3個CILHook類的對象,分別用來對CreateProcessW()函數(shù)、RegSetValueExW()函數(shù)和RegDeleteValueW()函數(shù)進行掛鉤。具體定義如下:
CILHook RegSetValueExWHook;
CILHook CreateProcessWHook;
CILHook RegDeleteValueWHook;
HOOK部分是在DllMain()函數(shù)中完成的,具體代碼如下:
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
{
g_hInst = (HINSTANCE)hModule;
RegSetValueExWHook.Hook(“advapi32.dll”,
“RegSetValueExW”,(PROC)MyRegSetValueExA);
RegDeleteValueWHook.Hook(“advapi32.dll”,
“RegDeleteValueW”,(PROC)MyRegDeleteValueW);
CreateProcessWHook.Hook(“kernel32.dll”,
“CreateProcessW”,(PROC)MyCreateProcessW);
break;
}
case DLL_PROCESS_DETACH:
{
RegSetValueExWHook.UnHook();
RegDeleteValueWHook.UnHook();
CreateProcessWHook.UnHook();
if ( g_hHook != NULL )
{
SetHookOff();
}
break;
}
}
return TRUE;
}
放行/攔截部分是給用戶選擇的,那么就要給出提示讓用戶進行選擇,至少要給出放行/攔截的類型,比如是注冊表寫入或是進程的創(chuàng)建,還要給出是哪個進程進行的操作。要把這個信息反饋給用戶,這里定義一個結(jié)構體,將該結(jié)構體的信息發(fā)送給用于加載DLL的EXE文件,并讓EXE給出提示。結(jié)構體定義如下:
typedef struct _HIPS_INFO
{
WCHAR wProcessName[0x200];
DWORD dwHipsClass;
}HIPS_INFO, *PHIPS_INFO;
定義一些常量用來標識放行/攔截的類型,具體如下:
#define HIPS_CREATEPROCESS 0x00000001L
#define HIPS_REGSETVALUE 0x00000002L
#define HIPS_REGDELETEVALUE 0x00000003L
將這些定義好以后,就可以開始完成HOOK函數(shù)了。這里主要給出CreateProcessW()函數(shù)的HOOK實現(xiàn)。其余兩個函數(shù)的HOOK實現(xiàn),請大家自行實現(xiàn)。具體代碼如下:
BOOL
WINAPI
MyCreateProcessW(
__in_opt LPCWSTR lpApplicationName,
__inout_opt LPWSTR lpCommandLine,
__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in BOOL bInheritHandles,
__in DWORD dwCreationFlags,
__in_opt LPVOID lpEnvironment,
__in_opt LPCWSTR lpCurrentDirectory,
__in LPSTARTUPINFOW lpStartupInfo,
__out LPPROCESS_INFORMATION lpProcessInformation
)
{
HIPS_INFO sz = { 0 };
if ( wcslen(lpCommandLine) != 0 )
{
wcscpy(sz.wProcessName, lpCommandLine);
}
else
{
wcscpy(sz.wProcessName, lpApplicationName);
}
sz.dwHipsClass = HIPS_CREATEPROCESS;
COPYDATASTRUCT cds = { NULL, sizeof(HIPS_INFO), (void *)&sz };
BOOL bRet = FALSE;
if ( SendMessage(FindWindow(NULL, “Easy Hips For R3”),
WM_COPYDATA,GetCurrentProcessId(),(LPARAM)&cds) != -1 )
{
CreateProcessWHook.UnHook();
bRet = CreateProcessW(lpApplicationName, lpCommandLine,
lpProcessAttributes, lpThreadAttributes,
bInheritHandles, dwCreationFlags,
lpEnvironment, lpCurrentDirectory,
lpStartupInfo, lpProcessInformation);
CreateProcessWHook.ReHook();
}
return bRet;
}
這里使用了一個SendMessage()函數(shù),該函數(shù)用來發(fā)送一個WM_COPYDATA消息,將結(jié)構體傳給了加載DLL的EXE程序,使EXE程序把提示顯示給用戶。
SendMessage()函數(shù)的功能非常強大,其定義如下:
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
?。?/p>
該函數(shù)的第一個參數(shù)是目標窗口的句柄,第二個參數(shù)是消息類型,最后兩個參數(shù)是消息的附加參數(shù),根據(jù)消息類型的不同而不同。
以上代碼就是DLL程序的全部了,剩下兩個對注冊表操作的HOOK函數(shù),由大家自己完成。
3. 行為監(jiān)控前臺程序的編寫
先來看一下程序能達到的效果,再講解程序EXE部分的實現(xiàn)代碼,如圖1和圖2所示。
圖1 程序主界面
圖2 攔截提示框
從上面兩個圖可以看出,程序的確是可以攔截進程的啟動的。當單擊“允許”按鈕后,進程會被正常創(chuàng)建;當單擊“取消”按鈕后,進程將被阻止創(chuàng)建。這就是最終要完成的功能,來看看主要的實現(xiàn)代碼。
EXE的部分主要就是如何來啟動行為監(jiān)控功能,以及如何接收DLL程序通過SendMessage()函數(shù)發(fā)出的消息給用戶彈出提示框。進行攔截的部分已經(jīng)在DLL程序中通過HOOK實現(xiàn)了,所以重點也就在界面上和消息的接收上。
先看如何啟動和停止行為的監(jiān)控。具體代碼如下:
typedef VOID (*SETHOOKON)(HWND);
typedef VOID (*SETHOOKOFF)();
void CHipsDlg::OnBtnOn()
{
在此處添加處理程序的代碼
m_hInst = LoadLibrary(“EasyHips.dll”);
SETHOOKON SetHookOn = (SETHOOKON)GetProcAddress(m_hInst, “SetHookOn”);
SetHookOn(GetSafeHwnd());
FreeLibrary(m_hInst);
m_BtnOn.EnableWindow(FALSE);
m_BtnOff.EnableWindow(TRUE);
}
void CHipsDlg::OnBtnOff()
{
在此處添加處理程序的代碼
m_hInst = GetModuleHandle(“EasyHips.dll”);
SETHOOKOFF SetHookOff = (SETHOOKOFF)GetProcAddress(m_hInst, “SetHookOff”);
SetHookOff();
CloseHandle(m_hInst);
FreeLibrary(m_hInst);
m_BtnOn.EnableWindow(TRUE);
m_BtnOff.EnableWindow(FALSE);
}
從代碼中不難看出,直接調(diào)用了DLL的兩個導出函數(shù),就可以開啟自己的打開。在關閉時為什么調(diào)用了CloseHandle()函數(shù)和FreeLibrary()函數(shù)呢?把FreeLibrary()函數(shù)去掉,然后單擊“停止”監(jiān)控行為,但還是處在被監(jiān)控的狀態(tài)下。因為恢復Inline Hook是在DLL被卸載的情況。因此,在卸載時,調(diào)用GetModuleHandle()獲得本進程的DLL句柄后,雖然CloseHandle()了,但是只是減少了對DLL的引用計數(shù),并沒有真正釋放,必須再次使用FreeLibrary()函數(shù)才可以使DLL被卸載,從而恢復Inline Hook。
EXE程序接收DLL消息的代碼如下:
BOOL CHipsDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// 在此處添加處理程序的代碼
CTips Tips;
PHIPS_INFO pHipsInfo = (PHIPS_INFO)pCopyDataStruct->lpData;
wcscpy(Tips.sz, pHipsInfo->wProcessName);
Tips.DoModal();
int nNum = m_HipsReports.GetItemCount();
CString Str;
Str.Format(“%d”, nNum);
m_HipsReports.InsertItem(nNum, Str);
SYSTEMTIME StTime;
GetLocalTime(&StTime);
Str.Format(“%04d/%02d/%02d %02d:%02d:%02d”,
StTime.wYear,StTime.wMonth,StTime.wDay,
StTime.wHour,StTime.wMonth,StTime.wSecond);
m_HipsReports.SetItemText(nNum, 1, Str);
Str.Format(“%S”, Tips.sz);
m_HipsReports.SetItemText(nNum, 2, Str);
switch ( pHipsInfo->dwHipsClass )
{
case HIPS_CREATEPROCESS:
{
Str = “進程創(chuàng)建”;
break;
}
case HIPS_REGSETVALUE:
{
break;
}
case HIPS_REGDELETEVALUE:
{
break;
}
}
m_HipsReports.SetItemText(nNum, 3, Str);
Str.Format(“%s”, Tips.bRet ? “放行” : “攔截”);
m_HipsReports.SetItemText(nNum, 4, Str);
if ( Tips.bRet )
{
return 0;
}
else
{
return -1;
}
return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}
這部分代碼就是對WM_COPYDATA消息的一個響應,整個代碼基本是對界面進行了操作。在代碼中有一個CTips類的對象,這個類是用來自定義窗口的。該窗口就是用來提示放行和攔截的窗口,其主要代碼如下:
void CTips::OnBtnOk()
{
// 在此處添加處理程序的代碼
bRet = TRUE;
EndDialog(0);
}
void CTips::OnBtnCancel()
{
// 在此處添加處理程序的代碼
bRet = FALSE;
EndDialog(0);
}
DLL程序中的SendMessage()函數(shù)的返回要等待WM_COPYDATA的消息結(jié)束,并從中獲得返回值來決定下一步是否執(zhí)行,因此這里只要簡單地返回TRUE或FALSE即可。
對于行為監(jiān)控就介紹這么多。這個例子演示了如何通過Inline Hook達到對進程創(chuàng)建、注冊表操作的管控。當然,這里的代碼并不能管控所有的進程,而且這里的行為監(jiān)控過于簡單,很容易被惡意程序突破。這里主要是通過實例來完成對行為監(jiān)控原理的介紹,希望可以起到拋磚引玉的作用。