Microsoft Detour - 带有汇编程序“调用”指令的挂钩函数

Posted

技术标签:

【中文标题】Microsoft Detour - 带有汇编程序“调用”指令的挂钩函数【英文标题】:Microsoft Detour - Hook Function with an assembler "call" instruction 【发布时间】:2011-08-15 01:43:02 【问题描述】:

这个板上的第一个问题已经很长了 - 对此我深表歉意(在此感谢大家从这个平台获得的重要提示)。

我正在尝试hook几个函数(它或多或少是插件代码,所以我要hook的函数不是我写的/不能由我更改,但可以在同一个进程/线程中直接访问)微软绕道。

每个需要挂钩的函数都是由 c 风格的编译器生成的,并且总是有以下汇编“启动”代码:

045A1A85  push        ebp  
045A1A86  push        ebx  
045A1A87  push        esi  
045A1A88  push        edi  
045A1A89  call        045A1A8E  
045A1A8E  pop         eax  
045A1A8F  mov         ebx,eax  
//go on with a little bit more assembler code

正如你所看到的,一些寄存器被压入堆栈,然后调用指令被调用到下一行(请不要要求更改它,因为我已经说过我无权访问这个生成的代码)。 call 指令更改堆栈 - 此堆栈更改保存在 eax 寄存器中并用于进一步处理(!!!)

此方法将被挂钩:

045A1A85  jmp         hooking_function (528040h)  
045A1A8A  int         3  
045A1A8B  int         3  
045A1A8C  int         3  
045A1A8D  int         3  
045A1A8E  pop         eax  

钩子函数被定义为一个裸函数,它只是跳转到蹦床函数。

__inline __declspec(naked) void hooking_function()

    //more code in future
    __asm 
        jmp org_func_trampoline
    

具有以下蹦床功能:

031F0060  push        ebp  
031F0061  push        ebx  
031F0062  push        esi  
031F0063  push        edi  
031F0064  call        045A1A8E  
031F0069  jmp         045A1A8E  

主要问题是,蹦床汇编代码中的调用指令 1) 向堆栈添加错误值(在本例中为 031f0064 而不是 045A1A89)--> 使用 eax 进一步处理将收到错误结果 2) 或多或少地破坏堆栈帧,因为下一个“ret”将跳回“031F0069” ==> 相同的处理将执行两次; ret 将被再次调用,导致错误的功能..

请理解,不保证我要挂钩的每个函数都以上述序言开头..因此我无法重写挂钩函数,并忽略蹦床方法...

所以在所有这些文本之后的基本问题: 是否可以将函数与 Microsoft Detour 挂钩,在函数的前 5 个字节中调用调用指令? (如果没有,还有其他选择吗?)

非常感谢您的阅读(希望您的帮助)

【问题讨论】:

地址和一切都相对正确吗?为什么您尝试挂钩的函数会调用下一条指令(call 045A1A8E)? 哦,是的,Detours 可以挂钩一个函数,其中前 5 个字节中有一个调用,只要你正确调用了蹦床。 @Seth Carnegie 感谢您的回答。据我所知,该函数尝试访问寄存器 eip,这可以通过调用下一行并读取堆栈(不是直接)来实现。 哦,地址并不总是相同的(相对跳转)——或者你的意思是什么地址? PS:打电话怎么可能绕道?据我了解(虽然我的汇编知识很基础,如果我错了请纠正我),调用指令会影响堆栈帧,下一个“ret”指令==>下一个“ret”指令总是会跳回到蹦床函数,如果蹦床函数包含一个“调用”,这可能不正确? 当 Detours 绕过某些东西时,它会复制前 5 个(左右)字节(确保不会将指令切成两半)并将它们保存在缓冲区中,然后放在缓冲区的末尾, jmp 回到它绕过它的地方,但加上它从函数中获取的字节数。那是它给你的“原始”函数的地址,所以当你调用它时,执行会转到缓冲区,然后执行前 5 个字节,然后跳回到原始函数 + 5 个字节,然后继续它是快乐的方式。堆栈没有混乱。原代码调用函数时,(更多来) 【参考方案1】:

原代码执行

045A1A89  call        045A1A8E  
045A1A8E  pop         eax  

这只是获取eip 寄存器内容的简单方法。 pop eax 立即从堆栈中删除返回地址 (045A1A8E),并在其执行时将 eax 设置为 eip 的值。

Detours 显然无法知道这一点并将其视为任何子程序,因此它从蹦床执行call 045A1A8E,这将导致eax 的不同值(不同eip)。

我不完全确定为什么它会返回031F0069,从阅读您发布的代码来看它不应该这样做。

所以,是的,这是一个非常特殊的情况。 Detours 通常完全能够挂钩在前几条指令中调用的函数。这个只是设法完美地放置了这两条指令,以便 Detours 以不利的方式将它们分开。

【讨论】:

以上是关于Microsoft Detour - 带有汇编程序“调用”指令的挂钩函数的主要内容,如果未能解决你的问题,请参考以下文章

带有 ASP.NET MVC 5 应用程序的 Microsoft 身份验证循环

MS绕道MakeFile错误

Linux Ubuntu下的绕行功能

带有RabbitMQ.Client.dll异常的Microsoft.Diagnostics.Tracing.EventSource

有没有办法绕过 C++ 构造函数?

EasyHook库系列使用教程之四钩子的启动与停止