Применение шаблонов C++ для перехвата кода

Тема в разделе "LANGS.C", создана пользователем GoldFinch, 25 апр 2010.

  1. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Сплайсинг функции с известным rva, инструкции в начале функции сохраняются вручную.

    Библиотечный код (hook.h)
    Код (Text):
    1. /* Записывает jmp hook_fn по адресу addr
    2.  */
    3. void patch_jmp(unsigned char* addr, void* hook_fn);
    4.  
    5. /* Базовый класс синглтона, возвращающего базу модуля
    6.  */
    7. template<class Derived>
    8. struct ModuleBase
    9. {
    10.     static HMODULE instance()
    11.     {
    12.         static HMODULE instance_ = 0;
    13.         return instance_ ? instance_ : instance_ = Derived::load();
    14.     }
    15. };
    16.  
    17. /* Базовый класс перехвата
    18.  */
    19. template<class Derived, class Module, int rva, int skip>
    20. struct ManualSplicingHook
    21. {
    22.     typedef ManualSplicingHook<Derived, Module, rva, skip> ManualSplicingHook_t;
    23.    
    24.     static void original();
    25.     static DWORD skip_addr;
    26.    
    27.     static void install()
    28.     {        
    29.         skip_addr = (DWORD)Module::instance() + rva;
    30.         jmp_hook(skip_addr, &Derived::hook);
    31.         skip_addr += skip;
    32.     }
    33. };
    34.  
    35. template<typename T, typename U> T clone_type_cast(T, U x) {return (T)x;}
    36.  
    37. #define ORIGINAL_FUNC (*clone_type_cast(&hook, &original))
    38. // В msvc2010 можно использовать decltype:
    39. // #define ORIGINAL_FUNC (*(decltype(&hook))&original)
    Пользовательский код
    Код (Text):
    1. #include "hook.h"
    2.  
    3. struct TargetModule: ModuleBase<TargetModule>
    4. {
    5.     static HMODULE load() {return LoadLibraryA("TargetModule.dll");}
    6. };
    7.  
    8. struct ssl3_read : ManualSplicingHook<ssl3_read, TargetModule, 0x6C770, 5>
    9. {   static
    10.     int hook(void *s, void *buf, int cb)
    11.     {
    12.         int result = ORIGINAL_FUNC(s, buf, cb);
    13.         g_ssl3_read_log.write((const char*)buf, result); // полезная нагрузка перехвата
    14.         return result;
    15.     }
    16. };
    17.  
    18. DWORD ssl3_read::ManualSplicingHook_t::skip_addr;
    19. template<> __declspec(naked) void ssl3_read::ManualSplicingHook_t::original()
    20. {__asm{
    21.     push    ebx    // 1
    22.     push    esi    // 1
    23.     push    edi    // 1
    24.     push    0      // 2
    25. }__asm jmp dword ptr [skip_addr]}
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    GoldFinch
    Если первая инструкция имеет длину менее записываемого участка, то ldasm прикрутить необходимо, енумить потоки, получать их контексты, проверять вхождение каждого Ip в диапазон изменяемых адресов(неар джамп 5-тибайтный как частный случай). При вхождении Ip в диапазон отпускать поток, делать задержку и снова енумить и проверять. Изменять атомарно 8 байт. Если все потоки остановлены, то атомарность не требуется.
     
  3. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Clerk
    разумеется лучше использовать автоматический анализ и копирование инструкций на точке входа с помощью ldasm
    Но вместо остановки потоков не проще ли поставить VEH и записать в 1й байт int3, а потом заменить его на e9 ?

    впрочем это все детали реализации patch_jmp()
     
  4. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    GoldFinch
    Проще не значит что правильно и не даст сбой. Функция может вызываться несколькими потоками. Иной может быть прерван на второй инструкции.
     
  5. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    действительно. значит надо по-очереди забивать инструкции int3
    если 1я инструкция 5 байт, то все хорошо, ее 1й байт меняем на int3
    если 1я инструкция 1-4 байт, то ее можно забить int3 за 1 раз, потом то же для следующей инструкции.
     
  6. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    не.. я похоже не так понял)
    действительно надо энумить потоки
     
  7. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    GoldFinch
    VEH вызывается последовательно, иной поток будет ждать освобождения первым кс RtlpCalloutEntryLock. Таким образом далее диспетчер исключений не может быть удалён.
    (кстате к деадлоку это приведёт.)
     
  8. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Норкоманы ITT.
     
  9. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    о_О где взял? тоже хочу редактирование)

    =====
    самом деле, как не извращайся, если один из потоков остановлен на инструкции в пределах 5 байт от начала функции,
    то пока он от туда не уйдет - ничего менять нельзя.

    но вместо "делать задержку и снова енумить и проверять" можно поменять eip потока на аналогичную инструкцию в сохраненном коде
     
  10. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Slishkom tolsto, bro.
     
  11. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    GoldFinch
    Почемуже, можно. Например заранее скопировать начало функции в буфер, записать в конец джамп и перенаправлять поток на этот буфер. Это чисто технические детали.
     
  12. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    static HMODULE instance()
    {
    static HMODULE instance_ = Derived::load();//0;
    return instance_;// ? instance_ : instance_ = Derived::load();
    }
     
  13. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    J0E
    в этом случае генерируется менее оптимальный код - создается дополнительная переменная-флаг для проверки того, были ли статические переменные инициализированы.
     
  14. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    Тогда понятна и экономия на проверках :) Еще экономнее сделоть instance просто статической переменной а не функцией, за счет потери ленивости.
     
  15. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Версия 2.

    Убрал лишние статические переменные класса.
    Предполагается что функция splice использует ldasm (ее код не приводится).

    основной код
    Код (Text):
    1. /* Базовый класс перехвата сплайсингом
    2.  *
    3.  * FunctionAddress
    4.  *  класс возвращающий адрес функции для перехвата, содержит статический метод
    5.  *  static DWORD address()
    6.  */
    7. template<class Derived, class FunctionAddress>
    8. struct SplicingHook
    9. {
    10.     typedef SplicingHook<Derived, FunctionAddress> SplicingHook_t;
    11.  
    12.     static DWORD& call_addr()
    13.     {
    14.         static DWORD callAddr;
    15.         return callAddr;
    16.     }
    17.  
    18.     template<typename T>
    19.     static T get_original(T h)
    20.     {
    21.         return (T)call_addr();
    22.     }
    23.  
    24.     static void install()
    25.     {      
    26.         call_addr() = splice((unsigned char*)FunctionAddress::address(), &Derived::hook);
    27.     }
    28. };
    использование
    Код (Text):
    1. struct ssl3_read : SplicingHook<ssl3_read, FunctionImportByRva<TargetModule, 0x6C770> >
    2. {
    3.     static int hook(void *s, void *buf, int cb)
    4.     {
    5.         int result = get_original(hook)(s, buf, cb);
    6.         log_write((const char*)buf, result);
    7.         return result;
    8.     }
    9. };
    10.  
    11. ........................
    12.  
    13. ssl3_read::install();
     
  16. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    кстати о качестве кода, генерируемого компилятором С++ (msvc 2008)

    Код (Text):
    1. struct testing : SplicingHook<testing, TestAddr>
    2. {
    3.     static void hook(int x)
    4.     {
    5.         get_original(hook)(x * 3);
    6.     }
    7. };
    результат
    Код (Text):
    1. 013D19B0 8B 44 24 04      mov         eax,dword ptr [esp+4]
    2. 013D19B4 8D 04 40         lea         eax,[eax+eax*2]
    3. 013D19B7 89 44 24 04      mov         dword ptr [esp+4],eax
    4. 013D19BB FF 25 D4 14 3F 01 jmp         dword ptr [`SplicingHook<testing,TestAddr>::call_addr'::`2'::callAddr (13F14D4h)]
     
  17. UnderCtl

    UnderCtl New Member

    Публикаций:
    0
    Регистрация:
    12 фев 2010
    Сообщения:
    16
    Предложи вариант лучше.
     
  18. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    UnderCtl, твои навыки телепатии тебя подвели.
    Я разве написал что мне не нравится такой код?
    Наоборот, он мне нравится, и я не думаю что тут можно было бы написать код лучше.
     
  19. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    насчет вот этой странной конструкции
    Я так написал чтобы не размещать определение статического поля callAddr в .h файле - спрятал его внутрь функции.
    Т.к. этот код в .h файле, то по идее это должно было помочь избежать множественных определений в .cpp файлах - нарушения "правила одного определения" (ODR).
    Но оказывается, в случае с шаблонами, ODR не нарушается, так что можно спокойно писать в .h
    Код (Text):
    1. template<class Derived, class FunctionAddress>
    2. struct SplicingHook
    3. {
    4.     static DWORD callAddr;
    5. .............
    6. };
    7. template<class Derived, class FunctionAddress>
    8. DWORD SplicingHook<Derived, FunctionAddress>::callAddr;