HOOK挂钩技术

1.Address Hook

地址hook顾名思义,就是将原函数地址直接替换成hook函数地址

1.1.PE的IAT

因为IAT具体指某个PE模块的IAT,所以他的作用范围只针对被Hook的模块,且必须在以静态链接的方式调用API时才会被Hook,在使用LoadLibrary或GetProcAddress进行动态调用时不受影响。

//指向IAT中pThunk的地址
PULONG_PTR g_PointerToIATThunk = NULL;
//MessageBoxA的函数原型
typedef int(WINAPI* PFNMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

PFNMessageBoxA OldMessageBox = NULL;

//自定义的MessageBoxA函数
int WINAPI NEW_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
    //干别的事
    //取得原函数地址
    OldMessageBox(hWnd, "消息框", "测试", 0);
    //调用原函数
    int ret = OldMessageBox(hWnd, lpText, lpCaption, uType);
    return ret;
}
BOOL InstallModuleIATHook(
	HMODULE hModToHook,// IN
	const char* szModuleName,// IN
	const char* szFuncName,// IN
	PVOID DetourFunc,// IN
	PULONG_PTR* pThunkPointer,//OUT
	ULONG_PTR* pOriginalFuncAddr//OUT
)
{
	PIMAGE_IMPORT_DESCRIPTOR  pImportDescriptor;
	PIMAGE_THUNK_DATA         pThunkData;
	ULONG ulSize;
	HMODULE hModule = 0;
	ULONG_PTR TargetFunAddr;
	PULONG_PTR lpAddr;
	char* szModName;
	BOOL result = FALSE;
	BOOL bRetn = FALSE;

	hModule = LoadLibrary(szModuleName);
	TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName);
	printf("[*]Address of %s:0x%p\n", szFuncName, TargetFunAddr);
	printf("[*]Module To Hook at Base:0x%p\n", hModToHook);
	pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);;
	printf("[*]Find ImportTable,Address:0x%p\n", pImportDescriptor);
	while (pImportDescriptor->FirstThunk)
	{
		szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name);
		printf("[*]Cur Module Name:%s\n", szModName);
		if (stricmp(szModName, szModuleName) != 0){
			printf("[*]Module Name does not match, search next...\n");
			pImportDescriptor++;
			continue;
		}
		//程序的导入表处理完毕后OriginalFirstThunk可能是无效的,不能再根据名称来查找,而是遍历FirstThunk直接根据地址判断
		pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk);
		while (pThunkData->u1.Function)
		{
			lpAddr = (ULONG_PTR*)pThunkData;
			//找到了地址
			if ((*lpAddr) == TargetFunAddr){
				printf("[*]Find target address!\n");
				//通常情况下导入表所在内存页都是只读的,因此需要先修改内存页的属性为可写
				DWORD dwOldProtect;
				MEMORY_BASIC_INFORMATION  mbi;
				VirtualQuery(lpAddr, &mbi, sizeof(mbi));
				bRetn = VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);
				if (bRetn){
					//内存页属性修改成功,继续下一步操作,先保存原始数据
					if (pThunkPointer != NULL)
						*pThunkPointer = lpAddr;
					if (pOriginalFuncAddr != NULL)
						*pOriginalFuncAddr = *lpAddr;
					//修改地址
					*lpAddr = (ULONG_PTR)DetourFunc;
					result = TRUE;
					//恢复内存页的属性
					VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0);
					printf("[*]Hook ok.\n");
				}
				break;
			}
			pThunkData++;
		}
		pImportDescriptor++;
	}
	FreeLibrary(hModule);
	return result;
}

BOOL IAT_InstallHook()
{
    BOOL bResult = FALSE;
    HMODULE hCurExe = GetModuleHandle(NULL);
    PULONG_PTR pt;
    ULONG_PTR OrginalAddr;
    bResult = InstallModuleIATHook(hCurExe, "user32.dll", "MessageBoxA", (PVOID)NEW_MessageBoxA, &pt, &OrginalAddr);
    if (bResult)
    {
        printf("[*]Hook安装完毕! pThunk=0x%p  OriginalAddr = 0x%p\n", pt, OrginalAddr);
        g_PointerToIATThunk = pt;
        OldMessageBox = (PFNMessageBoxA)OrginalAddr;
    }
    return bResult;
}

VOID IAT_UnInstallHook()
{

	DWORD dwOLD;
	MEMORY_BASIC_INFORMATION  mbi;
	if (g_PointerToIATThunk)
	{
		//查询并修改内存页的属性
		VirtualQuery((LPCVOID)g_PointerToIATThunk, &mbi, sizeof(mbi));
		VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
		//将原始的MessageBoxA地址填入IAT中
		*g_PointerToIATThunk = (ULONG)OldMessageBox;
		//恢复内存页的属性
		VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
	}

}

1.2.PE的EAT

