inline hook 的几种方式概述
阅读本文之前推荐阅读这篇博客http://blog.csdn.net/masefee/article/details/6326634,里面详细介绍了三种inline hook 的方法并给出了代码实例。
- 最基本的hook:保存目标函数的前5个字节,然后覆盖其为跳转指令,跳转到我们的代码的位置,在执行完我们的代码之后恢复代码的前5个字节,将函数返回地址设置为我们的程序,然后继续执行hook代码,执行结束后我们再重复hook的过程。优点是简单通用,缺点是效率低,线程不安全。在进行代码拷贝的过程中可能发生问题。
利用windows 函数预留的代码空间进行hook。原本这部分代码是留给windows 进行热更新的,所谓热更新就是在程序运行的时候改变程序的指令逻辑。
关于windows 函数开始处的5个NOP指令及MOV EDI,EDI 的作用:http://blog.csdn.net/jcwkyl/article/details/3598982
摘要:之所以不直接执行,运行时修改一个函数的行为。短跳转到前5个NOP指令,前五个指令完成一个长跳转。之所以不用NOP:为了效率,单个时使用NOP,两个时使用MOV 指令。优点:易于操作,线程安全。缺点:对大部分windows函数有效,但是对于自定义的函数不通用。在程序任意位置进行hook,前面两种hook 方法都是在函数的前几个字节进行的hook,原则上讲这样的做法更加稳定,因为函数运行一小段代码后其执行环境可能发生比较大的改变,此时改变函数的流程可能会破坏代码原来的逻辑。文章开头给出的链接中,作者给出了一种任意位置hook 的方法,通过作者的代码及介绍可以发现,这种方法其实也就是上面介绍的第一种方法,即hook操作将特定范围的指令设置为call 指令,调用我们的代码,之后的unhook 就是恢复原来的代码段。不同的是,作者通过写栈区控制函数返回地址的操作,将函数的返回地址设置为hook函数,然后函数执行完之后跳转到我们的自己的hook 函数中,进一步判断是否需要继续hook,需要的话,就调用hook 函数,否则hook 的操作就会结束。这种任意位置hook的方法同样是线程不安全的,总的感觉上类似一种调试程序的行为,可以在自己的hook 函数中检测程序的运行状态,作者是利用这种方法来检测游戏外挂的。平时的程序中还是尽量不使用这种方法来hook吧,如果需要使用另类的方法来监视程序的运行状态的话,在保证线程安全的情况下可以使用这种方法。
难度最大,但是最稳定通用的做法。针对不同的函数,利用反汇编引擎不断得到代码指令的长度,直到累加长度>= 跳转指令所需要的长度。之后将代码的前几个字节做特殊处理(之后讨论)然后将函数入口的前几个指令修改为跳转指令。跳转指令跳转到一个跳板函数,跳板函数负责整个hook的流程。跳板函数首先执行我们的替代函数,函数返回后,我们将执行一段代码。这段代码是从前面提到的目标函数的入口的前几个指令转变得到的。我们都知道,入口可能有很多种操作,我们前面的处理过程中就是将这些操作转以满足“这些指令的执行位置可能发生改变,但是执行效果相同”,比如说,可能函数前面有RIP 相对寻址的操作(X64),我们需要求得当前执行代码的位置与之前指令的位置的插值以修正RIP 相对寻址中的偏移以保证程序正确运行,再比如,可能该部分代码仅仅是进行建立栈帧以及开栈区的操作,这样的话只要我们之前函数的操作没有改变栈的结构(这是必须的),我们只要简单的复制代码就可以了,再比如函数的前几个指令是跳转指令,这样的情况也是比较简单的,因为,我们只要在跳转执行完我们的代码之后跳转到该目标地址就可以了(注意相对跳转与绝对跳转的区别)。hook 的时候一定得注意栈的操作以及寄存器的保存与恢复。