标签:姜晔 安全 木马 病毒 inline hook
之前文章中所讨论的恶意程序的应对方法,都是十分被动的,即只有当恶意程序被执行后,才考虑补救措施。这样,我们就会一直处于后手状态,而如果说病毒的危害性极大,那么即便我们完美地修复了诸如注册表项,服务项等敏感位置,并且删除了病毒本身,但是它依旧可能已经破坏了系统中非常重要的文件,造成了不可逆的损伤。因此这篇文章就来简单讨论一下利用Inline HOOK技术实现主动防御,在病毒执行前,就主动将危险函数劫持,如同一道防火墙,保护我们计算机的安全。
我们平时所使用的API函数都保存在操作系统提供的DLL文件中,当程序需要使用某个API函数时,在程序运行后,程序会隐式地将API函数所在的DLL加载到内存中。这样,程序就会像调用自己的函数一样调用API。依据这个原理,假设我们要对系统中的某个API函数进行HOOK,那么首先就需要在指定进程的内存中找到该函数的地址,然后修改该函数首地址的代码为“jmp MyProc”指令,这里的“MyProc”就是我们自己编写的欲执行的函数。这样一来,当指定的进程想要正常调用该函数时,就会直接跳到我们自己所编写的函数中去执行,这样就完成了Inline HOOK。将流程归纳如下:
(1)构造跳转指令,即jmp MyProc。
(2)在内存中找到欲HOOK函数的地址(这可以通过使用GetProcAddress()函数实现),然后保存欲HOOK位置处的前5个字节(跳转指令需占用5个字节)。
(3)将构造的跳转指令写入需要HOOK的位置处,这样当被HOOK的位置执行时,就会跳转到我们的函数执行。
(4)如果要执行原来的函数,那么需要取消HOOK,还原第(2)步中保存的5个字节。
(5)执行原来的流程。
那么我们接下来的编程就依照这个流程来进行。
为了方便起见,对于HOOK技术的编程我这里采用面向对象的思想。用C++封装一个Inline HOOK类,这样以后的编程也可以使用它。
一般来说,封装的类都有两个文件,一个是类的头文件,另一个是类的实现文件。类名一般都是以字母“C”为开头,表示“Class Name”,因此这里的类名为“CInlineHOOK”,头文件我们起名为“CInlineHOOK.h”,那么类的实现文件命名为“CInlineHOOK.cpp”。首先是类的头文件代码:
#include <Windows.h>
class CInlineHOOK
{
public:
CInlineHOOK(); // 构造函数,用于初始化
~CInlineHOOK(); // 析构函数,用户程序结束后资源的释放
// HOOK函数
BOOL HOOK(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc);
// 取消HOOK函数
void UnHOOK();
// 重新进行HOOK函数
BOOL ReHOOK();
private:
PROC m_pfnOrig; // 自定义的函数的地址
BYTE m_bOldBytes[5]; // 原始函数入口代码
BYTE m_bNewBytes[5]; // 构造的跳转指令的代码
}; 头文件中主要是声明一些需要使用的函数与变量,代码中已给出了相应的注释。接下来是类的实现文件的代码:
#include "stdafx.h"
#include "InlineHOOK.h"
CInlineHOOK::CInlineHOOK()
{
// 对成员变量的初始化
m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
}
CInlineHOOK::~CInlineHOOK()
{
// 取消HOOK
UnHOOK();
}
//挂钩函数,参数依次为模块名称、函数名称以及自定义的钩子函数
BOOL CInlineHOOK::HOOK(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc)
{
BOOL bRet = FALSE;
// 获取指定模块中函数的地址
m_pfnOrig = (PROC)GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName);
if ( m_pfnOrig != NULL )
{
// 保存该地址处前5个字节的内容
DWORD dwNum = 0;
ReadProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);
// 构造JMP指令,"\xe9"为jmp的Opcode
m_bNewBytes[0] = '\xe9';
// pfnHookFunc是HOOK后的地址,m_pfnOrig是原来的地址,5是指令长度
*(DWORD *)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5;
// 将构造好的地址写入该地址处
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);
bRet = TRUE;
}
return bRet;
}
//取消函数的挂钩
void CInlineHOOK::UnHOOK()
{
if ( m_pfnOrig != 0 )
{
DWORD dwNum = 0;
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);
}
}
//重新对函数进行挂钩
BOOL CInlineHOOK::ReHOOK()
{
BOOL bRet = FALSE;
if ( m_pfnOrig != 0 )
{
DWORD dwNum = 0;
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);
bRet = TRUE;
}
return bRet;
}
以上就是整个Inline HOOK的封装,代码非常简单,这里不再赘述。以后的研究文章中,还会用到这个类。因为利用它可以很容易地实现对函数的HOOK。
这里我们结合我之前所写的文章《反病毒攻防研究第010篇:DLL注入(中)——DLL注入与卸载器的编写》进行讨论。在那篇文章中,我使用DLL注入器将模拟病毒程序的DLL文件注入到了记事本进程中,这样当DLL成功注入后,用于模拟病毒的对话框程序就自动运行了。而在这篇文章中,我同样也是要编写一个DLL文件,用这个DLL文件来劫持“病毒”程序。这里是假设MessageBoxA()就是一个危险函数,也是我们所要劫持的目标。首先将本篇文章所生成的DLL注入,然后再注入“病毒”DLL进行验证。
同样需要建立一个简单的Win32 Dynamic Link Library项目,然后添加如下代码:// HookMessageBoxA.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "InlineHOOK.h"
CInlineHOOK MsgHOOK;
// 我们实现的HOOK函数
int
WINAPI
MyMessageBoxA(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
)
{
//先卸载Inline HOOK,以显示用户提示对话框
MsgHOOK.UnHOOK();
if ( MessageBox(NULL,"检测到可疑程序正在启动,是否拦截?", "提示", MB_YESNO) == IDNO )
{
//用户选择了不进行拦截
MessageBoxA(hWnd,lpText,lpCaption,uType);
MessageBox(NULL, "疑似恶意程序未被拦截", "提示", MB_OK);
MsgHOOK.ReHOOK();
}
else
{
//用户选择了进行拦截
MessageBox(NULL, "疑似恶意程序拦截成功", "提示", MB_OK);
MsgHOOK.ReHOOK();
}
return 0;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch ( ul_reason_for_call )
{
//当DLL被某进程加载时DllMain被调用
case DLL_PROCESS_ATTACH:
{
MsgHOOK.HOOK("user32.dll",
"MessageBoxA",
(PROC)MyMessageBoxA);
break;
}
//当DLL被某进程卸载时DllMain被调用
case DLL_PROCESS_DETACH:
{
MsgHOOK.UnHOOK();
break;
}
}
return TRUE;
} 然后将我们之前封装的类的两个文件添加到工程中,如下图所示:编译生成DLL文件,就可以进行测试了。
图2 成功拦截可疑程序
可见我们的Inline HOOK已经成功将MessageBox()函数拦截下来了,此时点击“是”:
图3 提示恶意程序拦截成功
程序会提示说拦截成功,而用于模拟病毒的对话框也并未弹出。此时再查看一下记事本进程中的模块信息:
图4 查看记事本进程的模块信息
可见我们所注入的两个DLL文件都在记事本进程中,但是HackedDll.dll已经失效,对我们的系统已经不会产生威胁了。如果在图2中所示的对话框选择了“否”,表明我们不进行拦截,则会如下图所示:
图5 放弃拦截
此时用于模拟病毒的对话框程序弹出,如果这个是真的病毒的话,那么我们的计算机就已经被感染了。点击“确定”:
图6 提示恶意程序未被拦截
至此,我们的Inline HOOK程序的目的达到,说明是可行的。
反病毒攻防研究第012篇:利用Inline HOOK实现主动防御
标签:姜晔 安全 木马 病毒 inline hook
原文地址:http://blog.csdn.net/ioio_jy/article/details/39892655