它存放的不是函数地址,而是函数地址的偏移,使用时需要加上模块基址。EAT并不直接起作用,它只能影响Hook之后对该函数的获取或者后续加载的模块IAT——看起来就像新加载的模块被安装了IAT Hook。

#include <windows.h>
#include <stdio.h>
#include <imagehlp.h>

#pragma comment(lib,"imagehlp.lib")

typedef int
(WINAPI* PFN_MessageBox)(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
	);

int WINAPI My_MessageBox(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
);

BOOL InstallModuleEATHook(
	HMODULE hModToHook,// IN
	const char* szFuncName,// IN
	PVOID ProxyFunc,// IN
	PULONG_PTR* pAddrPointer,//OUT
	ULONG_PTR* pOriginalFuncAddr//OUT
);

int main(int argc, char* argv[])
{
	HMODULE hUser32 = LoadLibrary("user32.dll");
	PULONG_PTR pEATPointer;
	ULONG_PTR uOldRVA;
	InstallModuleEATHook(hUser32, "MessageBoxA", My_MessageBox, &pEATPointer, &uOldRVA);
	printf("pEATPointer = 0x%X OldRVA = 0x%X\n", pEATPointer, uOldRVA);


	//Test
	printf("Now Test the EAT Hook.\n");
	PFN_MessageBox pMsgBox = (PFN_MessageBox)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA");
	printf("dwAddr = 0x%p\n", pMsgBox);
	pMsgBox(NULL, "EAT Hook", "Test", MB_OK);


	//加载一个弹MessageBox的Dll做测试
#ifdef _WIN64
	LoadLibrary("../Dll/MsgDll64.dll");
#else
	LoadLibrary("../Dll/MsgDll.dll");
#endif
	return 0;
}

int WINAPI My_MessageBox(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
)
{
	char newMsg[400];
	char newCation[] = "标题被我改了!";
	int result;
	if (lpText)
	{
		ZeroMemory(newMsg, 400);
		lstrcpy(newMsg, lpText);
		lstrcat(newMsg, "\n\tMessage Box hacked by pediy.com");
	}
	printf("有人调用MessageBox...\n");
	result = MessageBoxA(hWnd, newMsg, newCation, uType);
	return result;

}

BOOL InstallModuleEATHook(
	HMODULE hModToHook,// IN
	const char* szFuncName,// IN
	PVOID ProxyFunc,// IN
	PULONG_PTR* pAddrPointer,//OUT
	ULONG_PTR* pOriginalFuncAddr//OUT
)
{
	PIMAGE_EXPORT_DIRECTORY	  pExportDir;
	ULONG ulSize;
	ULONG_PTR TargetFunAddr;
	BOOL result = FALSE;
	ULONG nFuncCnt = 0;
	ULONG i = 0;
	ULONG_PTR TargetFunRVA = 0;
	ULONG* funrav = NULL;

	TargetFunAddr = (ULONG_PTR)GetProcAddress(hModToHook, szFuncName);
	TargetFunRVA = (ULONG)(TargetFunAddr - (ULONG_PTR)hModToHook);
	printf("[*]Address of %s:0x%p  RVA = 0x%X\n", szFuncName, TargetFunAddr, TargetFunRVA);
	printf("[*]Module To Hook at Base:0x%p\n", hModToHook);
	pExportDir = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize);
	printf("[*]Find ExportTable,Address:0x%p\n", pExportDir);
	nFuncCnt = pExportDir->NumberOfFunctions;
	funrav = (ULONG*)((BYTE*)hModToHook + pExportDir->AddressOfFunctions);
	for (i = 0; i < nFuncCnt; i++)
	{
		if (funrav[i] == TargetFunRVA)
		{
			printf("[*]Find target address!\n");
			//修改内存页的属性
			DWORD dwOLD;
			MEMORY_BASIC_INFORMATION  mbi;
			VirtualQuery(&funrav[i], &mbi, sizeof(mbi));
			VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
			//保存原始数据
			if (pAddrPointer)
			{
				*pAddrPointer = (PULONG_PTR)&funrav[i];
			}
			if (pOriginalFuncAddr)
			{
				*pOriginalFuncAddr = funrav[i];
			}
			//修改地址
			funrav[i] = (ULONG)((ULONG_PTR)ProxyFunc - (ULONG_PTR)hModToHook);
			result = TRUE;
			//恢复内存页的属性
			VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
			printf("[*]Hook ok.\n");
			break;
		}
	}
	return result;
}

1.3.PE的user32.dll的回调函数表

在user32.dll中有一个名为USER32!afpnDispatch的回调函数表,其中存放了用于GUI的回调函数,通常与内核中的KeUseModeCallBack函数配合使用

1.4.IDT

系统中断描述符表。IDT的表基址存放在idtr寄存器中,表内项目存放在idtl寄存器中。每个中断项的中断处理例程称为ISR

