Сплайсинг функции с известным rva, инструкции в начале функции сохраняются вручную. Библиотечный код (hook.h) Код (Text): /* Записывает jmp hook_fn по адресу addr */ void patch_jmp(unsigned char* addr, void* hook_fn); /* Базовый класс синглтона, возвращающего базу модуля */ template<class Derived> struct ModuleBase { static HMODULE instance() { static HMODULE instance_ = 0; return instance_ ? instance_ : instance_ = Derived::load(); } }; /* Базовый класс перехвата */ template<class Derived, class Module, int rva, int skip> struct ManualSplicingHook { typedef ManualSplicingHook<Derived, Module, rva, skip> ManualSplicingHook_t; static void original(); static DWORD skip_addr; static void install() { skip_addr = (DWORD)Module::instance() + rva; jmp_hook(skip_addr, &Derived::hook); skip_addr += skip; } }; template<typename T, typename U> T clone_type_cast(T, U x) {return (T)x;} #define ORIGINAL_FUNC (*clone_type_cast(&hook, &original)) // В msvc2010 можно использовать decltype: // #define ORIGINAL_FUNC (*(decltype(&hook))&original) Пользовательский код Код (Text): #include "hook.h" struct TargetModule: ModuleBase<TargetModule> { static HMODULE load() {return LoadLibraryA("TargetModule.dll");} }; struct ssl3_read : ManualSplicingHook<ssl3_read, TargetModule, 0x6C770, 5> { static int hook(void *s, void *buf, int cb) { int result = ORIGINAL_FUNC(s, buf, cb); g_ssl3_read_log.write((const char*)buf, result); // полезная нагрузка перехвата return result; } }; DWORD ssl3_read::ManualSplicingHook_t::skip_addr; template<> __declspec(naked) void ssl3_read::ManualSplicingHook_t::original() {__asm{ push ebx // 1 push esi // 1 push edi // 1 push 0 // 2 }__asm jmp dword ptr [skip_addr]}
GoldFinch Если первая инструкция имеет длину менее записываемого участка, то ldasm прикрутить необходимо, енумить потоки, получать их контексты, проверять вхождение каждого Ip в диапазон изменяемых адресов(неар джамп 5-тибайтный как частный случай). При вхождении Ip в диапазон отпускать поток, делать задержку и снова енумить и проверять. Изменять атомарно 8 байт. Если все потоки остановлены, то атомарность не требуется.
Clerk разумеется лучше использовать автоматический анализ и копирование инструкций на точке входа с помощью ldasm Но вместо остановки потоков не проще ли поставить VEH и записать в 1й байт int3, а потом заменить его на e9 ? впрочем это все детали реализации patch_jmp()
GoldFinch Проще не значит что правильно и не даст сбой. Функция может вызываться несколькими потоками. Иной может быть прерван на второй инструкции.
действительно. значит надо по-очереди забивать инструкции int3 если 1я инструкция 5 байт, то все хорошо, ее 1й байт меняем на int3 если 1я инструкция 1-4 байт, то ее можно забить int3 за 1 раз, потом то же для следующей инструкции.
GoldFinch VEH вызывается последовательно, иной поток будет ждать освобождения первым кс RtlpCalloutEntryLock. Таким образом далее диспетчер исключений не может быть удалён. (кстате к деадлоку это приведёт.)
о_О где взял? тоже хочу редактирование) ===== самом деле, как не извращайся, если один из потоков остановлен на инструкции в пределах 5 байт от начала функции, то пока он от туда не уйдет - ничего менять нельзя. но вместо "делать задержку и снова енумить и проверять" можно поменять eip потока на аналогичную инструкцию в сохраненном коде
GoldFinch Почемуже, можно. Например заранее скопировать начало функции в буфер, записать в конец джамп и перенаправлять поток на этот буфер. Это чисто технические детали.
static HMODULE instance() { static HMODULE instance_ = Derived::load();//0; return instance_;// ? instance_ : instance_ = Derived::load(); }
J0E в этом случае генерируется менее оптимальный код - создается дополнительная переменная-флаг для проверки того, были ли статические переменные инициализированы.
Тогда понятна и экономия на проверках Еще экономнее сделоть instance просто статической переменной а не функцией, за счет потери ленивости.
Версия 2. Убрал лишние статические переменные класса. Предполагается что функция splice использует ldasm (ее код не приводится). основной код Код (Text): /* Базовый класс перехвата сплайсингом * * FunctionAddress * класс возвращающий адрес функции для перехвата, содержит статический метод * static DWORD address() */ template<class Derived, class FunctionAddress> struct SplicingHook { typedef SplicingHook<Derived, FunctionAddress> SplicingHook_t; static DWORD& call_addr() { static DWORD callAddr; return callAddr; } template<typename T> static T get_original(T h) { return (T)call_addr(); } static void install() { call_addr() = splice((unsigned char*)FunctionAddress::address(), &Derived::hook); } }; использование Код (Text): struct ssl3_read : SplicingHook<ssl3_read, FunctionImportByRva<TargetModule, 0x6C770> > { static int hook(void *s, void *buf, int cb) { int result = get_original(hook)(s, buf, cb); log_write((const char*)buf, result); return result; } }; ........................ ssl3_read::install();
кстати о качестве кода, генерируемого компилятором С++ (msvc 2008) Код (Text): struct testing : SplicingHook<testing, TestAddr> { static void hook(int x) { get_original(hook)(x * 3); } }; результат Код (Text): 013D19B0 8B 44 24 04 mov eax,dword ptr [esp+4] 013D19B4 8D 04 40 lea eax,[eax+eax*2] 013D19B7 89 44 24 04 mov dword ptr [esp+4],eax 013D19BB FF 25 D4 14 3F 01 jmp dword ptr [`SplicingHook<testing,TestAddr>::call_addr'::`2'::callAddr (13F14D4h)]
UnderCtl, твои навыки телепатии тебя подвели. Я разве написал что мне не нравится такой код? Наоборот, он мне нравится, и я не думаю что тут можно было бы написать код лучше.
насчет вот этой странной конструкции Я так написал чтобы не размещать определение статического поля callAddr в .h файле - спрятал его внутрь функции. Т.к. этот код в .h файле, то по идее это должно было помочь избежать множественных определений в .cpp файлах - нарушения "правила одного определения" (ODR). Но оказывается, в случае с шаблонами, ODR не нарушается, так что можно спокойно писать в .h Код (Text): template<class Derived, class FunctionAddress> struct SplicingHook { static DWORD callAddr; ............. }; template<class Derived, class FunctionAddress> DWORD SplicingHook<Derived, FunctionAddress>::callAddr;