前面提到过,我们的CHookApi类主要向外部提供2个方法,HookAllAPI方法和UnhookAllAPI方法。当调用HookAllAPI的时候,将拦截系统所有用户程序中我们感兴趣的API函数。当调用UnhookAllAPI的时候,将撤销拦截。当拦截启动之前,我们应该将所有我们感兴趣的,即需要拦截的API信息(如API名称,对应的替换函数名称、参数个数等)交给CHookApi,CHookApi类内部才能完成所有的拦截工作。以下是CHookApi类的声明
(HookApi.h)
#include "../include/CommonHeader.h"
#include <Psapi.h>
#include <tlhelp32.h>
#pragma comment(lib,"Psapi.lib")
// 通用替换函数指针声明
typedef void(*COMMONFUNC)(void);
/*
CHookApi类实现了Hook的核心功能
*/
class CHookApi
{
public:
// 构造函数
CHookApi(void);
// 析构函数
~CHookApi(void);
private:
// 自身路径
static char m_szDllPathName[MAX_PATH];
// 此容器包含所有要Hook的API信息(在类中使用的有效信息)
static vector<APIINFO*> m_vpApiInfo;
// 此容器包含所有要Hook的API信息(用户填写的信息)
vector<HOOKAPIINFO*>* m_vpHookApiInfo;
// 防火墙策略模块句柄
HMODULE m_hFireWall;
// 加载防火墙策略模块
BOOL LoadFireWallModule(void);
// 卸载防火墙策略模块
BOOL UnloadFireWallModule(void);
public:
// 使用远程线程注入的方法将我们的DLL注入到指定进程
static int WINAPI InjectDll(DWORD dwProcessId, LPTSTR lpDllName);
// 使用远程线程注入的方法将我们的DLL卸载
static int WINAPI EjectLib(DWORD dwProcessId, LPTSTR lpDllName);
protected:
// 通用替换函数,这是技术核心部分
static void CommonFunc(void);
private:
// 初始化函数
BOOL Init(void);
// 挂钩一个指定API的函数
BOOL HookOneApi(APIINFO* pai);
// 挂钩所有指定API的函数
BOOL HookAllApis(void);
// 取消挂钩一个指定API的函数
BOOL UnhookOneApi(APIINFO* pai);
// 取消挂钩所有指定API的函数
BOOL UnhookAllApis(void);
// 设置进程内存区域的存取权限
BOOL SetMemmoryAccess(APIINFO* pai,BOOL CanWritten);
// 修改/恢复指定API的前5个字节
BOOL SetCallMemmory(APIINFO* pai,BOOL bHook);
};
其中HOOKAPIINFO结构填写基本的拦截信息,在CHookApi内部将会把它转化为APIINFO结构。HOOKAPIINFO和APIINFO结构定义在CommonHeader.h文件中,CommonHeader.h文件如下:
(CommonHeader.h)
#define FIREWALLDLLNAME "FireWallDll.Dll" // 实现被拦截的API替换函数的名字
#define MAINHOOKDLLNAME "MainHookDll.Dll" // 拦截API主模块的名字
#define GETHOOKINFOFUNCNAMEINFIREWALLDLL "GetHookApiInfo" // 替换模块导出函数,它返回要拦截的信息
#define FREEHOOKINFOFUNCNAMEINFIREWALLDLL "FreeHookApiInfo" // 替换模块导出函数,它释放要拦截的信息
#define CALLCODE 0xE8
#ifndef COMMONHEADER_H
#define COMMONHEADER_H
#include <vector>
using namespace std;
typedef void(*CMAPIFUNC)(void);
// 关于API函数信息的结构
typedef struct _APIINFO
{
// 要拦截的API函数名
char szOrgApiName[100];
// 要拦截的API的地址
CMAPIFUNC lfOrgApiAddr;
// 我们要替换原来API的地址
CMAPIFUNC lfMyApiAddr;
// 要保存的原来API入口点的前5个字节
BYTE OrgApiBytes[5];
// 对进程内存的保护状态,调用VirtualProtect来改变对内存访问权限时得到的。
DWORD dwOldProtectFlag;
// 参数的个数
int ParamCount;
// 临界对象,为了互斥用,避免同时修改原始api的前5个字节
CRITICAL_SECTION cs;
// 是否已经HOOK了
BOOL bIsHooked;
// 用户需要使用的结构,通过这个结构来了解用户需要拦截的API的信息
typedef struct _HOOKAPIINFO
{
union
{
struct
{
// 我们要替换的API所在的DLL的名字
char szMyModuleName[100];
// 我们要替换原来API的函数名字
char szMyApiName[50];
} MyApi;
CMAPIFUNC lfMyApi;
};
// 要拦截的API所在DLL的名字
char szOrgModuleName[100];
// 要拦截的API的名字
char szOrgApiName[50];
// 参数的个数
int ParamCount;
// 提供MyAPI的方式
BOOL bMyApiType; // 如果是0代表提供地址,1代表使用模块名和函数名指定
} HOOKAPIINFO;
// 获得要拦截的API的信息函数指针
typedef vector<HOOKAPIINFO*>*(*GETHOOKAPIINFO)(void);
// 释放要拦截的API的信息函数指针
typedef void(*FREEHOOKAPIINFO)(void);
// 远程注入函数指针
typedef int (*INJECTDLL)(DWORD dwProcessId, LPTSTR lpDllName);
#endif
在这个头文件中,除了定义了HOOKAPIINFO和APIINFO结构还有一些其它定义,FIREWALLDLLNAME宏指定防火墙策略模块的文件名称,MAINHOOKDLLNAME宏指定本模块的文件名称,GETHOOKINFOFUNCNAMEINFIREWALLDLL宏指定在防火墙策略模块中用来返回用户的HOOKAPIINFO结构容器指针的函数名称,程序将会自动加载防火墙策略模块并调用这个函数获得用户的HOOKAPIINFO结构容器的指针,根据后面的函数指针的定义不难看出,这个函数必须是一个返回值为vector<HOOKAPIINFO*>*,并且没有参数的导出函数。FREEHOOKINFOFUNCNAMEINFIREWALLDLL 是释放返回的vector<HOOKAPIINFO*>*指针的导出函数名字。CALLCODE宏指定要替换的跳转指令,这里只用CALL指令,他的机器码是E8。
在构造函数中调用Init函数和HookAllApis函数,这样使该类被构造的时候就能够自动进行初始化和hook工作。Init函数的主要工作是将用户提供的HOOKAPIINFO结构转化成APIINFO结构。HookAllApis函数内部循环调用HookOneApi函数进行真正的Hook操作。详见源代码中的注释。
--------------------------------------
联系我(liutao_free@sohu.com)
--------------------------------------
联系我(liutao_free@sohu.com)