1.5.SSDT和ShadowSSDT

调用系统服务的时候要去SSDT系统服务描述符表寻找。
与IATHook相比,SSDT Hook似乎简单一点。在查找原函数地址的时候,SSDT Hook不需要逐项比较,可根据服务索引直接获取

1.6.C++类的虚函数表

在C++类如果有虚函数成员,那么其第一个元素就是虚函数表
在编译后,虚函数表就是一个固定的表了,它位于PE的.rdata段。
(1)确定TargetFun在TargetClass虚函数表中的位置及函数原型
(2)定义DetourClass和TrampolineClass,每个类都有一个与TargetFun相同的虚函数成员。(不能直接定义一个函数是因为类成员函数的调用约定是特殊的_thiscall,它使用ecx寄存器作为类this指针)

#include "stdafx.h"
#include "CClassHook.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/*
要解决两个问题
1.如何获取类成员函数的地址
2.如何使用普通函数替换类成员函数
*/
/
// The one and only application object

CWinApp theApp;

using namespace std;


class base
{
public:
    virtual int Add(int a,int b);
    virtual void g(){cout<<"base::g"<<endl;};
    virtual void h(){cout<<"base::h"<<endl;};
	void novirtual(){cout<<"base::not virtual"<<endl;};
};

int base::Add(int a,int b)
{
	printf("base::Add\n");
	return a + b ;
}

class DetourClass
{
public:
    virtual int DetourFun(int a,int b);
};

class TrampolineClass
{
public:
	virtual int TrampolineFun(int a,int b){printf("TrampolineClass\n");return 0 ;};//原型与被Hook函数相同
};

DetourClass Detour;
TrampolineClass Trampoline;

int DetourClass::DetourFun(int a,int b)
{
	//TrampolineFun();  //由于这里的类实际上是base,所以调用TrampolineFun即调用第二个虚函数,相当于调用pbase->g()
	TrampolineClass *pTrampoline = new TrampolineClass;
	int result = pTrampoline->TrampolineFun(a,b);
	printf("DetourClass:: OriginalFun returned %d\n",result);
	result += 10 ;
	delete pTrampoline;
	return result;
}



typedef void (*pfun)();
LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index);
VOID HookClassMemberByAnotherClassMember();


int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	// initialize MFC and print and error on failure
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		// TODO: change error code to suit your needs
		cerr << _T("Fatal Error: MFC initialization failed") << endl;
		nRetCode = 1;
	}
	else
	{
		// TODO: code your application's behavior here.
		HookClassMemberByAnotherClassMember();
		getchar();
	}

	return nRetCode;
}


void HookClassMemberByAnotherClassMember()
{
	base b;
	base *pbase=&b;
	
	DWORD dwOLD;
	MEMORY_BASIC_INFORMATION  mbi;

	printf("pbase = 0x%X\n",pbase);
	
	ULONG_PTR *vfTableToHook = (ULONG_PTR*)*(ULONG_PTR*)pbase;
	printf("vfTable = 0x%x\n",vfTableToHook);

	ULONG_PTR *vfTableTrampoline = (ULONG_PTR*)*(ULONG_PTR*)&Trampoline;
	
	//先将原函数的地址保存到当前类的表中,作为调用原函数的入口
	VirtualQuery(vfTableTrampoline,&mbi,sizeof(mbi));
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
	//保存原始数据
	//原函数位于第几个这里就是第几个,必须保证位置一样
	vfTableTrampoline[0] = (ULONG_PTR)GetClassVirtualFnAddress(pbase,0);
	printf("Base::Add()  %p\n",vfTableTrampoline[0]);
	TrampolineClass *p = &Trampoline;
	//恢复内存页的属性
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
	//修改内存页的属性
	
	VirtualQuery(vfTableToHook,&mbi,sizeof(mbi));
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
	//保存原始数据
	vfTableToHook[0] = (ULONG_PTR)GetClassVirtualFnAddress(&Detour,0);
	printf("Detour::Add()  %p\n",vfTableToHook[0]);
	//恢复内存页的属性
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
	int result = pbase->Add(1,2);    //调用第3个虚函数,实际调用的是HookClass::DetourFun()
	printf("result = %d  \nafter call member fun.\n",result);
}

//获得类虚拟成员函数指针
LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index) 
{
	ULONG_PTR *vfTable = (ULONG_PTR*)*(ULONG_PTR*)pthis;
	return (LPVOID)vfTable[Index];
}

2.Inline Hook

关键是转移程序的执行流程,主要有一下5中模式
(1)jmp xxxxxxxx(5字节)
(2)push xxxxxxxx/retn(6字节)
(3)mov eax,xxxxxxxx/jmp eax(7字节)
(4)call Hook(更换指令或输入表)
(5)HotPatch Hook(短跳+长跳)

