1. 程式人生 > 實用技巧 >x64 Inline Hook 程式碼封裝

x64 Inline Hook 程式碼封裝

Hook 技術常被叫做掛鉤技術,掛鉤技術其實早在DOS時代就已經存在了,該技術是Windows系統用於替代DOS中斷機制的具體實現,鉤子的含義就是在程式還沒有呼叫系統函式之前,鉤子捕獲呼叫訊息並獲得控制權,在執行系統呼叫之前執行自身程式,簡單來說就是函式劫持.

接著來研究一下64位程式的Hook,64位與32位系統之間無論從定址方式,還是語法規則都與x32架構有著本質的不同,所以上面的使用技巧只適用於32位程式,注入32位程序使用,下面的內容則是64位下手動完成Hook掛鉤的一些操作手法,由於64位編譯器無法直接內嵌彙編程式碼,導致我們只能呼叫C庫函式來實現Hook的中轉.

該筆記是針對64位Hook的簡易封裝,自己留著也沒什麼意思,還是分享出來吧,轉載請加出處,謝謝!


簡單實現64位Hook去彈窗: 由於64位編譯器無法直接內嵌彙編程式碼,所以在我們需要Hook時只能將跳轉機器碼以二進位制位元組方式寫死在程式裡,如下程式碼是一段簡單的演示案例,主要實現了去彈窗的功能.

#include <stdio.h>
#include <Windows.h>

BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
DWORD_PTR base;

int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	return 1;
}

int main(int argc,char * argv[])
{
	HMODULE hwnd = GetModuleHandle(TEXT("user32.dll"));
	DWORD_PTR base = (DWORD_PTR)GetProcAddress(hwnd, "MessageBoxW");
	DWORD OldProtect;

	if (VirtualProtect((LPVOID)base, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy(OldCode, (LPVOID)base, 12);                  // 拷貝原始機器碼指令
		*(PINT64)(HookCode + 2) = (INT64)&MyMessageBoxW;    // 填充90為指定跳轉地址
	}
	memcpy((LPVOID)base, &HookCode, sizeof(HookCode));      // 拷貝Hook機器指令

	MessageBoxW(NULL, L"hello lyshark", NULL, NULL);

	return 0;
}

64位Hook程式碼完整版: 接著我們在上面程式碼的基礎上,繼續進行完善,新增恢復鉤子的功能,該功能時必須要有的,因為我們還是需要呼叫原始的彈窗程式碼,所以要在呼叫時進行暫時恢復,呼叫結束後再繼續Hook掛鉤.

#include <stdio.h>
#include <Windows.h>

void Hook();
void UnHook();

BYTE Ori_Code[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };

/* Hook 機器碼的原理如下所示
MOV RAX, 0x9090909090909090
JMP RAX
*/

static int (WINAPI *OldMessageBoxW)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) = MessageBoxW;

int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	UnHook();                                                                  // 恢復Hook
	int ret = OldMessageBoxW(hWnd, TEXT("Hook Inject"), lpCaption, uType);     // 呼叫原函式
	Hook();                                                                    // 繼續hook
	return ret;
}

void Hook()
{
	DWORD OldProtect;
	if (VirtualProtect(OldMessageBoxW, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy(Ori_Code, OldMessageBoxW, 12);               // 拷貝原始機器碼指令
		*(PINT64)(HookCode + 2) = (INT64)&MyMessageBoxW;    // 填充90為指定跳轉地址
	}
	memcpy(OldMessageBoxW, &HookCode, sizeof(HookCode));    // 拷貝Hook機器指令
}

void UnHook()
{
	memcpy(OldMessageBoxW, &Ori_Code, sizeof(Ori_Code));    // 恢復hook原始程式碼
}

int main(int argc, char *argv [])
{
	Hook();
	MessageBoxW(NULL, TEXT("hello lyshark"), TEXT("MsgBox"), MB_OK);
	UnHook();
	MessageBoxW(NULL, TEXT("hello lyshark"), TEXT("MsgBox"), MB_OK);

	return 0;
}

完整版DLL注入鉤子: 最後將上面所寫的程式碼進行封裝,實現一個完整的鉤子處理程式,程式碼如下.

#include <stdio.h>
#include <Windows.h>

BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };

void Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
	DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
	DWORD OldProtect = 0;

	if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy(OldCode, (LPVOID)FuncAddress, 12);                   // 拷貝原始機器碼指令
		*(PINT64)(HookCode + 2) = (UINT64)lpFunction;               // 填充90為指定跳轉地址
	}
	memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode));       // 拷貝Hook機器指令
	VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
}

void UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
	DWORD OldProtect = 0;
	UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
	if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy((LPVOID)FuncAddress, OldCode, sizeof(OldCode));
	}
	VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
}

