hook小试

hook


hook(钩子)技术:拦截程序原有的信息、数据、代码,进行处理,再交给原来的程序使用。

效果/目标:

  • 查看、处理拦截的信息
  • 修改程序部分功能

hook一般要搭配注入使用的,这里为了整理总结,直接主程序LoadLibrary()

1. 原理

windows消息钩子基于一个重要的api:

HHOOK
WINAPI
SetWindowsHookExW(
    _In_ int idHook,
    _In_ HOOKPROC lpfn,
    _In_opt_ HINSTANCE hmod,
    _In_ DWORD dwThreadId);

它可以截获:

  • 所有窗口程序的消息
  • 某一个线程的消息

复习一下:窗口的消息由创建这个窗口的线程获得(GetMessage)。线程在创建完窗口后,就变成了GUI线程。

idHook表示消息类型。

dwThreadId为

  • 0,就截获所有线程的消息(危险);
  • PID,就截获该线程的消息。

截获消息后,需要执行自己存在dll中的代码lpfn。设置好钩子之后,应该将dll注入目标进程,这个dll就是参数hmod。

这里要注意一下,注入和hook是两种技巧,这里搭配使用,不存在必须注入才能hook这种说法。

钩子用完后,用UnhookWindowsHookEx()卸载。

程序可能有多个钩子,所有回调函数最后要CallNextHookEx():

LRESULT
CallNextHookEx(
    _In_opt_ HHOOK hhk,
    _In_ int nCode,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam);

后三个参数就是回调函数的参数。

2. hook分类

两类:

  • windows消息hook,截获消息,不常用;
  • 自定义hook,传统认知上的hook。

2.1 消息hook

也是一种dll注入手段。同样依赖于SetWindowsHookExA()

应用时TID应该是通过遍历得到,这里为了方便就hook所有程序。

消息钩子是windows支持的一种钩子技术,内部维护着钩子链表。这里我们利用它拿来做dll注入。

msgbullet.dll

下面是dll代码,包括回调函数,设置和卸载hook:

回调函数可以决定是否继续传递消息。

第一个参数需要查询msdn。


#include <windows.h>

HHOOK	g_hook = NULL;
HMODULE g_hDll = NULL;

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode == HC_ACTION &&
		(lParam & 0x80000000))
	{
		char msg[3] = { (char)wParam, 0 ,'\n'};
		OutputDebugStringA(msg);
	}

	return CallNextHookEx(g_hook, nCode, wParam, lParam);
}


#ifdef __cplusplus
extern "C"{
#endif

	__declspec(dllexport)
	void HookStart()
	{
		g_hook = SetWindowsHookEx(
			WH_KEYBOARD,
			KeyboardProc,
			g_hDll,
			0);
	}

	__declspec(dllexport)
	void HookEnd()
	{
		UnhookWindowsHookEx(g_hook);
	}

#ifdef __cplusplus
}
#endif

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		g_hDll = hModule;
		break;;
	case DLL_THREAD_ATTACH:
		break;;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

OutputDebugString()输出可以用dbgview查看。

msghook.cpp

#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

#include <stdio.h>
#include <windows.h>
#include <direct.h>
#include <conio.h>

typedef void(*PFUN)();

int main()
{
	char szDllPath[MAX_PATH] = { 0 };
	HMODULE hDll = NULL;
	DWORD dwPID = 0;
	PFUN pHookStart = NULL, pHookEnd = NULL;

	_getcwd(szDllPath, MAX_PATH);
	PathRemoveFileSpecA(szDllPath);
	strcat_s(szDllPath, MAX_PATH, "\\x64\\Debug\\msgbullet.dll");

	hDll = LoadLibraryA(szDllPath);
	if (!hDll) return 0;
	
	pHookStart = (PFUN)GetProcAddress(hDll, "HookStart");
	pHookEnd = (PFUN)GetProcAddress(hDll, "HookEnd");

	pHookStart();

	while (_getch() != 'q');

	pHookEnd();
	system("pause");
	return 0;
}

但进程本身也被hook了。

除了用dbgview,还可以用OD附加,观察dll模块是否注入。

2.2 自定义hook