//程序功能:对user32.dll导出的MessageBoxA进行Inline Hook
//本程序中写内存不再使用繁琐的VirtualProtect函数,直接使用WriteProcessMemory

#include <windows.h>
#include <stdio.h>
#include <CONIO.H>

//定义如下结构,保存一次InlineHook所需要的信息
typedef struct _HOOK_DATA{
	char szApiName[128];	//待Hook的API名字
	char szModuleName[64];	//待Hook的API所属模块的名字
	int  HookCodeLen;		//Hook长度
	BYTE oldEntry[16];		//保存Hook位置的原始指令
	BYTE newEntry[16];		//保存要写入Hook位置的新指令
	ULONG_PTR HookPoint;		//待HOOK的位置
	ULONG_PTR JmpBackAddr;		//回跳到原函数中的位置
	ULONG_PTR pfnTrampolineFun;	//调用原始函数的通道
	ULONG_PTR pfnDetourFun;		//HOOK过滤函数
}HOOK_DATA,*PHOOK_DATA;

#define HOOKLEN (5)	//要改写的指令的长度
HOOK_DATA MsgBoxHookData;

ULONG_PTR SkipJmpAddress(ULONG_PTR uAddress);
LPVOID GetAddress(char *,char *);
void makehookentry(PVOID HookPoint);
int WINAPI My_MessageBoxA(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
int WINAPI OriginalMessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
BOOL Inline_InstallHook(void);
BOOL Inline_UnInstallHook();
BOOL InstallCodeHook(PHOOK_DATA pHookData);
BOOL UninstallCodeHook(PHOOK_DATA pHookData);

int main(int argc, char* argv[])
{
	MessageBoxA(NULL,"Before Inline Hook","Test",MB_OK);
	Inline_InstallHook();
	MessageBoxA(NULL,"After  Inline Hook","Test",MB_OK);
	Inline_UnInstallHook();
	MessageBoxA(NULL,"After  Inline Hook Unhooked","Test",MB_OK);
	return 0;
}

//************************************
// Method:    FakeMessageBox
// FullName:  FakeMessageBox
// Purpose:   取代原始MessageBoxA的功能,HOOK后所有对MessageBoxA的调用将实际调用本函数
// Author:    achillis
// Returns:   int WINAPI
// Parameter: HWND hWnd
// Parameter: LPCTSTR lpText
// Parameter: LPCTSTR lpCaption
// Parameter: UINT uType
//************************************
//注意函数的定义和原始函数一定要一样,尤其是调用约定,否则函数返回后将出错
int WINAPI My_MessageBoxA(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType)
{
	//在这里,你可以对原始参数进行任意操作
	int ret;
	char newText[1024]={0};
	char newCaption[256]="pediy.com";
	printf("有人调用MessageBox!\n");
	//在调用原函数之前,可以对IN(输入类)参数进行干涉
	lstrcpy(newText,lpText);//为防止原函数提供的缓冲区不够,这里复制到我们自己的一个缓冲区中再进行操作
	lstrcat(newText,"\n\tMessageBox Hacked by pediy.com!");//篡改消息框内容
	uType|=MB_ICONERROR;//增加一个错误图标
	ret = OriginalMessageBox(hWnd,newText,newCaption,uType);//调用原MessageBox,并保存返回值
	//调用原函数之后,可以继续对OUT(输出类)参数进行干涉,比如网络函数的recv,可以干涉返回的内容
	return ret;//这里你还可以干涉原始函数的返回值
}

BOOL Inline_InstallHook()
{
	//准备Hook
	ZeroMemory(&MsgBoxHookData,sizeof(HOOK_DATA));
	strcpy(MsgBoxHookData.szApiName , "MessageBoxA");
	strcpy(MsgBoxHookData.szModuleName , "user32.dll");
	MsgBoxHookData.HookCodeLen = 5;
	MsgBoxHookData.HookPoint = (ULONG_PTR)GetAddress(MsgBoxHookData.szModuleName,MsgBoxHookData.szApiName);//HOOK的地址
	MsgBoxHookData.pfnTrampolineFun = (ULONG_PTR)OriginalMessageBox;//调用原始函数的通道
	MsgBoxHookData.pfnDetourFun = (ULONG_PTR)My_MessageBoxA;//Fake
	
	return InstallCodeHook(&MsgBoxHookData);
}


BOOL Inline_UnInstallHook()
{
	return UninstallCodeHook(&MsgBoxHookData);
}
/*
MessageBoxA的代码开头:
77D5050B >  8BFF                   mov edi,edi
77D5050D    55                     push ebp
77D5050E    8BEC                   mov ebp,esp
77D50510    833D 1C04D777 00       cmp dword ptr ds:[gfEMIEnable],0
*/
//当需要调用原始的MessageBox时,直接调用此函数即可,参数完全相同
__declspec( naked )
int WINAPI OriginalMessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType)
{
	_asm
	{
		//由于我们写入的Jmp指令破坏了原来的前3条指令,因此在这里执行原函数的前3条指令
		mov edi,edi  //这一句其实可以不要
		push ebp
		mov ebp,esp
		jmp MsgBoxHookData.JmpBackAddr //跳到Hook代码之后的地方,绕过自己安装的HOOK
	}
}