int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	// 首先恢復Hook程式碼
	UnHook(L"user32.dll", "MessageBoxW");
	int ret = MessageBoxW(0, L"hello lyshark", lpCaption, uType);

	// 呼叫結束後,再次掛鉤
	Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
	return ret;
}

bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
	switch (dword)
	{
	case DLL_PROCESS_ATTACH:
		Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
		break;
	case DLL_PROCESS_DETACH:
		UnHook(L"user32.dll", "MessageBoxW");
		break;
	}
	return true;
}

針對Hook程式碼的封裝: 上方的程式碼還是基於過程化的案例,為了能更加通用,我們將其封裝成類,這樣後期可以直接呼叫了.

// hook.h
#pragma once
#include <Windows.h>

#ifdef __cplusplus
extern "C"{
#endif

	class MyHook
	{
	public:
		FARPROC m_pfnOrig;       // 儲存函式地址
		BYTE m_bOldBytes[12];  // 儲存函式入口程式碼
		BYTE m_bNewBytes[12];  // 儲存Inlie Hook程式碼
	public:
		MyHook();
		~MyHook();
		BOOL Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction);
		BOOL UnHook(LPCWSTR lpModule, LPCSTR lpFuncName);
	};
#ifdef __cplusplus
}
#endif
// hook.cpp
#include "hook.h"

MyHook::MyHook()
{
	BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
	BYTE NewCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };

	RtlMoveMemory(MyHook::m_bNewBytes, NewCode, 12);
	memset(MyHook::m_bOldBytes, 0, 12);
	m_pfnOrig = NULL;
}

MyHook::~MyHook()
{
	m_pfnOrig = NULL;
	ZeroMemory(MyHook::m_bNewBytes, 12);
	ZeroMemory(MyHook::m_bOldBytes, 12);
}

BOOL MyHook::Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
	DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
	DWORD OldProtect = 0;

	if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy(m_bOldBytes, (LPVOID)FuncAddress, 12);                           // 拷貝原始機器碼指令
		*(PINT64)(MyHook::m_bNewBytes + 2) = (UINT64)lpFunction;                // 填充90為指定跳轉地址
	}
	memcpy((LPVOID)FuncAddress, &m_bNewBytes, sizeof(m_bNewBytes));             // 拷貝Hook機器指令
	VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
	return TRUE;
}

BOOL MyHook::UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
	DWORD OldProtect = 0;
	UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
	if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy((LPVOID)FuncAddress, m_bOldBytes, sizeof(m_bOldBytes));
	}
	VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
	return TRUE;
}
// main.cpp
#include <Windows.h>
#include "hook.h"

MyHook MsgHook;

int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	// 首先恢復Hook程式碼
	MsgHook.UnHook(L"user32.dll", "MessageBoxW");
	int ret = MessageBoxW(0, L"Hook Inject", lpCaption, uType);

	// 呼叫結束後,再次掛鉤
	MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
	return ret;
}

int main(int argc, char * argv[])
{
	// 開始Hook
	MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);

	MessageBoxW(NULL, L"hello lyshark", L"Msg", MB_OK);

	// 結束Hook
	MsgHook.UnHook(L"user32.dll", "MessageBoxW");
	return 0;
}

第二種封裝方式: 接著我們將程式碼宣告與實現合在一起,實現第二種封裝方式.

// hook.h
#include <Windows.h>

class MyHook
{
public:
	static UINT64 Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
	{
		UINT64 dwAddr = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
		BYTE jmp[] =
		{
			0x48, 0xb8,               // jmp
			0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00,   // address
			0x50, 0xc3                // retn
		};

		ReadProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, MemoryAddress(), 12, 0);
		UINT64 dwCalc = (UINT64)lpFunction;
		memcpy(&jmp[2], &dwCalc, 8);

		WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, jmp, 12, nullptr);
		return dwAddr;
	}

	static BOOL UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
	{
		UINT64 dwAddr = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);

		if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, MemoryAddress(), 12, 0))
			return TRUE;
		return FALSE;
	}

	static BYTE* MemoryAddress()
	{
		static BYTE backup[12];
		return backup;
	}
};
// main.cpp
#include <windows.h>
#include "hook.h"

MyHook MsgHook;

static int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
	MsgHook.UnHook(L"user32.dll", "MessageBoxW");
	int ret = MessageBox(hWnd, L"Hook Inject", lpCaption, uType);
	MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
	return ret;
}

int main(int argc, char * argv[])
{
	MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
	MessageBoxW(NULL, L"hello lyshark", L"MsgBox", MB_OK);

	MsgHook.UnHook(L"user32.dll", "MessageBoxW");
	MessageBoxW(NULL, L"hello lyshark", L"MsgBox", MB_OK);

	return 0;
}