HOOK的方式有很多种,各有优缺点。IAT表的HOOK需要监视LIABRARY,GETPROCESS这些相关函数,真想实现起来还是很麻烦的。用内存补丁的方法,JMP到自己代码中,这个的缺点是需要在自己的代码里实现原来JMP处的指令。。。
现在我来说个新的思路,用异常来实现HOOK。我们都知道,当一个程序发生异常的时候,异常传递路径是:1.debuger 2. 向量化异常,即VEH 3.SEH。。。
如果在没有调试器的情况下,首先接受到异常的就是VEH了。那么我们可以在要HOOK的代码上插入一个 int3 ,然后通过VEH处理这个异常,从而实现HOOK的目的。原理很简单。用代码说话吧
代码:
// Hook.h: interface for the CHook class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_HOOK_H__C0F41F39_4EB7_4129_BFB7_156CB203826F__INCLUDED_)
#define AFX_HOOK_H__C0F41F39_4EB7_4129_BFB7_156CB203826F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <list>
using namespace std;
typedef void (*HOOKHANDLEFUN)(EXCEPTION_POINTERS *pExceptionInfo,void *pExtendData);
struct t_HookInfo
{
DWORD dwAddr;
HOOKHANDLEFUN pFn;
BYTE byOldCode;
};
class CHook
{
private:
static CHook *m_Instance; //单件模式
protected:
CHook(); //保证只生成一个实例
public:
static CHook* GetInstance();
t_HookInfo *m_pCurrentBP;
BOOL m_bSingleFlag;
EXCEPTION_POINTERS *m_pException;
void SetTF();
static LONG WINAPI VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo);
list<t_HookInfo*> m_listHook;
BOOL AddHook(DWORD dwHookAddr, HOOKHANDLEFUN pFn);
BOOL RemoveHook(DWORD dwHookAddr);
BOOL SetBP(t_HookInfo*);
BOOL UnSetBP(t_HookInfo*);
virtual ~CHook();
};
#endif // !defined(AFX_HOOK_H__C0F41F39_4EB7_4129_BFB7_156CB203826F__INCLUDED_)
代码:
// Hook.cpp: implementation of the CHook class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "HookDll.h"
#include "Hook.h"
#define TEST
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#ifdef TEST
#define TESTMSG(str) AfxMessageBox(str)
#else
#define TESTMSG(str)
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define BREAKPOINTLEN 2
typedef PVOID (WINAPI *ADDVECTOREDEXCEPTIONHANDLER)(ULONG,PVOID);
CHook* CHook::m_Instance;
CHook::CHook()
{
//清空链表
m_listHook.empty();
//安装顶层异常
HINSTANCE hMod = LoadLibrary("kernel32.dll");
ADDVECTOREDEXCEPTIONHANDLER pfn = (ADDVECTOREDEXCEPTIONHANDLER)
GetProcAddress(hMod,"AddVectoredExceptionHandler");
PVOID ret = pfn(1,VectoredHandler);
m_bSingleFlag = FALSE;
m_pCurrentBP = NULL;
}
//函数名称:GetInstance
//函数参数:无
//函数功能:返回一个类的实例。
//返回值: CHook*
CHook* CHook::GetInstance()
{
if (!m_Instance)
{
m_Instance = new CHook;
}
return m_Instance;
}
CHook::~CHook()
{
list<t_HookInfo*>::iterator it;
for (it = m_listHook.begin(); it!=m_listHook.end(); it++)
{
UnSetBP((*it));
delete (*it);
}
m_listHook.empty();
}
//函数名称:AddHook
//函数参数:1.要HOOK的地址 2.处理HOOK的函数指针
//函数功能:添加一个HOOKINFO到链表中。并安装一个HOOK
//返回值: 成功返回TRUE,否则FALSE
//注意: 本函数没有检查参数地址的有效性,由调用方检查。
BOOL CHook::AddHook(DWORD dwHookAddr, HOOKHANDLEFUN pFn)
{
t_HookInfo *pHookInfo = new t_HookInfo;
if (!pHookInfo)
{
return FALSE;
}
pHookInfo->dwAddr = dwHookAddr;
pHookInfo->pFn = pFn;
m_listHook.push_back(pHookInfo);
return SetBP(pHookInfo);
}
//函数名称:RemoveHook
//函数参数:1.要REMOVEHOOK的地址
//函数功能:删除一个HOOK
//返回值: 成功返回TRUE,否则FALSE
//注意: 本函数没有检查参数地址的有效性,由调用方检查。
BOOL CHook::RemoveHook(DWORD dwHookAddr)
{
list<t_HookInfo*>::iterator it;
for (it = m_listHook.begin(); it!=m_listHook.end(); it++)
{
if ((*it)->dwAddr == dwHookAddr)
{
UnSetBP(*it);
delete (*it);
m_listHook.remove(*it);
return TRUE;
}
}
return FALSE;
}
//函数名称:SetBP
//函数参数:1.t_HookInfo hook信息
//函数功能:写入一个INT3
//返回值: 成功返回TRUE,否则FALSE
//注意: 本函数没有检查参数地址的有效性,由调用方检查。
BOOL CHook::SetBP(t_HookInfo *pHookInfo)
{
DWORD dwOldProtect,dwNewProtect = PAGE_EXECUTE_READWRITE;
if (!pHookInfo)
{
return FALSE;
}
VirtualProtect((void*)pHookInfo->dwAddr,BREAKPOINTLEN,dwNewProtect,&dwOldProtect);
pHookInfo->byOldCode = *(BYTE*)(pHookInfo->dwAddr);
*(BYTE*)(pHookInfo->dwAddr) = 0xcc;
VirtualProtect((void*)pHookInfo->dwAddr,BREAKPOINTLEN,dwOldProtect,&dwNewProtect);
return TRUE;
}
//函数名称:SetBP
//函数参数:1.t_HookInfo hook信息
//函数功能:删除断点
//返回值: 成功返回TRUE,否则FALSE
//注意: 本函数没有检查参数地址的有效性,由调用方检查。
BOOL CHook::UnSetBP(t_HookInfo *pHookInfo)
{
DWORD dwOldProtect,dwNewProtect = PAGE_EXECUTE_READWRITE;
if (!pHookInfo)
{
return FALSE;
}
VirtualProtect((void*)pHookInfo->dwAddr,BREAKPOINTLEN,dwNewProtect,&dwOldProtect);
*(BYTE*)(pHookInfo->dwAddr) = pHookInfo->byOldCode;
VirtualProtect((void*)pHookInfo->dwAddr,BREAKPOINTLEN,dwOldProtect,&dwNewProtect);
return TRUE;
}
//函数名称:VectoredHandler
//函数参数:异常信息结构指针
//函数功能:处理异常
//返回值: 自己异常返回EXCEPTION_CONTINUE_EXECUTION,否则返回EXCEPTION_CONTINUE_SEARCH
LONG WINAPI CHook::VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
CHook *pHook = CHook::GetInstance();
//检测是否INT3断点
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
{
CString str;
str.Format("%-8x",ExceptionInfo->ExceptionRecord->ExceptionAddress);
// TESTMSG(str);
list<t_HookInfo*>::iterator it;
//检测是否自己设置的断点
for (it = pHook->m_listHook.begin(); it!=pHook->m_listHook.end(); it++)
{
if ((*it)->dwAddr ==(DWORD) ExceptionInfo->ExceptionRecord->ExceptionAddress)
{
//记录当前断点,设置单步标志,删除断点,
pHook->m_pException = ExceptionInfo;
pHook->SetTF();
pHook->UnSetBP(*it);
pHook->m_pCurrentBP = *it;
//调用相关函数
(*it)->pFn(ExceptionInfo,NULL);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
//单步断点的处理
else if ( ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP )
{
//判断是否自己的单步断点
if (pHook->m_bSingleFlag && pHook->m_pCurrentBP)
{
//还原断点,标志位。
pHook->SetBP(pHook->m_pCurrentBP);
pHook->m_bSingleFlag = FALSE;
pHook->m_pCurrentBP = NULL;
return EXCEPTION_CONTINUE_EXECUTION;
}
else
{
return EXCEPTION_CONTINUE_SEARCH;
}
}
else
{
return EXCEPTION_CONTINUE_SEARCH;
}
}
//函数名称:SetTF
//函数参数:无
//函数功能:设置一个单步断点
//返回值: 无
void CHook::SetTF()
{
if (m_pException)
{
m_pException->ContextRecord->EFlags |= 0x100;
m_bSingleFlag = TRUE;
}
}
1。由于VectoredHandler是WINDOWS回调函数,而我想把他封装在类中,如果想访问类的其他成员,则只能用静态变量了,所以我干脆把这个类设计成单件模式。有的人说了,用全局变量不是也可以么?全局变量确实是也可以达到目的,但是我总感觉他会破坏类的封装。。
2。当异常发生的时候,处理完了HOOK的事情,要先设置一个单步断点,然后把原来的代码还原,在下个单步断点的时候,再写入INT3
3。调试程序的郁闷。。我用VC调试这个程序的时候,发现异常代码总是被修改为STATUS_ACCESS_VIOLATION ,而非STATUS_BREAKPOINT,不知道啥原因。。希望明白人解答
4。在进行异常处理的时候,一些控件的消息通知无法正常运行,比如LIST控件的ADDITEM,CWND的UPDATEDATA。。。。。等,希望能有人解决下这个问题。我测试的时候只能用GetItemText,SetItemText了。
另外把工程文件也一起奉上。。。