//获取指定模块中指定API的地址
LPVOID GetAddress(char *dllname,char *funname)
{
	HMODULE hMod=0;
	if (hMod=GetModuleHandle(dllname))
	{
		return GetProcAddress(hMod,funname);
	} 
	else
	{
		hMod=LoadLibrary(dllname);
		return GetProcAddress(hMod,funname);
	}
	
}

/*	
	这里计算一下要填充的指令
	使用的是5字节的Jmp
*/
void InitHookEntry(PHOOK_DATA pHookData)
{
	if (pHookData==NULL
		|| pHookData->pfnDetourFun==NULL
		|| pHookData->HookPoint==NULL)
	{
		return;
	}

	pHookData->newEntry[0] = 0xE9; //Jmp 
	//计算跳转偏移并写入
	*(ULONG*)(pHookData->newEntry+1) = (ULONG)pHookData->pfnDetourFun - (ULONG)pHookData->HookPoint - 5;//0xE9 式jmp的计算
	
	
}

ULONG_PTR SkipJmpAddress(ULONG_PTR uAddress)
{
	ULONG_PTR TrueAddress = 0 ;
	PBYTE pFn = (PBYTE)uAddress;
	
	if (memcmp(pFn,"\xFF\x25",2) == 0)
	{
		TrueAddress = *(ULONG_PTR*)(pFn + 2);
		return TrueAddress;
	}
	
	if (pFn[0] == 0xE9)
	{
		TrueAddress = (ULONG_PTR)pFn + *(ULONG_PTR*)(pFn + 1) + 5 ;
		return TrueAddress;
	}
	
	if (pFn[0] == 0xEB)
	{
		TrueAddress = (ULONG_PTR)pFn + pFn[1] + 2 ;
		return TrueAddress;
	}
	
	return (ULONG_PTR)uAddress;
}

BOOL InstallCodeHook(PHOOK_DATA pHookData)
{
	DWORD dwBytesReturned=0;
	HANDLE hProcess=GetCurrentProcess();
	BOOL bResult=FALSE;
	if (pHookData==NULL
		|| pHookData->HookPoint==0
		|| pHookData->pfnDetourFun == NULL
		|| pHookData->pfnTrampolineFun ==NULL)
	{
		return FALSE;
	}
	pHookData->pfnTrampolineFun = SkipJmpAddress(pHookData->pfnTrampolineFun);
	pHookData->HookPoint = SkipJmpAddress(pHookData->HookPoint); //如果函数开头是跳转,那么将其跳过
	pHookData->JmpBackAddr = pHookData->HookPoint + pHookData->HookCodeLen ;
	LPVOID OriginalAddr = (LPVOID)pHookData->HookPoint ;
	printf("Address To HOOK=0x%08X\n",OriginalAddr);
	InitHookEntry(pHookData);//填充Inline Hook代码
	if(ReadProcessMemory(hProcess,OriginalAddr,pHookData->oldEntry,pHookData->HookCodeLen,&dwBytesReturned))
	{
		if (WriteProcessMemory(hProcess,OriginalAddr,pHookData->newEntry,pHookData->HookCodeLen,&dwBytesReturned))
		{
			printf("Install Hook write oK! WrittenCnt=%d\n",dwBytesReturned);
			bResult=TRUE;
		}
	}
	return bResult;
}

BOOL UninstallCodeHook(PHOOK_DATA HookData)
{
	DWORD dwBytesReturned=0;
	HANDLE hProcess=GetCurrentProcess();
	BOOL bResult=FALSE;
	LPVOID OriginalAddr;
	if (HookData==NULL
		|| HookData->HookPoint==0
		|| HookData->oldEntry[0]==0)
	{
		return FALSE;
	}
	OriginalAddr=(LPVOID)HookData->HookPoint;
	bResult = WriteProcessMemory(hProcess,OriginalAddr,HookData->oldEntry,HookData->HookCodeLen,&dwBytesReturned) ;
	return bResult;
}

3.基于异常处理的HOOK实施过程

安装一个VectoredHandler过程,在要Hook的函数那里设置INT3断点,当程序执行到断点时产生异常,当该异常被VectoredHandler捕获后,对执行过程进行干预。

VectoredHandler函数原型

LONG CALLBACK VectoredHandler([in] PEXCEPTION_POINTERS ExceptionInfo);
/*-----------------------------------------------------------------------
第13章  Hook技术
《加密与解密(第四版)》
(c)  看雪学院 www.kanxue.com 2000-2018
-----------------------------------------------------------------------*/