又细分为两类:

  • inline hook:修改程序代码,执行自己的代码;
  • IAT hook:修改存储函数地址的变量,容易被检测到(当然,所有hook都能检测到)。

内核层还有IDT-hook(修改IDT表)、SYSENTER-hook.

自定义hook很普遍,是通常所说的传统意义上的hook。

inline hook

在高危代码首地址处写入call, jmp, ret等跳转指令,跳转到自己的代码执行。自己的代码要注意:

  1. 开头pushad,最后popad;
  2. 最后还要再执行一下被修改的指令,然后跳回去。

例如jmp公式:offset = target addr - (jmp addr + 5)

5:jmp xxxxxxxx为5个字节,这里涉及jmp指令的opcode计算,不赘述。所以说,用jmp时要找另一个长度为5的指令,避免指令截断。

IAT hook

找到函数地址的方法:

  • 文件IAT
  • 文件INT
  • 内存INT

注意,我们不能通过映射到内存中的IAT找到函数地址,因为没有函数名称。

由IAT hook可以想到,虚函数表也能hook。

3. 代码示例

3.1 inline hook

InlineHook.dll

#include <windows.h>

#define HOOKLEN	5

extern "C"
__declspec(dllexport)
void InstallHook();

extern "C"
__declspec(dllexport)
void UninstallHook();

BYTE g_oldCodes[HOOKLEN] = { 0 };
BYTE g_newCodes[HOOKLEN] = { 0xE9 };


int
MyMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType)
{
	UninstallHook();
	int ret = MessageBoxW(hWnd, L"MessageBoxW() is hooked~", L"Ooops", uType);
	InstallHook();

	return ret;
}

extern "C"
__declspec(dllexport)
void InstallHook()
{
	HMODULE hModule = GetModuleHandle(L"user32.dll");
	LPVOID lpMsgBox = NULL;
	if (hModule)
	{
		lpMsgBox = GetProcAddress(hModule, "MessageBoxW");
	}
	if (!lpMsgBox) return;

	memcpy(g_oldCodes, lpMsgBox, HOOKLEN);

	DWORD offset = (DWORD)MyMessageBoxW - (DWORD)lpMsgBox - HOOKLEN;
	*(DWORD*)(g_newCodes + 1) = offset;

	DWORD dwProtect = 0;
	VirtualProtect(lpMsgBox, HOOKLEN, PAGE_EXECUTE_READWRITE, &dwProtect);
	memcpy(lpMsgBox, g_newCodes, HOOKLEN);
	VirtualProtect(lpMsgBox, HOOKLEN, dwProtect, &dwProtect);
}

extern "C"
__declspec(dllexport)
void UninstallHook()
{
	HMODULE hModule = GetModuleHandle(L"user32.dll");
	LPVOID lpMsgBox = NULL;
	if (hModule)
	{
		lpMsgBox = GetProcAddress(hModule, "MessageBoxW");
	}
	if (!lpMsgBox) return;

	DWORD dwProtect = 0;
	VirtualProtect(lpMsgBox, HOOKLEN, PAGE_EXECUTE_READWRITE, &dwProtect);
	memcpy(lpMsgBox, g_oldCodes, HOOKLEN);
	VirtualProtect(lpMsgBox, HOOKLEN, dwProtect, &dwProtect);
}

/*
	为了演示,先将DllMain注释。
*/
//BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
//{
//	switch (dwReason)
//	{
//	case DLL_PROCESS_ATTACH:
//		InstallHook();
//		break;
//	case DLL_PROCESS_DETACH:
//		UninstallHook();
//		break;
//	default:
//		break;
//	}
//	return TRUE;
//}

CallHook.cpp

#include <windows.h>
#include <direct.h>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
typedef void (*PFUNC)();

