首页
社区
课程
招聘
[原创]函数hook注意事项
发表于: 2012-12-1 16:17 16435

[原创]函数hook注意事项

2012-12-1 16:17
16435

我们在深入测试一些没有代码的软件时,往往需要进行函数hook,简单地说就是把目标函数替换我们的函数,用比较技术的语言描述就是:

(1) Target函数:要拦截的函数,通常为Windows的API。
(2) Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。
(3) Detour函数:用来替代Target函数的函数(往往是我们自己写的函数)。
一般进行函数hook的原理是在Target函数的开头加入JMP Address_of_ Detour_Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_Target _ Function + 5作为Trampoline函数地址。

        其实关于函数hook方法的资料已经汗牛充栋,而且微软也早就推出了官方的库来辅助这一过程,无需我再赘述,我在这里只想谈下面一个话题:如何写好Detour函数,这是我们总结了前人和自身实践经验得出的。
        进行过函数hook实践的同学是否有过这种经历,注入我们的代码或dll,对目标函数进行hook后,执行到我们的函数或者执行过我们的函数后,程序会出错或崩溃(甚至蓝屏)。这是正常的,编写detour函数和普通的编程很不一样,它就像深入敌后的间谍,要考虑很多细节,稍不注意会破坏Context,影响目标进程的正常运行。那么要注意什么呢?
1.        自己定义的detour函数和目标target函数的参数很返回值完全一致
因为没有代码,所以对于目标函数的参数和返回值的确定是需要逆向分析的,在有pdb的情况下很嗨,直接IDA等工具能帮你准确确定。没有pdb的情况下需要通过参数所占内存大小、特征甚至动态调试去确定,这里想强调一点是指针类型、数值类型、和引用类型的区分很关键且容易出错。另外有一种可怕的经验是返回值是一个结构,没经验无从讲起,但愿不要遇到这种情况。
2.        函数调用方式的确定与选择
我假设大家都知道了常见的cdecl和stdcall还有fastcall三种调用方式的异同,通常情况下碰到的函数调用都是cdecl和stdcall方式。他们最大的区别是前者是函数调用者负责栈平衡,后者是函数内负责栈平衡。我们编写detour函数时最好明确声明函数调用方式,原则是和target函数声明方式保持一致。Tips:就hook这种特殊情形,一般用stdcall更方便,因为我们的控制范围是函数内部,要向控制函数调用上下文要绕写弯路,而stdcall是在函数内部控制栈平衡的,so,under control!
3.        现场保护
在编写detour函数时要时刻有一个观念就是这段代码的正确运行是以不破坏当前进程正常运行为前提的,但一个问题是我们往往不知道进程需要用到哪些现场,所以就是通过pushad-popad对寄存器进行完整保护。但应该在什么情况下使用pushad和popad呢?这个需要视具体情况而定,但有一个原则是:在开始执行自己的指令前pushad,在执行原生代码前popad,这个原则不是很准确,只是想表示:不要让自己的指令执行破坏原来的context。
4.        This:“勿忘我!”
对于hook一般的系统函数,在detour函数中可以直接调用Trampoline函数,但在hook一些类成员函数后,不能直接调用Trampoline函数,因为进程确定对象成员函数需要以该对象this指针为入口进行索骥。所以hook了类成员函数,又要在detour函数中调用Trampoline函数,需要在调用前将对象的地址(this指针的内容)存入ecx中。
        就我目前的经验和水平只能总结这些,其实上述的123点背后都有一个共同的背后灵就是“栈平衡”,函数调用的最基本规则就是要在函数调用前和函数调用结束后保证sp指在同一个地方,所以你如果碰到hook崩溃的情况,第一步想到的调试方法就是对比查看函数执行前后的栈的情况然后就是比较寄存器的异同。