// VEHHook.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>

BOOL SetBreakPoint(PVOID pFuncAddr);
BOOL ClearBreakPoint(PVOID pFuncAddr);
BOOL InstallVEHHook(PVECTORED_EXCEPTION_HANDLER Handler);
VOID UnInstallVEHHook();

typedef int 
(WINAPI *PFN_MessageBox)(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
	);

int WINAPI My_MessageBox(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
	);

LONG WINAPI VectoredHandler1(struct _EXCEPTION_POINTERS *ExceptionInfo);
LONG WINAPI VectoredHandler2(struct _EXCEPTION_POINTERS *ExceptionInfo);
LONG WINAPI VectoredHandler3(struct _EXCEPTION_POINTERS *ExceptionInfo);
VOID ShowMsgBox(LPCTSTR lpMsg);
ULONG_PTR InitTrampolineFun();

PFN_MessageBox g_OriginalMessageBoxA;
PVOID g_AddrofMessageBoxA = 0 ;
PVOID g_hVector;
BYTE  g_OldCode[16]={0};

int main(int argc, char* argv[])
{
	HMODULE hUser32 = LoadLibrary("user32.dll");
	g_AddrofMessageBoxA =  (PVOID)GetProcAddress(hUser32,"MessageBoxA");
	printf("Address of MessageBoxA = 0x%p\n",g_AddrofMessageBoxA);
	g_OriginalMessageBoxA = (PFN_MessageBox)InitTrampolineFun();  //跳过开头的Hook
	
	printf("Addr of VectoredHandler1 = 0x%p\n",VectoredHandler1);
	printf("Addr of VectoredHandler2 = 0x%p\n",VectoredHandler2);
	printf("Addr of VectoredHandler3 = 0x%p\n",VectoredHandler3);

	//选择安装一个进行测试
	InstallVEHHook(VectoredHandler3);

	//设置断点
	SetBreakPoint(g_AddrofMessageBoxA);

	//call
	ShowMsgBox("VEH Hook Test.");

	printf("All Finished!\n");
	ClearBreakPoint(g_AddrofMessageBoxA);
	UnInstallVEHHook();
	

	ShowMsgBox("Hook Cleared");
	return 0;
}

VOID ShowMsgBox(LPCTSTR lpMsg)
{
	MessageBoxA(NULL,lpMsg,"Test",MB_OK);
}

ULONG_PTR InitTrampolineFun()
{
	ULONG_PTR uResult = 0 ;
	PBYTE pFun = NULL;

#ifdef _WIN64
	//x64需要申请shellcode
	/*
	USER32!MessageBoxA:
	00000000`779412b8 4883ec38        sub     rsp,38h
	00000000`779412bc 4533db          xor     r11d,r11d
	00000000`779412bf 44391d760e0200  cmp     dword ptr [USER32!gapfnScSendMessage+0x927c (00000000`7796213c)],r11d
	*/
	pFun = (PBYTE)VirtualAlloc(NULL,128,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	uResult = (ULONG_PTR)pFun;
	
	memset(pFun,0,128);
	memcpy(pFun,(PVOID)g_AddrofMessageBoxA,4); //拷贝第一条指令,4字节,推荐使用反汇编引擎来实际计算
	pFun += 4 ; //下一条指令构造为jmp [xxxxxx]
	pFun[0] = 0xFF;
	pFun[1] = 0x25;
	*(ULONG_PTR*)(pFun + 6) = (ULONG_PTR)g_AddrofMessageBoxA + 4 ; //跳回到原函数加4的地方
#else
	//x86,第一条指令是mov edi,edi,直接跳过即可
	uResult = (ULONG_PTR)g_AddrofMessageBoxA + 2;
#endif

	return uResult;
}


//处理方式,修改参数并返回原函数继续执行
LONG WINAPI
VectoredHandler1(
	struct _EXCEPTION_POINTERS *ExceptionInfo
	)
{
	char *szNewMsg =  "[VectoredHandler1] Hacked by pediy.com";
	LONG lResult = EXCEPTION_CONTINUE_SEARCH ;
	PEXCEPTION_RECORD pExceptionRecord;  
	PCONTEXT pContextRecord;
	int ret = 0 ;
	pExceptionRecord = ExceptionInfo->ExceptionRecord ;
	pContextRecord = ExceptionInfo->ContextRecord ;
	ULONG_PTR* uESP = 0 ;
	printf("Exception Address = %p\n",pExceptionRecord->ExceptionAddress);
	if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT
		&& pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA)
	{
#ifdef _WIN64
		//x64上前四个参数依次为RCX,RDX,R8,R9
		//修改第二个参数,即LpMsg
		printf("lpText = 0x%p   %s\n",pContextRecord->Rdx,(char*)pContextRecord->Rdx);
		pContextRecord->Rdx = (ULONG_PTR)szNewMsg;
		pContextRecord->Rip = (ULONG_PTR)g_OriginalMessageBoxA ; //跳到Trampoline继续执行
#else
		/*
		0012FF70   0040105A   /CALL 到 MessageBoxA 来自 VEHHook.00401054
		0012FF74   00000000   |hOwner = NULL
		0012FF78   00407030   |Text = "VEH Hook"
		0012FF7C   0040703C   |Title = "Test"
		0012FF80   00000000   \Style = MB_OK|MB_APPLMODAL
		0012FF84   00401225   返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000
		*/
		printf("ESP = 0x%p\n",pContextRecord->Esp) ;
		uESP = (ULONG_PTR*)pContextRecord->Esp ; //取中断时的ESP
		uESP[2] = (ULONG_PTR)szNewMsg; //修改栈中的参数
		pContextRecord->Eip = (ULONG_PTR)g_OriginalMessageBoxA ; //跳过函数开头
#endif
		
		lResult = EXCEPTION_CONTINUE_EXECUTION ;
	}
    return lResult;
}