int main()
{
	char szDllPath[MAX_PATH] = { 0 };
	HMODULE hModule = NULL;
	PFUNC InstallHook = NULL, UninstallHook = NULL;
	_getcwd(szDllPath, MAX_PATH);
	PathRemoveFileSpecA(szDllPath);

	strcat_s(szDllPath, MAX_PATH, "\\Debug\\InlineHook.dll");
	//strcat_s(szDllPath, MAX_PATH, "\\Debug\\IATHook.dll");


	hModule = LoadLibraryA(szDllPath);
	InstallHook = (PFUNC)GetProcAddress(hModule, "InstallHook");
	UninstallHook = (PFUNC)GetProcAddress(hModule, "UninstallHook");

	MessageBoxW(NULL, L"Before InstallHook()", L"", 0);
	InstallHook();
	MessageBoxW(NULL, L"after hooked", L"", 0);
	UninstallHook();
	MessageBoxW(NULL, L"after UninstallHook()", L"", 0);

	return 0;
}

这段代码虽然成功,但实际上调用被hook的原函数是有点问题的。

00FF17C8    8BF4            mov esi,esp
00FF17CA    6A 00           push 0x0
00FF17CC    68 6C6BFF00     push CallHook.00FF6B6C
00FF17D1    68 706BFF00     push CallHook.00FF6B70                           ; UNICODE "Before InstallHook()"
00FF17D6    6A 00           push 0x0
00FF17D8    FF15 CCA0FF00   call dword ptr ds:[<&USER32.MessageBoxW>]        ; apphelp.722C1E80
00FF17DE    3BF4            cmp esi,esp
00FF17E0    E8 33F9FFFF     call CallHook.00FF1118
00FF17E5    8BF4            mov esi,esp
00FF17E7    FF95 DCFEFFFF   call dword ptr ss:[ebp-0x124]                    ; InlineHo.InstallHook
00FF17ED    3BF4            cmp esi,esp
00FF17EF    E8 24F9FFFF     call CallHook.00FF1118
00FF17F4    8BF4            mov esi,esp
00FF17F6    6A 00           push 0x0
00FF17F8    68 6C6BFF00     push CallHook.00FF6B6C
00FF17FD    68 A46BFF00     push CallHook.00FF6BA4                           ; UNICODE "after hooked"
00FF1802    6A 00           push 0x0
00FF1804    FF15 CCA0FF00   call dword ptr ds:[<&USER32.MessageBoxW>]        ; apphelp.722C1E80
00FF180A    3BF4            cmp esi,esp
00FF180C    E8 07F9FFFF     call CallHook.00FF1118

注意,每次调用函数前后,都有mov esi, espcmp esi, esp,来检查栈平衡。

下面是hook之前的MB函数结尾:

722C1ECC    5E              pop esi
722C1ECD    5B              pop ebx
722C1ECE    5D              pop ebp
722C1ECF    C2 1000         retn 0x10

而我自己写的MyMessageBoxW的结尾:

0FFB161F    8BE5            mov esp,ebp
0FFB1621    5D              pop ebp                                  ; CallHook.<ModuleEntryPoint>
0FFB1622    C3              retn

并没有retn 10,这就导致00FF180A处检查出了堆栈不平衡,会报错。

原因!!!!!:

这是调用约定的问题,于是查看MessageBox声明,是__stdcall自动清栈,而vs项目属性里的调用约定是__cdecl。于是修改dll:

int __stdcall
MyMessageBoxW(
//....

所以说,被hook的函数和我们自己的函数调用方式一定要一致!!!!!!!

3.2 IAT hook

IAT_Hook.dll

注意是如何区分32位和64位的。

#include <windows.h>

extern "C"
__declspec(dllexport)
void InstallHook();

extern "C"
__declspec(dllexport)
void UninstallHook();

typedef
int(__stdcall *PMessageBoxW)(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType);

PMessageBoxW g_pOriginMsgBoxW = NULL;
PIMAGE_THUNK_DATA g_pMsgBoxIAT = NULL;

int __stdcall
MyMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType)
{
	return g_pOriginMsgBoxW(hWnd, L"I'm the True MessageBoxW", L"Ooops", uType);
}



