硬件断点HOOK

c++

Posted by YiMiTuMi on November 18, 2021

硬件断点HOOK

一些软件会通过一个线程来循环检测代码的正确性,当你HOOK去修改dll或exe中的代码时会触发这个检测导致被HOOK软件失败。

通过硬件HOOK过检测:

1)不修改检测线程

2)不修改挂钩子函数的代码

在要HOOK的函数地址(函数地址可以用LoadLiberal系列函数获取)处下一个硬件断点,通过我们上面的异常可以获取到这个异常和CONTEXT,可以修改Esp、Eip等寄存器,获取了Esp就是拿到了堆栈,即参数、返回地址等。Eip可以跳到我们想要执行的位置。

可以通过设置顶层异常处理过滤函数来捕获这个异常,最好也在dll注入后设置。至于硬件断点我们只需要在DLL注入时在我们想要HOOK的函数地址通过DR7与空闲的DR0~3中下上断点就好。

可能需要调试知识:软件调试

部分函数说明

ChangeContext函数:

ChangeContext函数主要是通过寄存器修改堆栈,使函数执行我们想执行的功能。因为我们是在函数开头的的位置下的断点即:

push ebp

mov ebp, esp

这两行函数开头的代码都不会执行,所以此时我们栈顶用ESP去修改,因为并没有执行

push ebp

这时我们的ESP指向的是CALL指令执行时,push到堆栈里的返回地址,因为函数入栈从后向前push参数,所以ESP为:

esp        = 返回地址

esp + 0x4  = 第一个参数

esp + 0x8  = 第二个参数 

以此类推。

OriginalFun函数:

OriginalFun函数主要是为了让我们修改堆栈的值后,跳过我们下断点的位置,否则会死循环。

主程序

主程序是一个DLL,通过 CreateRemoteThread 函数进行注入:Dll注入–CreateRemoteThread

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include "tlhelp32.h"

DWORD g_dwHookFunAdd = 0;
DWORD g_dwHookFunAddOffset = 0;

void _declspec(naked) OriginalFun(void)
{
	//不能从开始执行,已经下了断点,跳过断点执行
	_asm
	{
		mov edi, edi
		jmp [g_dwHookFunAddOffset]
	}
}


void ChangeContext(PCONTEXT pContext)
{
	/*
	if (!(pContext.Dr6 & 0xF))  //判断是我们引起的中断
	{
		return;
	}
	*/
	
	char szNewText[] = "Hook Success!"; 
	LPSTR lpsstrText = NULL;
	DWORD dwLength = 0;
	DWORD dwOldProtect = 0;
	DWORD dwNewProtect = 0;

	//获取要修改MessageBoxA参数地址,此时还没执行push ebp 和mov ebp, esp 所以esp指向函数返回地址
	//MessageBoxA的参数反向入栈所以 +0x8 为文本参数

	lpsstrText = (LPSTR)(*(DWORD*)(pContext->Esp + 0x8));  
	dwLength = strlen(lpsstrText);  //获取长度
	
	VirtualProtect(lpsstrText, dwLength, PAGE_EXECUTE_READWRITE, &dwOldProtect); //修改权限
	memcpy(lpsstrText, szNewText, dwLength);
	VirtualProtect(lpsstrText, dwLength, dwOldProtect, &dwNewProtect); //将权限修改回去

	return;
}


//顶层异常处理函数
LONG WINAPI HookExceptionFilter(PEXCEPTION_POINTERS pException)
{
	if (pException->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) //单步异常
	{
		if ((DWORD)pException->ExceptionRecord->ExceptionAddress == g_dwHookFunAdd) //判断地址是否为我们加断点的位置
		{

			PCONTEXT pContext =pException->ContextRecord; 	
			
			ChangeContext(pContext); //修改上下文,即修改函数参数等操作

			//修改EIP从断点位置继续执行
			pException->ContextRecord->Eip = (DWORD)(&OriginalFun);

			return EXCEPTION_CONTINUE_EXECUTION;
		}
	}

	return EXCEPTION_CONTINUE_SEARCH;
}


DWORD GetThreadHandle(HANDLE &hHookThread, DWORD dwThreadCount)
{
	HANDLE hThreadSnap = NULL;
	DWORD dwordRet = 0;
	do 
	{
		hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (hThreadSnap == INVALID_HANDLE_VALUE)
		{
			break;
		}

		THREADENTRY32 thread32 = { 0 };
		thread32.dwSize = sizeof(THREADENTRY32);

		DWORD dwCount = 0;  //线程计数,一个进程下有多个线程

		if (Thread32First(hThreadSnap, &thread32))  
		{
			do 
			{  
				if (thread32.th32OwnerProcessID == GetCurrentProcessId())  //判断是当前进程
				{
					dwCount++; 
					if (dwCount == dwThreadCount)  //查找进程中的第几个线程
					{
						hHookThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION,
												FALSE, thread32.th32ThreadID);
					}
				}

			} while (Thread32Next(hThreadSnap, &thread32));
		}

	} while (FALSE);

	if (hThreadSnap != NULL)
	{
		CloseHandle(hThreadSnap);
	}
	return dwordRet;
}

BOOL SetHardwareBreakPointHook()
{
	HANDLE hHookThread = NULL;

	//获取Message地址
	//已经映射进来了直接用GetModuleHandle,否则用LoadLibrary
	g_dwHookFunAdd = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
	g_dwHookFunAddOffset = g_dwHookFunAdd + 2;
	
	//遍历线程找到要HOOK的线程
	GetThreadHandle(hHookThread, 1);
	if (hHookThread == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}

	//设置断点和异常处理函数
	(void)SetUnhandledExceptionFilter(HookExceptionFilter);  //设置一个顶层异常

	//获取线程上下文
	CONTEXT context;
	memset(&context, 0, sizeof(CONTEXT));

	context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
	GetThreadContext(hHookThread, &context);

	//设置断点位置
	context.Dr0 = (DWORD)g_dwHookFunAdd;
	context.Dr7 |= 1;   //设置为局部断点

	//清零
	context.Dr7 &= 0xFFF0FFFF;  //清零 16 - 19位  LEN0 和 RWE0

	//设置断点长度
	context.Dr7 |= 0x00000000; //18 - 19 置0 -- 1字节 (多大都行主要是断下)
	
	//设置断点类型
	context.Dr7 |= 0x00000000;  //执行断点(17、18位置零)

	SetThreadContext(hHookThread, &context);

	CloseHandle(hHookThread);

	return TRUE;
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:

		SetHardwareBreakPointHook();

		break;
	case DLL_PROCESS_DETACH:  
		//卸载HOOK在这添加,需要还原DR0和DR7寄存器和修在顶层处理异常函数
		break;
	}
	return TRUE;
}

蓝色妖姬 – 相守