//处理方式:直接调用原函数并替原函数返回
LONG WINAPI
VectoredHandler2(
	struct _EXCEPTION_POINTERS *ExceptionInfo
	)
{
	char *szNewMsg =  "[VectoredHandler2] Hacked by pediy.com";
	LONG lResult = EXCEPTION_CONTINUE_SEARCH ;
	PEXCEPTION_RECORD pExceptionRecord;  
	PCONTEXT pContextRecord;
	int ret = 0 ;
	pExceptionRecord = ExceptionInfo->ExceptionRecord ;
	pContextRecord = ExceptionInfo->ContextRecord ;
	ULONG_PTR* uESP = 0 ;
	if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT
		&& pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA)
	{
		
#ifdef _WIN64
		//x64上前四个参数依次为RCX,RDX,R8,R9
		printf("RSP = 0x%p\n",pContextRecord->Rsp) ;
		uESP = (ULONG_PTR*)pContextRecord->Rsp ;
		printf("Return Address = 0x%p\n",uESP[0]);
		ret = g_OriginalMessageBoxA((HWND)pContextRecord->Rcx,szNewMsg,(LPCTSTR)pContextRecord->R8,(int)pContextRecord->R9);
		printf("ret = %d\n",ret);
		//修正RSP
		pContextRecord->Rsp += sizeof(ULONG_PTR);//参数在寄存器中,栈中无参数,仅需跳过返回地址
		//直接返回到调用者处
		pContextRecord->Rip = uESP[0] ;//设置EIP为返回地址
#else
		/*
		0012FF70   0040105A   /CALL 到 MessageBoxA 来自 VEHHook.00401054
		0012FF74   00000000   |hOwner = NULL
		0012FF78   00407030   |Text = "VEH Hook"
		0012FF7C   0040703C   |Title = "Test"
		0012FF80   00000000   \Style = MB_OK|MB_APPLMODAL
		0012FF84   00401225   返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000
		*/
		printf("ESP = 0x%p\n",pContextRecord->Esp) ;
		uESP = (ULONG_PTR*)pContextRecord->Esp ;
		ret = g_OriginalMessageBoxA((HWND)uESP[1],szNewMsg,(LPCTSTR)uESP[3],(int)uESP[4]);
		printf("ret = %d\n",ret);

		//直接返回到调用者处
		pContextRecord->Eip = uESP[0] ;//设置EIP为返回地址
		pContextRecord->Esp += (4 + 1)*sizeof(ULONG_PTR); //4为参数个数,1为返回地址
#endif
		
		lResult = EXCEPTION_CONTINUE_EXECUTION ;
	}
    return lResult;
}

//处理方式:直接返回,相当于过滤掉
LONG WINAPI
VectoredHandler3(
	struct _EXCEPTION_POINTERS *ExceptionInfo
	)
{
	LONG lResult = EXCEPTION_CONTINUE_SEARCH ;
	PEXCEPTION_RECORD pExceptionRecord = ExceptionInfo->ExceptionRecord ;
	PCONTEXT pContextRecord = ExceptionInfo->ContextRecord ;
	ULONG_PTR* uESP = 0 ;
	if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT
		&& pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA)
	{
		
		/*
		0012FF70   0040105A   /CALL 到 MessageBoxA 来自 VEHHook.00401054
		0012FF74   00000000   |hOwner = NULL
		0012FF78   00407030   |Text = "VEH Hook"
		0012FF7C   0040703C   |Title = "Test"
		0012FF80   00000000   \Style = MB_OK|MB_APPLMODAL
		0012FF84   00401225   返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000
		*/
		

		//直接返回到调用者处
#ifdef _WIN64
		printf("RSP = 0x%p\n",pContextRecord->Rsp) ;
		uESP = (ULONG_PTR*)pContextRecord->Rsp ;
		pContextRecord->Rip = uESP[0] ;//设置EIP为返回地址
		pContextRecord->Rsp += sizeof(ULONG_PTR); //将压入栈内的参数和返回地址清理掉,4为参数个数,1为返回地址
#else
		printf("ESP = 0x%X\n",pContextRecord->Esp) ;
		uESP = (ULONG_PTR*)pContextRecord->Esp ;
		pContextRecord->Eip = uESP[0] ;//设置EIP为返回地址
		pContextRecord->Esp += (4 + 1)*sizeof(ULONG_PTR); //将压入栈内的参数和返回地址清理掉,4为参数个数,1为返回地址
#endif
		
		lResult = EXCEPTION_CONTINUE_EXECUTION ;
	}
    return lResult;
}

