将代码注入现有的编译模块

时间:2011-07-09 10:48:09

标签: assembly x86

我正在为一些插件工作(我无法访问其来源)并遇到了问题。下面的程序集摘录来自主程序,并且是负责调用我的代码的过程的开始(整个代码非常长,因为它通常能够调用最多16个参数的任何函数加上字符串的相关内存管理,并支持__stdcall或__cdecl)。不幸的是,一个非常重要的值没有通过这个函数传递到我的代码中,“this”指针保存在edx寄存器中。

所以我需要在这个函数的开头注入某种代码(提供的API允许我获取它的地址,在本例中为0x00613ED4),它将把值存储在edx寄存器中供我以后检索

我以前做了一些x86程序集,但是我以前从未需要在运行时将新代码注入到现有模块中,而且我无法找到有关如何执行此类操作的任何信息:(如果这样的事情实际上可以从内部完成C / C ++没有我必须在汇编中写出所有更好的东西。

00613ED4 push       ebp
00613ED5 mov        ebp,esp
00613ED7 add        esp, 0FFFFFE64h
00613EDD push       ebx
00613EDE push       esi
00613EDF push       edi
00613EE0 mov        dword ptr [ebp-19Ch],ecx
00613EE6 mov        ecx,11h
00613EEB lea        eax, [ebp-198h]
...

3 个答案:

答案 0 :(得分:2)

您无法在两个汇编指令之间插入代码。您必须将所有代码调高以腾出空间,这可能会破坏所有硬编码偏移(例如数据段中的跳转目标或偏移)。

可以做的是在相关区域设置代码断点并将iret放入新代码中,然后将新代码ret放入原始代码。

如果要求提高性能,请注意这会损害性能。


另一个类似的选项是使用INT3调试断点。它实际上就是为了这个目的。它的使用方式是,调试器用INT3操作码替换它想要打破的操作码,后者(方便地)是单字节操作码,确保它可以插入任何地方。这将导致跳转到您在注释中建议的异常处理程序。通过检查被推入堆栈的EIP,您将能够分辨出您的来源并采取相应行动。

这将与其他选项具有相同的性能损失。


要考虑两点:

  • 我知道在C / C ++中无法做到这一点,但是我再也不是专家了。
  • 摆弄异常处理程序可能需要在CPL0上工作。你应该确保你的代码可以做到这一点。

答案 1 :(得分:2)

如果您在Linux上工作,可以查看kernel probes (KProbes),这是一个方便的内核工具。它们旨在允许在内核中的几乎任意位置执行几乎任意的代码。它就像是INT 3 / Breakpoint的高级版本。

您可以确定您感兴趣的函数的地址,将KProbes放在它们的第一个字节,并且您提供的处理程序将在这些函数运行时执行。处理程序用C语言编写。它们也接收有关寄存器内容的信息,并且可能允许更改它(虽然不确定后者)。

在内部,KProbes使用INT 3或跳跃和绕行缓冲区,具体取决于具体情况。

答案 2 :(得分:0)

  

只是在想,如果我能以某种方式将某些东西变成jmp,那么我可以把我的代码放在其他地方,做任何我覆盖的东西并跳回到下一条指令之后?

...这是在运行时修补代码的好方法,是的。但根据确切覆盖的指令所做的事情,可能需要考虑细节。代码注入可能比你预期的更难:例如,代码可能只是以只读方式映射。

  

这个代码的整个代码非常长,因为它非常通用,能够调用任意多达16个参数的函数以及字符串的相关内存管理,并支持__stdcall或__cdecl)

所以:这段代码回调你的代码,可以传递参数吗?你能注册至少一个指针大小的参数以及将被回调的函数吗?

如果是这样,你可以自己传递this指针,稍微摆弄一下。您将需要注册一个需要this指针(例如静态成员函数)作为回调的函数,以及this指针作为参数回调;然后,该回调可以解包该参数,以便调用您真正想要调用的成员函数。

像这样的事情(如果很明显我花了大半生的时间来写C而不是C ++,那就道歉了):

#include <iostream>

// Simple C callback interface

extern "C" {

  typedef void (*tCallbackFn)(void *);

  static struct {
    tCallbackFn fn;
    void *context;
  } callback;

  static void set_callback(tCallbackFn fn, void *context)
  {
    callback.fn = fn;
    callback.context = context;
  }

  static void call_callback(void)
  {
    (callback.fn)(callback.context);
  }

}

// Simple test class

class MyClass
{
public:
  // We'll use this for test purposes
  int id;

  // This is the actual member function we want to invoke
  void DoIt(void)
  {
    std::cout << "Callback - id = " << id << std::endl;
  }

  // Static wrapper which we can pass as the function in the callback interface
  static void CallbackWrapper(void *context)
  {
    static_cast<MyClass *>(context)->DoIt();
  }

  // Register wrapper as callback function, with "this" pointer as context
  void Test(void)
  {
    set_callback(CallbackWrapper, static_cast<void *>(this));
    call_callback();
  }
};

// Test it

int main(void)
{
  MyClass m1, m2;

  m1.id = 1;
  m2.id = 2;
  m1.Test();
  m2.Test();

  return 0;
}