首页
论坛
课程
招聘
[原创]YY-一种高可靠性Hook的思路
2014-5-9 01:16 10313

[原创]YY-一种高可靠性Hook的思路

2014-5-9 01:16
10313
看了这个众神出没的帖子:
《那些年搞驱动的那些坑_欢迎补充 》
http://bbs.pediy.com/showthread.php?p=1275930#post1275930
里面提到一种比较纠结的hook情况:
hook时某个线程的EIP正好指向hook位置的中间


最初由 aait发布 查看帖子
你hold住其他 U的时候,总要放开的吧。你一放开,系统里面等待排队的线程有很多个,如果其中某一个线程排队等待恢复的EIP正好是你hook的5个字节的第3个字节怎么办?


这种情况下,EIP恢复后会指向hook的位置的中间,结果是指令被截断后进行解释,很可能会产生异常。

以下是个人针对这种情况的一点思路,不喜勿喷

1.最简单的方法是避免hook的位置原先存在多条指令。我们可以尝试替换掉一条长度>=5的指令,但是这种方法局限性很大,将会严重限制hook的位置。
2.既然如此,就不能想着尽量避免这种情况了,得正面应对,最直接的解决方案是暂停掉整个系统,检查所有的线程的eip…………太复杂太扯淡。
3.之后又想到另一种方案,这也是本文的重点:

我们能通过MDL保证hook位置可写,但是是否能截获这种情况,并作出处理避免悲剧?
A.最简单的截获方法是在异常处理阶段,但是仔细想想这也是不可行的:如果eip停在hook的位置中间,恢复执行后不一定立刻产生异常,代码说不定还会跑很远,我们无法简单地预测之后的行为,并且在这一过程中很可能已经产生了一些不可逆转的错误。

B.那么接下来的思路就是直接截获了,可以采取类似于调试器下软断点的方法,将hook位置所在代码页设置成其NX位(别的位或许也能实现),然后在异常处理例程中作出处理。

C.既然说道断点,硬断点理论上也能实现B中的思路。


不过要实现更高的可靠性最终做的也不是很简单,得根据hook位置的情况采取相应的处理,比如恢复已经执行的指令影响的寄存器等,这里也只是一个思路,没有代码(至少现在没有)。
欢迎讨论~

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

收藏
点赞0
打赏
分享
最新回复 (24)
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-9 01:28
2
0
伪代码:
RtlSafeCopyMemory(pHookAddr,pHookCodeBuffer,dwHookCodeSize);//通过MDL安全写入内存
KeSetHookWatcher(pHookAddr);//通过修改页属性使得执行在hook位置所在页上的代码产生异常,并且挂接异常处理流程(这里可以通过相应的接口,或者hook idt——hook idt是安全的)



处理过程要做的事情是:
1.恢复上一条指令的操作,比如很常见的:
 
00000	55		 push	 ebp
00001	8b ec		 mov	 ebp, esp

而eip指向 mov         ebp, esp,那么我们需要做的就是
add esp,4

同时这种“hook保护机制”在hook后一定时间内即可撤除(之后所有已存在的活动线程都得到调度之后就不存在之前的问题),对于被挂起的线程如果也要处理可以采取别的方法。
雪    币: 272
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AASSMM 活跃值 2014-5-9 01:56
3
0
理论上CPU的缓存机制不会遇到这种情况的
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-9 02:12
4
0
无法保证hook到线程执行这段时间内缓存没有更新
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-9 06:49
5
0
你的意思是cpu l3里面是没有hook的原来的指令,而hook以后是在内存中修改。但是你不要忘了,hook以后,内存区域变成“脏”的标志,这样会促使l3更新,从新从内存中读取数据到l3中。
雪    币: 346
活跃值: 活跃值 (27)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
学雄 活跃值 1 2014-5-9 07:53
6
0
咱也YY下,

不知道有没有人认为这样可行.

我们给全部cpu发送APC消息,
据说系统调用apc的时间是在 KifastCall调用完函数后,返回到应用层时,

此时的代码eip绝对不在我们要hook的代码段中.

进入我们的apc函数中,sleep也好,自旋锁也好,先将cpu挂着,

然后就用这种方式,将除本cpu外的cpu全部挂着,最后hook,貌似挺不错.

上文纯属灵光一闪...

有人会反驳说,这样和"给各个cpu挂DPC有啥不同",个人认为,dpc属于强制中断当前cpu,
使之执行dpc例程,eip当然不会被确定,当然会几率蓝屏~
雪    币: 346
活跃值: 活跃值 (27)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
学雄 活跃值 1 2014-5-9 07:56
7
0
以上方法,细细想想,也有局限性,
那就是,对于hook执行在非 dispatchlevel 级别的api函数好使,
对于执行在dispatchlevel的api函数就不好使了~
雪    币: 346
活跃值: 活跃值 (27)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
学雄 活跃值 1 2014-5-9 07:58
8
0

