【Windows系统编程】08.异常处理与异常Hook(软件断点Hook,硬件断点Hook)

异常处理

1.结构化异常SEH

#include <iostream>

int main()
{
    goto Exit;
    __try {
        //受保护节
        int a = 0;
        int b = 0;
        int c = a / b;
        std::cout << "触发异常" << std::endl;
    }
    /*
    EXCEPTION_CONTINUE_EXECUTION (-1) 异常已消除。 从出现异常的点继续执行。
    EXCEPTION_CONTINUE_SEARCH (0) 无法识别异常。 继续向上搜索堆栈查找处理程序,首先是所在的 try-except 语句,然后是具有下一个最高优先级的处理程序。
    EXCEPTION_EXECUTE_HANDLER (1) 异常可识别
    */
    //__except (1){
    //    std::cout << "SEH 异常" << std::endl;
    //}
    __finally {
        //终止处理程序,如果程序异常退出,将会执行
        std::cout << "异常退出" << std::endl;
    }
    std::cout << "程序即将退出" << std::endl;
Exit:
    std::cout << "正常退出" << std::endl;
}

2.向量化异常VEH:

_EXCEPTION_POINTERS:包含一个异常记录,其中包含异常的计算机独立描述,以及一个上下文记录。

typedef struct _EXCEPTION_POINTERS {
  PEXCEPTION_RECORD ExceptionRecord;
  PCONTEXT          ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

ExceptionRecord:

其中,ExceptionRecord指向了有关异常说明的PEXCEPTION_RECORD结构体指针:

typedef struct _EXCEPTION_RECORD {
  DWORD                    ExceptionCode;
  DWORD                    ExceptionFlags;
  struct _EXCEPTION_RECORD *ExceptionRecord;
  PVOID                    ExceptionAddress;
  DWORD                    NumberParameters;
  ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

其中,Exception:发生异常的原因,这是由硬件异常生成的代码,常见错误都有宏

ExceptionFlags:异常标志

ExceptionRecord:指向相关联的指针,这是一个链状结构

ExceptionAddress:发生异常的地址

NumberParameters:与一场关联的参数的个数

ExceptionInformation:描述异常的其他参数的数组

ContextRecord:

其中ContextRecord指向了发生异常时,处理器状态的说明(上下文)

#include <iostream>
#include <Windows.h>

LONG MyVEHCallBack(
	struct _EXCEPTION_POINTERS *ExceptionInfo
) {
	//向量化异常需要我们自己选择处理方式,以及是否回去
	//异常代码
	DWORD dwCode = ExceptionInfo->ExceptionRecord->ExceptionCode;
	//判断接收到的是什么异常
	if (dwCode == EXCEPTION_BREAKPOINT) {
		//异常处理
		std::cout << "This is Int3" << std::endl;
		system("pause");
		//我们这里想处理异常后,回去,继续执行后面的代码
		//EIP 指令指针寄存器,保存了下一行要执行的位置
		//我们将EIP+1,就会执行后面的代码了
		ExceptionInfo->ContextRecord->Eip += 1;
		//返回,继续执行
		return EXCEPTION_CONTINUE_EXECUTION;
	}
	return EXCEPTION_CONTINUE_EXECUTION;
	return 0;
}

int main()
{
	/*
	//线程上下文获取
	CONTEXT ctx;
	GetThreadContext(GetCurrentThread(), &ctx);
	//设置线程上下文
	ctx.Dr0 = 111;
	SetThreadContext(GetCurrentThread(), &ctx);
	*/

	//注册向量异常处理程序
	AddVectoredExceptionHandler(
		0,      //调用处理程序的顺序,程序第一个处理异常还是最后处理异常
		(PVECTORED_EXCEPTION_HANDLER)MyVEHCallBack   //指向要调用处理程序的指针,实际上就是我们定义的处理异常函数
	);

	//这里我们使用汇编,触发一个int3断点
	//int3 软件断点 0xCC
	_asm {
		int 3
	}

	std::cout << "异常之后的代码" << std::endl;

	system("pause");
	return 0;
}

异常Hook:

会了异常处理,这里我们来看一种基于异常的Hook:

VEH软件断点HOOK

思路:我们将API实现的第一个字节改为int3断点,然后我们捕获异常,处理这个异常,将API参数修改掉

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

ULONG_PTR MessageBoxProc = (ULONG_PTR)GetProcAddress(GetModuleHandle(L"User32.dll"), "MessageBoxW");

struct _EXECEPTION_HOOK {
	//保存要Hook的地址
	ULONG_PTR ExceptionAddress;
	//原来位置的代码/硬编码
	UCHAR OldCode;
};

_EXECEPTION_HOOK HookInfo;

//这里实际上是设置int3断点
VOID SetVEHHook(ULONG_PTR ProcAddress) {
	DWORD dwProtect = 0;
	VirtualProtect((LPVOID)ProcAddress, 1, PAGE_EXECUTE_READWRITE, &dwProtect);
	//要Hook的地址,就是我们获取的API函数地址
	HookInfo.ExceptionAddress = ProcAddress;
	//函数地址上取一字节,就是原来的硬编码
	HookInfo.OldCode = *(UCHAR*)ProcAddress;
	//将函数内部第一个字节修改位int3
	*(UCHAR*)ProcAddress = 0xCC;
	//修复内存保护属性
	VirtualProtect((LPVOID)ProcAddress, 1, dwProtect, &dwProtect);
}

//这里才是真正的Hook(修改API参数)
LONG VEHHook(
	struct _EXCEPTION_POINTERS *ExceptionInfo
) {
	if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {
		if ((ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress == HookInfo.ExceptionAddress) {
			const WCHAR* szStr = L"我被Hook了";
			*(DWORD*)(ExceptionInfo->ContextRecord->Esp + 8) = (DWORD)szStr;
			ExceptionInfo->ContextRecord->Eip += 2;
			return EXCEPTION_CONTINUE_EXECUTION;
		}
	}
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)VEHHook);
		SetVEHHook(MessageBoxProc);
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


Hook测试:

先运行目标程序(前面讲InlineHook的时候有源码)

注入我们刚生成的dll:

注入dll
Hook成功:

Hook成功

VEH硬件断点HOOK

断点寄存器

下断点需要保存地址:保存到DR0~DR3(断点地址寄存器)

开关:DR7 : 07(L:局部,G:全局)03对应DR0~3

类型:R/W读写域 0~3 (DR0~DR3)占两位

四种状态:

00:执行时断下来(硬件执行断点)

01:写数据的时候断下来(硬件写入断点)

10:I/O终端

11:读写数据的时候都断,但是读指令不断(硬件访问断点)


长度LEN域03(DR0DR3)

四种状态

00:断点一字节长

01:断点2字节长

10:断点8字节长

11:断点4字节长


GD:启用访问检测功能:

如果开启了此位,当修改DR系列寄存器的时候,CPU会触发异常


DR6:

B0~B3:某一组

如果B0被设置为1,说明DR0的R/W,LEN位都被满足条件了

VEH硬件断点优势:不修改内存,如果有内存检测,就不会触发

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

ULONG_PTR MessageBoxProc = (ULONG_PTR)GetProcAddress(GetModuleHandle(L"User32.dll"), "MessageBoxW");

struct _EXECEPTION_HOOK {
	//保存要Hook的地址
	ULONG_PTR ExceptionAddress;
	//原来位置的代码/硬编码
	UCHAR OldCode;
};

_EXECEPTION_HOOK HookInfo;

//这里实际上是设置int3断点
VOID SetVEHHook(ULONG_PTR ProcAddress) {
	DWORD dwProtect = 0;
	VirtualProtect((LPVOID)ProcAddress, 1, PAGE_EXECUTE_READWRITE, &dwProtect);
	//要Hook的地址,就是我们获取的API函数地址
	HookInfo.ExceptionAddress = ProcAddress;
	//函数地址上取一字节,就是原来的硬编码
	HookInfo.OldCode = *(UCHAR*)ProcAddress;
	//将函数内部第一个字节修改位int3
	*(UCHAR*)ProcAddress = 0xCC;
	//修复内存保护属性
	VirtualProtect((LPVOID)ProcAddress, 1, dwProtect, &dwProtect);
}

//这里才是真正的Hook(修改API参数)
LONG VEHHook(
	struct _EXCEPTION_POINTERS *ExceptionInfo
) {
	if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {
		if ((ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress == HookInfo.ExceptionAddress) {
			const WCHAR* szStr = L"我被Hook了";
			*(DWORD*)(ExceptionInfo->ContextRecord->Esp + 8) = (DWORD)szStr;
			ExceptionInfo->ContextRecord->Eip += 2;
			return EXCEPTION_CONTINUE_EXECUTION;
		}
	}
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)VEHHook);
		SetVEHHook(MessageBoxProc);
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


Hook测试:

先运行目标程序

注入dll:

注入dll
Hook成功:
Hook成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shad0w-2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值