SEH(Struct Exception Handler,结构化异常) VEH(Vector Exception Handler,向量异常处理)
SEH是OS提供给线程来感知和处理异常的一种回调机制。
在Intel Win32平台上,由于FS寄存器问题指向当前的TIB(线程信息块),因此FS:[0]处能找到最新的一个EXCEPTION_REGISTRATION_RECORD结构。
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
EXCEPTION_ROUTINE (
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext
);
typedef EXCEPTION_ROUTINE *PEXCEPTION_ROUTINE;
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
XP之后,在基于线程的SEH基础增加了基于进程的VEH
比较:
1.SEH基于线程,VEH基于进程
2.优先级:调试器 > VEH > SEH 即KiUserExceptionDispatcher()函数先检查进程是否处理调试,然后VEH,最后SEH.
3.SEH单链表,VEH双链表,VEH节点可挂在头上或尾上。
注册VEH的回调API:
PVOID WINAPI AddVectoredExceptionHandler(
__in ULONG FirstHandler,
__in PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
IA-32处理器定义了8个调试寄存器(DR0-DR7) DR0-DR3用于指点内存地址或I/O地址 DR4-DR5保留,DR6事件发生报告详细信息,DR7定义中断条件。
硬件断点HOOK是结合DR0-DR3调试寄存器和Winows SEH或VEH机制所引入的HOOK机制,因不涉及修改代码,不易检验检测到。
//测试EXE
#include <stdio.h>
#include <Windows.h>
#include <vector>
typedef LONG (WINAPI *PVECTOREDEXCEPTIONHANDLER)(PEXCEPTION_POINTERS ExceptionInfo);
typedef PVOID (WINAPI *ADDVECTOREEXCEPTIONHANDLER)(
ULONG FirstHandler,
PVECTOREDEXCEPTIONHANDLER VectoredHandler
);
typedef struct _VECTORED_EXCEPTION_NODE {
LIST_ENTRY ListEntry;
PVECTORED_EXCEPTION_HANDLER pfnHandler; // 该指针出于安全目的已经被加密
} VECTORED_EXCEPTION_NODE, *PVECTORED_EXCEPTION_NODE;
LONG CALLBACK VectoredHandlerinit1(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
return EXCEPTION_CONTINUE_SEARCH;
}
LONG CALLBACK VectoredHandlerinit2(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
return EXCEPTION_CONTINUE_SEARCH;
}
LONG CALLBACK VectoredHandlerinit3(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
return EXCEPTION_CONTINUE_SEARCH;
}
LONG CALLBACK VectoredHandler(__in PEXCEPTION_POINTERS ExceptionInfo)
{
ExceptionInfo;
return EXCEPTION_CONTINUE_EXECUTION;
}
int CheckVEHHook(std::vector<PVOID>& vecVEH, bool bcache, bool bdelete = false)
{
PVOID pExceptionHandler = NULL;
PVECTORED_EXCEPTION_NODE pCurrent = NULL;
PVECTORED_EXCEPTION_NODE pNext = NULL, pDel = NULL;
pCurrent = (PVECTORED_EXCEPTION_NODE)AddVectoredExceptionHandler(0,VectoredHandler);
if( pCurrent == NULL )
{
printf("AddVectoredExceptionHandler111 failed.\n");
return 0;
}
pNext = (PVECTORED_EXCEPTION_NODE)pCurrent->ListEntry.Blink;
//pNext = (PVECTORED_EXCEPTION_NODE)pNext->ListEntry.Flink;
printf("fake addr:0x%.8x pfnHandler:0x%.8x VEH function.\n",
pCurrent, DecodePointer(pCurrent->pfnHandler));
for( ; pNext != pCurrent; )
{
pExceptionHandler = DecodePointer(pNext->pfnHandler);
if( pExceptionHandler )
{
if( bcache )
{
vecVEH.push_back(pNext);
}
printf("found addr:0x%.8x pfnHandler:0x%.8x VEH function.\n",pNext,pExceptionHandler);
pNext = (PVECTORED_EXCEPTION_NODE)pNext->ListEntry.Blink;
if( bdelete )
{
std::vector<PVOID>::iterator findit = find(vecVEH.begin(),vecVEH.end(),pNext);
if( findit == vecVEH.end() )
{
pDel = pNext;
pNext = (PVECTORED_EXCEPTION_NODE)pNext->ListEntry.Blink;
printf("#### addr:0x%.8x pfnHandler:0x%.8x VEH function is illegal.\n",pDel,pExceptionHandler);
if( RemoveVectoredExceptionHandler(pDel) )
{
printf("####the 0x%.8x is deleted.\n",pDel);
}
else
{
printf("####delete failed.\n");
}
break;
}
}
}
}
if( RemoveVectoredExceptionHandler(pCurrent) )
{
printf("the fake node 0x%.8x is deleted.\n",pCurrent);
}
else
{
printf("delete failed.\n");
}
return 0;
}
DWORD func_addr = 0, func_addr_offset = 0;
void __declspec(naked) ReturnOriginalFunc(void) {
__asm {
mov edi,edi
//push ebp way 222
jmp [func_addr_offset]
}
}
LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
func_addr = (DWORD)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA");
func_addr_offset = func_addr + 0x2;
printf("***catch some exception, don't know who hook that.\n");
if((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress == func_addr) {
//if((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress == func_addr+2) { way 222
PCONTEXT debug_context = ExceptionInfo->ContextRecord;
printf("only blind cat meet mouse !\n");
debug_context->Eip = (DWORD)&ReturnOriginalFunc;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
void main()
{
SetUnhandledExceptionFilter(ExceptionFilter);
//事先加3个进去
AddVectoredExceptionHandler(1,VectoredHandlerinit1);
AddVectoredExceptionHandler(0,VectoredHandlerinit2);
AddVectoredExceptionHandler(1,VectoredHandlerinit3);
MessageBox(NULL,"the fact infor111","test SEH hook",MB_OK);
std::vector<PVOID> vecVEH;
printf("-----------------before LoadLibraryA.----------------\n");
CheckVEHHook(vecVEH,true,false);
::LoadLibraryA("WaiGua.dll");
printf("-----------------after LoadLibraryA.----------------\n");
CheckVEHHook(vecVEH,false,false);
MessageBox(NULL,"the fact infor222","test SEH hook",MB_OK);
printf("-----------------delete LoadLibraryA added veh.-----\n");
CheckVEHHook(vecVEH,false,true);
MessageBox(NULL,"the fact infor333","test SEH hook",MB_OK);
//如果用下面的,是字面值需要VirtualProtect,并且和第2个MessageBox指向同一地址,则弹出内容还是被改变的。
//MessageBox(NULL,"the fact infor222","test SEH hook",MB_OK);
getchar();
}
//VEH hook dll.
// VEHHook.cpp : Defines the entry point for the DLL application.
//
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <limits.h>
#include <Winbase.h>
typedef HANDLE (WINAPI *OPENTHREAD) (DWORD dwFlag, BOOL bUnknow, DWORD dwThreadId);
OPENTHREAD g_lpfnOpenThread = NULL;
typedef LONG (WINAPI *PVECTOREDEXCEPTIONHANDLER)(PEXCEPTION_POINTERS ExceptionInfo);
typedef PVOID (WINAPI *ADDVECTOREEXCEPTIONHANDLER)(
ULONG FirstHandler,
PVECTOREDEXCEPTIONHANDLER VectoredHandler
);
ADDVECTOREEXCEPTIONHANDLER g_AddVectorExceptionHandler = NULL;
DWORD func_addr = 0x00401000;
DWORD func_addr_offset = func_addr + 0x2;
void PrintParameters(PCONTEXT debug_context)
{
printf("EAX: %X EBX: %X ECX: %X EDX: %X\n",
debug_context->Eax, debug_context->Ebx, debug_context->Ecx, debug_context->Edx);
printf("ESP: %X EBP: %X\n",
debug_context->Esp, debug_context->Ebp);
printf("ESI: %X EDI: %X\n",
debug_context->Esi, debug_context->Edi);
printf("Parameters\n"
"HWND: %X\n"
"text: %s\n"
"caption: %s\n",
(HWND)(*(DWORD*)(debug_context->Esp + 0x4)),
(char*)(*(DWORD*)(debug_context->Esp + 0x8)),
(char*)(*(DWORD*)(debug_context->Esp + 0xC)));
}
void ChangeText(PCONTEXT debug_context) {
char* text = (char*)(*(DWORD*)(debug_context->Esp + 0x8));
int length = strlen(text);
DWORD oldprotect = 0;
VirtualProtect(text,length,PAGE_EXECUTE_READWRITE,&oldprotect);
_snprintf(text, length, "Be Hooked!");
VirtualProtect(text,length,oldprotect,&oldprotect);
}
void __declspec(naked) ReturnOriginalFunc(void) {
__asm {
mov edi,edi
jmp [func_addr_offset]
}
}
LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
if((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress == func_addr) {
PCONTEXT debug_context = ExceptionInfo->ContextRecord;
printf("Breakpoint hit!\n");
PrintParameters(debug_context);
ChangeText(debug_context);
debug_context->Eip = (DWORD)&ReturnOriginalFunc;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
void VEHHook(void)
{
HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if(hTool32 != INVALID_HANDLE_VALUE)
{
THREADENTRY32 thread_entry32;
thread_entry32.dwSize = sizeof(THREADENTRY32);
FILETIME exit_time, kernel_time, user_time;
FILETIME creation_time;
FILETIME prev_creation_time;
prev_creation_time.dwLowDateTime = 0xFFFFFFFF;
prev_creation_time.dwHighDateTime = INT_MAX;
HANDLE hMainThread = NULL;
if(Thread32First(hTool32, &thread_entry32))
{
do { //取最早启动的线程作为hook对象
if(thread_entry32.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(thread_entry32.th32OwnerProcessID)
&& thread_entry32.th32OwnerProcessID == GetCurrentProcessId()
/*&& thread_entry32.th32ThreadID != GetCurrentThreadId()*/)
{
HANDLE hThread = g_lpfnOpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,
FALSE, thread_entry32.th32ThreadID);
GetThreadTimes(hThread, &creation_time, &exit_time, &kernel_time, &user_time);
if(CompareFileTime(&creation_time, &prev_creation_time) == -1)
{
//creation_time 小于 prev_creation_time时候为-1
memcpy(&prev_creation_time, &creation_time, sizeof(FILETIME));
if(hMainThread != NULL)
CloseHandle(hMainThread);
hMainThread = hThread;
}
else
CloseHandle(hThread);
}
thread_entry32.dwSize = sizeof(THREADENTRY32);
} while(Thread32Next(hTool32, &thread_entry32));
//(void)SetUnhandledExceptionFilter(ExceptionFilter);
g_AddVectorExceptionHandler(1, ExceptionFilter);
CONTEXT thread_context = {CONTEXT_DEBUG_REGISTERS};
thread_context.Dr0 = func_addr;
thread_context.Dr7 = (1 << 0);
SetThreadContext(hMainThread, &thread_context);
CloseHandle(hMainThread);
}
CloseHandle(hTool32);
}
}
int APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved)
{
if(reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
if(AllocConsole())
{
freopen("CONOUT$", "w", stdout);
SetConsoleTitle("Console");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
printf("DLL loaded.\n");
}
HMODULE hDll = ::LoadLibrary("kernel32.dll");
g_lpfnOpenThread = (OPENTHREAD)::GetProcAddress(hDll, "OpenThread");
func_addr = (DWORD)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA");
func_addr_offset = func_addr+2;
printf("MessageBoxA Addr: 0x%x\n",func_addr);
g_AddVectorExceptionHandler = (ADDVECTOREEXCEPTIONHANDLER)GetProcAddress(GetModuleHandle("kernel32.dll"), "AddVectoredExceptionHandler");
VEHHook();
}
return TRUE;
}