貌似咱说的,咱也很多不确定是不是这样,如果说错,概不负责~
雪    币: 1164
活跃值: 活跃值 (466)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
小调调 活跃值 2014-5-9 10:01
9
0
楼主,可以看下mhook,它能在hook前有检查,你的eip是否在要hook的代码上
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-9 10:31
10
0
遍历系统中所有线程似乎属于比较蛋疼的行为
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-9 10:44
11
0
像arm那样的risc比较好,指令都是4个字节,一次hook就改4个字节就可以了。
但是64位系统下,不明白32位指令如何能表示64位的地址从而实现64位地址跳转。
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-9 11:29
12
0
或者windows如果有这样一种机制就好了,在每个线程恢复eip 的时候,windows检查eip处的指令是否更改,如果被更改,那么产生一个异常。那么我们hook以后,处理这个异常就好了。
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-9 11:36
13
0
或者windows没有这种机制,我们自己hook windows切换线程的函数,自己添加这样一种机制。
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-9 20:24
14
0
这样的话只能避免Hook立刻蓝屏
雪    币: 8670
活跃值: 活跃值 (770)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2014-5-9 21:43
15
0
hook切换也会遇到相同的问题。
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-10 08:21
16
0
假设windows切换线程的函数叫做“切换管理器”。
那么对于切换管理器的hook,会不会出现 "eip问题呢"?应该是不会的。切换管理器实际上就是
时钟中断处理程序,并不是属于某个线程,不会说某个线程的“恢复eip”正好指向切换管理器。
因为切换管理器的特殊,所以不会出现某个线程的eip指向“切换管理器被hook的5个字节的第3个字节”这样的现象。
雪    币: 346
活跃值: 活跃值 (27)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
学雄 活跃值 1 2014-5-10 08:23
17
0
没明白你说的,
eip已经掌握在了我们的手里,应该不会蓝吧?
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-10 08:39
18
0
对于这样一种机制,我们可以再内存中实时inlinehook进行添加这种机制的操作,应该不会出现eip问题。也可以把这种机制添加到windows 内核文件,对windows内核的文件作出修改,下次启动后,windows 就具有了这种机制。当然,微软公司可以直接在源码中添加这种机制,也许以后的windows版本就会存在这种机制了。
雪    币: 8670
活跃值: 活跃值 (770)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2014-5-10 09:59
19
0
会的,那个东西是非时钟的有其他路径执行,比如我没事KeAttachProcess什么(实际上好多路径)的于是有可能eip卡在Swap里了~
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2014-5-10 11:22
20
0
会不会,谁也说不准,实际做出来才知道。但是,swap的地方,我们可以hook一条指令长度大于5的指令,那是绝对没有eip问题的。总之,安全解决了swap的hook,添加了前文所提的这样一种机制以后,再hook任意地方,都有了一个保险,绝对安全了。再说,可以 在磁盘上修改windows文件,重启以后就具有这种机制。
话说,这个方面,arm就好多了,每个指令都是4个字节,任意hook,根本不会出现这种x86指令集的问题。
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-10 14:10
21
0
1.如果对每个线程都注入APC,这和遍历线程无异,蛋疼~
2.如果只是让每个核心都进入自己的APC,那么还是有无数多处于等待状态的线程的EIP可能位于“危险位置”上。
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-10 14:14
22
0
hook负责切换线程的函数不是不可以,但是本身考虑主题中所说的问题已经是对于稳定性要求非常高的场合了,hook此处对整个系统的性能也会产生影响,添加这样一种机制,需要对每次线程切换都进行判断,还要重载一份内核再配上自动汇编引擎,试问这种场合下能承受这样的代价么。
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-10 14:16
23
0

为何到目前为止木有人指点一下俺提出的这种方法本身~
雪    币: 279
活跃值: 活跃值 (14)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
irp 活跃值 1 2014-5-10 16:20
24
0
[QUOTE=B.那么接下来的思路就是直接截获了,可以采取类似于调试器下软断点的方法,将hook位置所在代码页设置成其NX位(别的位或许也能实现),然后在异常处理例程中作出处理。
C.既然说道断点,硬断点理论上也能实现B中的思路。
[/QUOTE]

大多数函数和其他函数共享内存页,执行其他函数也会异常,有的函数运行在dispatch_level以上,bugcheck。实际中都是大干快上,从目标函数选择合适的指令来hook, 比如扫描jmp,call
替换他们,大多数有hook价值的目标函数满足这个条件,基本都是non-leaf函数。总之分析各种条件,最终化归为一个基本等价问题: 和IAT hook 等价的成本和安全度,patch aligned dword, atomic write.
雪    币: 105
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 活跃值 1 2014-5-11 01:45
25
0
额确实通过异常截获确实带来了很大的局限性,利用调试寄存器或许效果会更好一些。
游客
登录 | 注册 方可回帖
返回