Внедрение кода в существующий скомпилированный модуль - PullRequest
1 голос
/ 09 июля 2011

Я работаю над плагином для чего-то (к которому у меня нет доступа к источнику) и столкнулся с проблемой.Следующий фрагмент сборки взят из основной программы и является началом процедуры, которая отвечает за вызов в мой код (весь код для этого очень и очень длинный, так как он в целом способен вызывать любую функцию с количеством аргументов до 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 ]

2 голосов
/ 10 июля 2011

Если вы работаете в Linux, вы можете взглянуть на зонды ядра (KProbes) , удобное средство в ядре. Они предназначены для выполнения почти произвольного кода в почти произвольном месте ядра. Это как расширенная версия INT 3 / Breakpoint.

Вы можете определить адрес интересующих вас функций, разместить KProbes на их первых байтах, и предоставленные вами обработчики будут выполняться при запуске этих функций. Обработчики написаны на C. Они также получают информацию о содержимом регистров и могут позволить изменить ее (хотя и не уверены в последнем).

Внутри KProbes используют INT 3 или скачки и обходные буферы, в зависимости от ситуации.

2 голосов
/ 09 июля 2011

Вы не можете вставить код между двумя инструкциями по сборке.Вы должны сдвинуть весь код, чтобы освободить место, и это, вероятно, нарушит все жестко закодированные смещения (например, цели перехода или смещения в сегментах данных).

Что вы могли бы do устанавливает точку прерывания кода в соответствующей области и вводит iret в новый код, а затем вводит новый код ret в исходный код.

Если требуется производительность,затем имейте в виду, что это ухудшит производительность.


Другой аналогичный вариант - использовать точку останова отладки INT3.Это на самом деле предназначено именно для этой цели.При использовании отладчик заменяет код операции, который он хочет разбить, на код операции INT3, который (удобно) представляет собой однобайтовый код операции, гарантируя, что он может быть вставлен куда угодно.Это вызовет переход к обработчику исключений, как вы предложили в комментарии.Изучив EIP, который был помещен в стек, вы сможете определить, откуда вы пришли, и действовать соответствующим образом.

Это будет иметь такое же снижение производительности, как и другой вариант.


Два вопроса для рассмотрения:

  • Я не знаю, как это сделать на C / C ++, но опять же, я не эксперт.
  • Возможно, возиться с обработчиками исключенийтребовать работы на CPL0.Вы должны убедиться, что ваш код может это сделать.
0 голосов
/ 10 июля 2011

Просто думал, что если я смогу что-то превратить в 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...