VEH
0x00-VEH
VEH的中文名字为:向量化异常处理 (Vectored Exception Handling) ,是为操作系统提供的异常处理机制,类似于SEH,VEH的优先级高于SEH
0x01-VEH回调函数详解
VEH由AddVectorExceptionHandler添加处理函数,处理函数有一个参数
参数类型为PEXCEPTION_POINTERS结构体
结构PEXCEPTION_POINTERS保存着当前异常的各个寄存器,堆栈,地址等多种信息
0x02-VEH Hook原理
1、异常处理结构中,VEH是唯一一个可以接收到所有异常信息的处理。换句话说:所有的异常信息都会经过VEH
2、异常信息通常是由数组越界、内存访问出错、无效参数、int 3等造成的
3、一旦发生异常,操作系统会立即遍历VEH,如果有处理函数,中断线程,并由处理函数处理
思路
如果我们要Hook消息框,首先要给API的首地址写入int 3断点,当执行时会产生异常,线程暂停,转交给异常处理函数处理,此时我们可以在处理函数中修改堆栈参数等
相应操作完成后将int 3(0xCC)修改回源代码 修改EIP=addr(异常地址),然后让此处的代码重新执行一次正确的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include<Windows.h> #include<stdio.h> #pragma data_seg("ldata")
char trapcode[] = "*"; unsigned long long llcode = 204; #pragma data_seg() #pragma comment(linker,"/SECTION:ldata,RWE") void Decshellcode() {
llcode = 0xF333333333333333; llcode <<= 2; char* data = (char*)&llcode;
MessageBoxA(0, "不要再打辣", "停下", MB_OK); return ; } long _stdcall ExceptionHandle(PEXCEPTION_POINTERS val) { if (val->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) { Decshellcode(); val->ContextRecord->Eip = (DWORD)&llcode; return EXCEPTION_CONTINUE_EXECUTION; } else return EXCEPTION_CONTINUE_SEARCH; }
#pragma optimize("",off) int main() { AddVectoredExceptionHandler(1, ExceptionHandle); int bilibili = 2020; int arr[2] = { 1,2 }; int ti = 23; arr[5] = (int)&llcode; return 0; } #pragma optimize("",on)
|
这里修改EIP为我们的Print()函数,因为函数名表示的就是函数地址,所以就是让他执行我们的Print函数
0x03-VEH实现隐藏函数调用
我们知道函数调用时,会先把call的地址压入栈中,而在函数内部的返回ret指令就是从栈中取出call指令的下一条地址
所以我们可以对栈中的地址进行修改让其调用我们的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include<Windows.h> #include<stdio.h>
void showflag() { MessageBoxA(0, "this is flag", "flag", MB_OK); return ; }
long _stdcall ExceptionHandler(PEXCEPTION_POINTERS val) { val->ContextRecord->Eip += 6; val->ContextRecord->Esp -= 4; *(int*)val->ContextRecord->Esp = val->ContextRecord->Eip + 1; val->ContextRecord->Esp -= 4; *(int*)val->ContextRecord->Esp=*(int*)(val->ContextRecord->Ebp-8); return EXCEPTION_CONTINUE_EXECUTION; } int main() { AddVectoredExceptionHandler(1, ExceptionHandler); DWORD a = (DWORD)showflag; int k = 0; int c; c=a / k; __asm { ret } printf("Get flag!!!"); return 0; }
|
具体的可以先看汇编
触发除零异常后,PEXCEPTION_POINTERS val会接收此时的信息
我们先将EIP修改为ret指令,然后将ret下一条指令压入栈中,再把我们要调用的函数压入栈,这时候栈的结构为
返回值为EXCEPTION_CONTINUE_EXECUTION,表示继续往下执行,这时候EIP是ret指令,取出栈顶元素,跳转过去,执行完我们的shellcode后也存在ret,此时的栈为
shellcode尾部的ret取出栈顶的值,跳转过去,回到我们正常的程序
修改EIP的时候也可以
1
| val->ContextRecord->Eip = (DWORD)val->ExceptionRecord->ExceptionAddress+6;
|
ExceptionRecord->ExceptionAddress是触发异常的地址
ExceptionRecord->ExceptionCode表示异常的类型
异常类型