相关函数
我们只需要用到 AddVectoredExceptionHandler 设置VEH异常捕获
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
ULONG WINAPI RemoveVectoredExceptionHandler(
_In_ PVOID Handler
);
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
ULONG WINAPI RemoveVectoredContinueHandler(
_In_ PVOID Handler
);
设置异常捕获后,我们的callback就能处理异常
_EXCEPTION_POINTERS 可以获得ExceptionRecord
ExceptionRecord中还有异常代码(ExceptionCode)和异常地址(ExceptionAddress)
VEH HOOK原理
1.将需要HOOK的函数入口内容改为0xcc中断,如果你不了解中断向量表和int软中断你可以理解为设置了个断点,这样运行时就会产生异常
2.由我们的异常处理函数捕获后,修改寄存器的值(原参数)和EIP
3.返回EXCEPTION_CONTINUE_EXECUTION,让程序重新跑起来,就能达到HOOK的效果了
下面是个DLL,你需要自己手动在DLL_PROCESS_ATTACH时运行VEH_HOOK_MessageBoxA();
编译后随便写个EXE载入DLL调用MessageBoxA试试吧
// Dll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "Dll.h"
#include <stdio.h>
#define BREAKPOINTLEN 1
PVOID MessageBoxA_Addr;
BYTE MessageBoxA_New_INT;
BYTE MessageBoxA_Ole_INT;
DWORD dwNewProtect = PAGE_EXECUTE_READWRITE;
DWORD dwOldProtect;
//修改参数
char lpText[] = "Test Text! ";
LONG NTAPI CALL_MessageBoxA(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)//判断异常类型是否是断点
{
PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;//获得异常地址
if (addr != nullptr/*(PVOID)0*/)
{
//+0x8正常情况下是汇编中的第一个参数,+0x4为返回EIP
//但是这里+0x8是汇编中的第二个参数,因为函数开头push ebp的代码被我们改成了0xcc,没有对esp进行压栈
//又由于所有参数从右到左依次入栈,所以顺序其实是反过来的
//+0xc 在MessageBox代码中是第二个lpText
//+0x8 在MessageBox代码中是第三个lpCaption
*(int*)((ExceptionInfo->ContextRecord->Esp) + 0x8) = (int)lpText;
*(int*)((ExceptionInfo->ContextRecord->Esp) + 0xc) = (int)lpText;
//修改状态
ExceptionInfo->ContextRecord->ContextFlags = CONTEXT_CONTROL;
//查询控制寄存器组
ExceptionInfo->ContextRecord->Eip = /*(DWORD)&MessageBoxA;*/(DWORD)addr;
/*修改TF标志位*/
ExceptionInfo->ContextRecord->EFlags |= 0x100;
//恢复代码
VirtualProtect((void*)addr, BREAKPOINTLEN, dwNewProtect, &dwOldProtect);
*(BYTE*)(addr) = MessageBoxA_Ole_INT;//把首个字节地址改回去
VirtualProtect((void*)addr, BREAKPOINTLEN, dwOldProtect, &dwNewProtect);
//OutputDebugStringA("My::单步运行,取消断点");
printf_s("单步运行,取消断点\n");
//AddVectoredExceptionHandler(NULL,(PVECTORED_EXCEPTION_HANDLER)ExceptionInfo);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_EXECUTE_HANDLER;
}
return EXCEPTION_EXECUTE_HANDLER;
}
void VEH_HOOK_MessageBoxA()
{
AddVectoredExceptionHandler(0, CALL_MessageBoxA);//设置VEH异常捕获
MessageBoxA_Addr = (PVOID)&MessageBoxA;//MessageBoxA地址
MessageBoxA_Ole_INT = *(BYTE*)MessageBoxA_Addr; //MessageBoxA 首个字节
VirtualProtect((void*)MessageBoxA_Addr, BREAKPOINTLEN, dwNewProtect, &dwOldProtect);
*(BYTE*)(MessageBoxA_Addr) = 0xcc;//首个字节内容改成0xcc(int 3)断点中断
VirtualProtect((void*)MessageBoxA_Addr, BREAKPOINTLEN, dwOldProtect, &dwNewProtect);
}
关于Eflags|=0x100
看下面的图,程序状态字(标志寄存器),TF在8,但是开头是从0开始的,所以应该在从右向左第9个bit。