研究背景:
emm...
大概半年之前鹅厂反作弊某次更新后偶然发现在调用AddVectoredExceptionHandler注册了一个异常处理函数不到几分钟就封号了,这让我产生了bypass的想法,于是在网上找了找资料还真有了思路。
下面给大家介绍具体的原理吧。
实现原理:
那段时间在网上看到一个非常有意思的尾部挂钩方法: InstrumentationCallback
在KPROCESS结构的偏移地址0x2c8处,包含一个名为InstrumentationCallback的域,Windows系统Vista以及之后的版本中,可以使用InstrumentationCallback域来指定回调函数的地址,每次函数从内核态返回用户态之后系统都会调用指定的回调函数。
至于原理大致就是以上阐述的,那么了解Windows的异常派遣机制后可以知道每次系统产生异常时会从内核返回到用户层,既然这样那么我们是不是可以通过回调在回用户层的时候先拦截派遣,调用我们的异常处理函数后再放过它回去执行原来的派遣呢?
实践出真理,那就行用来验证想法是否正确吧。
创建一个动态库项目:
定义一个Exception类后来写一个注册回调和异常函数安装的功能吧
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 |
|
至于为什么要自己实现syscall和拿RtlRestoreContext偏移放到后面用的时候再说,这里主要是拿到KiUserExceptionDispatcher函数地址,用于回调中判断是否为我们想拦截的函数。
写一段回调的汇编:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
当我们注册了回调后,内核返回应用层会首先执行我们的CallbackEntry
下面实现MyCallbackRoutine:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
如果rip是KiUserExceptionDispatcher地址那么我们就先调用我们注册的回调函数处理异常后判断返回值影响执行原始异常派遣流程。
上面获取RtlRestoreContext主要是因为在这里直接调用此函数达不到用户模式上下文的设置效果导致进程崩溃,所以我这里就先通过特征码定位到它在KiUserExceptionDispatcher函数中的偏移,修改rip让他返回执行到这里就正常啦!
这里的_self_exception_api类型为:typedef LONG(__stdcall* pfnExceptionHandlerApi)(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT context),后面介绍这个。
到这里回调基本就跑起来了,接下来实现修改设置硬件断点(dr0-3)0-3
实现设置硬断函数:
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
|
这里逻辑比较混乱,毕竟半年前写的了...朋友们这里可以自行修改。
上面说的自己实现syscall是因为怕反作弊勾住NtSuspendThread、NtSetContextThread所以干脆自己写不过它的钩子。
另外对于操作的线程一定要排除掉自己的线程,别把自己线程给暂停了恢复不起来(#^.^#),至于设置硬断的坑朋友们自己去踏吧>.<。
到这里此Exception类基本完成了,下面在类外定义好自己的回调函数
定义自己的异常处理函数:
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 |
|
上图中注释的地方展示了我的Hook点,相信懂鹅厂家反作弊的朋友都知道是什么函数了(#^.^#)。
上面拿了Rsi寄存器,它在保存的是Caller地址
Rsp+0x20指向Callee地址。
最后附上函数调用:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
效果展示:
经过上面一顿折腾后来启动游戏看看效果:
OK,大功告成!
分析到此结束。