首页
社区
课程
招聘
[原创]关于VEH无痕hook的一些小把戏
2023-1-16 06:23 12496

[原创]关于VEH无痕hook的一些小把戏

2023-1-16 06:23
12496

众所周知VEH hook是为了躲避CRC检测而诞生的一项技术,古老但实用,故在此总结一下关于VEH hook的几种方法

 

先来简单介绍一下如何添加VEH处理函数,微软开放给了我们一个API:

AddVectoredExceptionHandler(ULONG First PVECTORED_EXCEPTION_HANDLER Handler)

第一个参数为true则代表插入VEH链表头,第二个参数则是要添加的VEH处理函数

 

那么来讲讲常见的hook方式

1.DR寄存器硬件断点

1
2
3
4
5
6
7
8
9
10
11
12
13
SuspendThread(hHookThread);
 
CONTEXT thread_context = { CONTEXT_DEBUG_REGISTERS };
thread_context.Dr0 = HookAddr;
thread_context.Dr7 = 0x405;
 
//设置线程环境 抛出异常
 
DWORD oldprotect;
VirtualProtect((LPVOID)HookAddr, 5, PAGE_EXECUTE_READWRITE, &oldprotect);
SetThreadContext(hHookThread, &thread_context);
 
ResumeThread(hHookThread);

这是最为常见的VEH hook方式,利用硬件断点抛出异常,在异常处理函数里判断发生异常的地址是否和hook的地址一致,如果一致,则修改EIP(RIP)跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LONG NTAPI  ExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
 
    //判断当前异常码是否为硬件断点异常
    if (ExceptionInfo->ExceptionRecord->ExceptionCode== EXCEPTION_SINGLE_STEP)
    {
 
        //判断hook地址
        if (ExceptionInfo->ExceptionRecord->ExceptionAddress == HookAddr)
        {   
            /*在这里做你想做的事或者直接跳转到你的Hook函数中
            ........................
                            */
            ExceptionInfo->ContextRecord->Eip=(DWORD)&OriginalFunc;   
 
            return EXCEPTION_CONTINUE_EXECUTION;
 
        }
    }
    return EXCEPTION_CONTINUE_SEARCH;//继续向下搜索
}

缺点:容易被检测(当然也可以Hook KiUserExceptionDispatcher来清空Dr寄存器规避检测),且用户最多只能够设置4个硬件断点(Dr0~Dr3)

2.利用PAGE_GURAD属性

先讲讲PAGE_GURAD是个什么东东,他是一个"附属属性",不能单独使用,当内存区域设置了这个属性时,该内存区域第一次被访问时将抛出一个STATUS_GUARD_PAGE_VIOLATION异常,随后属性会被系统自动清除

这里就涉及到一个问题了,咱们想要这个Hook点不止一次被触发,所以该怎么做呢?
这里给出两种方法:

 

a.利用单步异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo) {
 
    if(ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
        // 这里不修改单步的话就会陷入无限死循环
        if(ExceptionInfo->ContextRecord->Eip == reinterpret_cast<uintptr_t>(printf)) {
 
            ExceptionInfo->ContextRecord->Eip = reinterpret_cast<uintptr_t>(print_woo);
        }
 
        ExceptionInfo->ContextRecord->EFlags |= 0x100;
        return EXCEPTION_CONTINUE_EXECUTION;
    }
 
 
 
    if(ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) {
        DWORD prot_buff;
        VirtualProtect(reinterpret_cast<PVOID>(printf), 1, PAGE_EXECUTE_READ | PAGE_GUARD, &prot_buff);
//在这里重新设置上保护
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

b.利用析构函数(PolyHook做法)
这是个十分巧妙的做法,利用了C++中析构函数的特性,其原理就是构造一个恢复保护的析构函数,并在你的hook函数内初始化一个类,当程序执行完后(包括返回原函数),对象销毁,析构函数执行恢复保护
这里附上大概例子,详细做法各位可以移步 PolyHook 这是个十分优秀的Hook引擎

1
2
3
4
5
6
7
8
int __stdcall NewFunction(int param)
{
 
    auto ProtectionObject = VEHHook_Ex->GetProtectionObject();
 
    return OldFunction(param);//当原函数执行完后,我们将会执行析构函数
 
}

最后是触发方法,只需:

1
VirtualProtect(reinterpret_cast<PVOID>(HookAddress), 1, PAGE_EXECUTE_READ | PAGE_GUARD, &pro)

缺点:容易被查页属性,好处就是没有动DR寄存器(emmmmmm)

3.0xCC断点

将Hook点首字节修改为0xCC,网上有例子,这里不再赘述

4.PAGE_HOOK

这应该说是所有常见VEH hook方法中最为隐蔽的了,将页面修改为只读这种不可执行属性,一但执行便会触发C0000005,只需像上面一样接管他就行

1
VirtualProtect(reinterpret_cast<PVOID>(HookAddress), USN_PAGE_SIZE, PAGE_READ_ONLY, &pro)

但由于影响的是一整个页面,也就是说这一整个页都会产生异常,所以说一个页面不能存在多个Hook点~但是有菊花说的好,办法总比困难多,这里说一下我的思路(用来Hook模块内函数,比较无脑且垃圾,大佬勿喷)

 

首先遍历整个进程的模块,找出要Hook的那个函数所在的模块,获取模块的地址以及大小,然后直接新建一块同样大小的内存,然后把整个模块拷贝到这块内存内,将这块内存中的“要Hook的函数”直接Inline Hook,然后通过异常将EIP(RIP)重定向到这块内存中“要Hook的函数”(ImageBase + HookAddress_Offset),直接不走原来的函数

 

这种方法的缺陷呢就是太太太太慢了,而且做法非常繁琐,所以说只适合Hook某些API,如果大佬们有什么更好的方法可以不吝赐教的话,不胜感激


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞4
打赏
分享
最新回复 (3)
雪    币: 12
活跃值: (388)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
MaMy 2023-3-11 11:26
2
0
这种Query一下代码段就g,或者enum有没有第二份gamebase 也G...
crc走到最后还是要正面对抗
雪    币: 49
活跃值: (62)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
水木凌风 2023-6-24 16:59
3
1
可否留下联系方式 我想定制这个page hook
雪    币: 19299
活跃值: (28933)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-6-24 21:19
4
1
感谢分享
游客
登录 | 注册 方可回帖
返回