PIMAGE_THUNK_DATA GetDllFunc(const char* dllName, const char* funcName)
{
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)GetModuleHandle(0);
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (ULONGLONG)pDos);
	PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)(pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
		+ (ULONGLONG)pDos);
	PIMAGE_THUNK_DATA pIAT = NULL, pINT = NULL;
	char *pDllName = NULL;
	PIMAGE_IMPORT_BY_NAME pImportName = NULL;
	while (pIID->Name)
	{
		pIAT = (PIMAGE_THUNK_DATA)(pIID->FirstThunk + (ULONGLONG)pDos);
		pINT = (PIMAGE_THUNK_DATA)(pIID->OriginalFirstThunk + (ULONGLONG)pDos);

		pDllName = (char*)(pIID->Name + (ULONGLONG)pDos);
		if (!_stricmp(pDllName, dllName))
		{
			while (pINT->u1.Ordinal)
			{
				// If imported by name
				if (!(pINT->u1.Ordinal &
					(1 << 8 * sizeof(pINT->u1.Ordinal) - 1)
					))
				{
					pImportName = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.AddressOfData
						+ (ULONGLONG)pDos);
					if (!strcmp(pImportName->Name, funcName))
					{
						return pIAT;
					}
				}

				++pINT;
				++pIAT;
			}
		}
		++pIID;
	}
	return NULL;
}

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  dwReason,
	LPVOID lpReserved
)
{
	switch (dwReason)
	{
	case DLL_PROCESS_ATTACH:
		g_pMsgBoxIAT = GetDllFunc("user32.dll", "MessageBoxW");
		if (g_pMsgBoxIAT)
		{
			g_pOriginMsgBoxW = (PMessageBoxW)
				((sizeof(g_pMsgBoxIAT) == 8) 
					? *(ULONGLONG*)g_pMsgBoxIAT
					: *(DWORD*)g_pMsgBoxIAT);
			InstallHook();
		}
		break;
	case DLL_PROCESS_DETACH:
		if (g_pMsgBoxIAT)
		{
			UninstallHook();
		}
		break;
	}
	return TRUE;
}

extern "C"
__declspec(dllexport)
void InstallHook()
{
	DWORD dwProtect = 0;
	VirtualProtect(g_pMsgBoxIAT, sizeof(g_pMsgBoxIAT), PAGE_EXECUTE_READWRITE, &dwProtect);
	
	//64 bit
	if (sizeof(g_pMsgBoxIAT) == 8)
	{
		*(ULONGLONG*)g_pMsgBoxIAT = (ULONGLONG)MyMessageBoxW;
	}
	//32bit
	else
	{
		*(DWORD*)g_pMsgBoxIAT = (DWORD)MyMessageBoxW;
	}

	VirtualProtect(g_pMsgBoxIAT, sizeof(g_pMsgBoxIAT), dwProtect, &dwProtect);
}

extern "C"
__declspec(dllexport)
void UninstallHook()
{
	DWORD dwProtect = 0;
	VirtualProtect(g_pMsgBoxIAT, sizeof(g_pMsgBoxIAT), PAGE_EXECUTE_READWRITE, &dwProtect);

	//64 bit
	if (sizeof(g_pMsgBoxIAT) == 8)
	{
		*(ULONGLONG*)g_pMsgBoxIAT = (ULONGLONG)g_pOriginMsgBoxW;
	}
	//32bit
	else
	{
		*(DWORD*)g_pMsgBoxIAT = (DWORD)MyMessageBoxW;
	}

	VirtualProtect(g_pMsgBoxIAT, sizeof(g_pMsgBoxIAT), dwProtect, &dwProtect);
}

callHook.c

#include <windows.h>
#include <stdio.h>
#include <direct.h>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
typedef void (*PFUNC)();

typedef

HANDLE
(*POpenProcess)(
	_In_ DWORD dwDesiredAccess,
	_In_ BOOL bInheritHandle,
	_In_ DWORD dwProcessId
);

int main()
{
	char szDllPath[MAX_PATH] = { 0 };
	HMODULE hModule = NULL;
	PFUNC InstallHook = NULL, UninstallHook = NULL;
	_getcwd(szDllPath, MAX_PATH);
	PathRemoveFileSpecA(szDllPath);

	//strcat_s(szDllPath, MAX_PATH, "\\Debug\\InlineHook.dll");
	strcat_s(szDllPath, MAX_PATH, "\\x64\\Debug\\IAT_Hook.dll");


	hModule = LoadLibraryA(szDllPath);

	MessageBoxW(NULL, L"not work", L"caption", 0);
	return 0;
}

这里的路径是vs调试路径,OD调试的时候要在数据窗口改dll路径。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值