BOOL InstallVEHHook(PVECTORED_EXCEPTION_HANDLER Handler)
{
	printf("Current Handler Address = 0x%p\n",Handler);
	g_hVector = AddVectoredExceptionHandler(1,Handler);
	return g_hVector != NULL ;
}

VOID UnInstallVEHHook()
{
	RemoveVectoredExceptionHandler(g_hVector);
}

/*
0:000> u user32!messageboxA
USER32!MessageBoxA:
77d507ea 8bff            mov     edi,edi
77d507ec 55              push    ebp
77d507ed 8bec            mov     ebp,esp
*/
BOOL SetBreakPoint(PVOID pFuncAddr)
{
	DWORD dwCnt = 0 ;
	BYTE *pTarget = (BYTE*)pFuncAddr;
	
	
	g_OldCode[0] = *pTarget;
	printf("Original Fun Head Code = 0x%02X\n",g_OldCode[0]);
	//修改内存页的属性
	DWORD dwOLD;
	MEMORY_BASIC_INFORMATION  mbi;
	VirtualQuery(pTarget,&mbi,sizeof(mbi));
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
	
	//写入int3
	*pTarget  = 0xCC ;

	//恢复内存页的属性
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
	return TRUE;
}

BOOL ClearBreakPoint(PVOID pFuncAddr)
{
	BYTE *pTarget = (BYTE*)pFuncAddr;
	//修改内存页的属性
	DWORD dwOLD;
	MEMORY_BASIC_INFORMATION  mbi;
	VirtualQuery(pTarget,&mbi,sizeof(mbi));
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
	
	*pTarget  = g_OldCode[0] ;
	
	//恢复内存页的属性
	VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
	return TRUE;
}

int WINAPI My_MessageBox(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
	)
{	
	char newMsg[400];
	char newCation[]="标题被我改了!";
	int result;
	if (lpText)
	{
		ZeroMemory(newMsg,400);
		lstrcpy(newMsg,lpText);
		lstrcat(newMsg,"\n\tMessage Box hacked by pediy.com");
	}
	printf("有人调用MessageBox...\n");
	result = g_OriginalMessageBoxA(hWnd,newMsg,newCation,uType);
	return result;
	
}

4.Hook位置

在应用层上,对API进行IAT Hook和Inline Hook足以满足这个要求。在内核中,KiFastCallEntry和KeServiceDescriptorTable(含shadow)是两个绝佳的Hook位置。

5.钩子

钩子分为局部钩子和全局钩子。局部钩子是针对单一线程,而全局钩子针对当前桌面的所有线程。

钩子是基于消息机制的,只有触发相应的消息,钩子函数过程才有机会执行,系统才会加载钩子模块。

钩子实际上是一个处理消息的程序段,通过系统机制使其挂入系统。当产生特定消息时,在未到达目的窗口之前,钩子程序就先捕获该消息,取得控制权。

对于每种类型的钩子,系统都维护一个钩链。每次安装钩子时,都会向对应类型的链表添加一个结点项,用于记录钩子的函数过程、引用计数等信息。最近安装的钩子放在链表的开始位置,而最先安装的钩子放在最后。

安装钩子使用SetWindowsHookEx,函数原型定义如下:

HHOOK SetWindowHookEx(
	int idHook,           //钩子类型
	HOOKPROC lpfn,		  //钩子函数过程
	HINSTANCE  hMod,	  //钩子模块实例句柄(即lpfn指定函数过程所在模块句柄)
	DWORD    dwThreadId   //需要挂钩的线程ID
);

BOOL UnHookWindowsHookEx(
	HHOOK hhk   //钩子句柄
);

在应用程序中安装钩子,接收到对应消息后,可以在钩子的消息处理过程对消息进行处理,但通常需要使用CallNextHookEx函数来继续传递消息

LRESULT CallNextHookEx(
	HHOOK hhk,       //钩子句柄
	int   nCode,	 //钩子标识
	WPARAM  wParam,  //根据具体钩子类型决定
	LPARAM  lParam,  //根据具体钩子类型决定
);
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值