补充:来自taker的观点
“很少用Detours,具体细节不太了解。不过在HOOK的时候,如果是自己写代码而没有用Detours的话,记得带上反汇编引擎去算被HOOK命令的长度,否则会出现同样的HOOK函数 有时候崩有时候不崩的情况(之前针对该问题测试过DeTours,发现DeTours里有反汇编引擎处理了指令长度)
还有就是注意寄存器。由于对DeTours不了解,不知道在HOOK后它有没有其他的包装,如果有的话 那么就有可能破坏寄存器(VS2010起大部分函数调用都会更多的使用寄存器而非纯堆栈。OBJECTIV C写的代码也是 大部分使用REG,反正这个是由编译器决定的)。最好是使用nake函数。避免普通函数构造EBP/ESP框架破坏堆栈,并自己的函数头上PUSHAD PUSHFD,尾上POPFD POPAD。在代码中堆栈操作部分加上0x28就可以了。但这样就不能构造函数内局部变量。除非你自己手工维护堆栈。”


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 6
支持
分享
最新回复 (10)
雪    币: 61
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
那除过堆栈平衡之外,哪里还会出错?   我也是遇到问题,第一看堆栈,但是堆栈没有任何问题了,还是会崩溃。。。找不到问题,到现在还放着没管
2012-12-1 21:09
0
雪    币: 51
活跃值: (61)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
呵呵 这种事常有哦  有没对比所有寄存器的变换 堆栈没问题可能是函数类型 参数类型 不一致。
2012-12-1 21:52
0
雪    币: 3279
活跃值: (3331)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
1 你的函数里面非法内存访问也会导致挂掉
2 hook的时机不对
2012-12-1 22:24
0
雪    币: 2829
活跃值: (2633)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
5
多线程得挂起,再hook
2012-12-1 22:48
0
雪    币: 51
活跃值: (61)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
顶 楼上两位 , 一看就经验丰富, 或者说也被坑过
2012-12-2 12:57
0
雪    币: 61
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
函数参数其实不一定要一模一样吧。。。不过要用汇编写,用汇编[ebp + 8]这样去访问也可以的,只是根据编译器的不同而编译的不同的话,代码可移植性就差。而我的问题是我访问参数成功了,我hook的是send函数,然后将截获到的发包数据显示,这功能都成功了。。。最后蹦了。
2012-12-2 13:45
0
雪    币: 61
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这个倒是未考虑到 。          不懂得如何挂起其他线程。。。   求指教
2012-12-2 13:58
0
雪    币: 51
活跃值: (61)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
补充一下来自高手taker的指点:
“很少用Detours,具体细节不太了解。不过在HOOK的时候,如果是自己写代码而没有用Detours的话,记得带上反汇编引擎去算被HOOK命令的长度,否则会出现同样的HOOK函数 有时候崩有时候不崩的情况(之前针对该问题测试过DeTours,发现DeTours里有反汇编引擎处理了指令长度)
还有就是注意寄存器。由于对DeTours不了解,不知道在HOOK后它有没有其他的包装,如果有的话 那么就有可能破坏寄存器(VS2010起大部分函数调用都会更多的使用寄存器而非纯堆栈。OBJECTIV C写的代码也是 大部分使用REG,反正这个是由编译器决定的)。最好是使用nake函数。避免普通函数构造EBP/ESP框架破坏堆栈,并自己的函数头上PUSHAD PUSHFD,尾上POPFD POPAD。在代码中堆栈操作部分加上0x28就可以了。但这样就不能构造函数内局部变量。除非你自己手工维护堆栈。”
2012-12-3 13:34
0
雪    币: 228
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
这里有一个简单的C++ 封装 APIHook
对于一般的挂钩,由于线程的并发性而出现的挂钩失败的机会还是比较少的,几乎可以忽略,除非被挂钩的那段代码被频繁的执行(比如像图形渲染等死循环执行的线程所执行的代码),那么挂钩时崩溃的机会多一些。但如果是在系统内核挂钩的话,那么这个问题绝对要避免
2012-12-3 14:01
0
雪    币: 92
活跃值: (100)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
11
我这里也有个封包截取程序老是崩溃。。不知道原因,感觉很无力。。。
2013-5-3 03:34
0
游客
登录 | 注册 方可回帖
返回
//