• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток! Спешите принять участие в конкурсе "Таинственные миры" 2024!
    Ждем именно вас!

    Ссылка на конкурсную тему - тык

Union - Кодогенерация хуков

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.815
Баллы
240
Программа не актуальна: уже есть решение интегрированное в Union SDK, а также дополнительное, через фрагменты кода - для моментальной установки хука (но с более "грязным" кодом)

Программа для кодогенерации хука, по имени класса и сигнатуре его метода.


Пример использования:

1. Запускаем программу
2. В единственное поле вводим имя класса, с которым работаем. Например, zCWorld.
3. Сворачиваем программу в трей, чтобы не мешалась.
4. Копируем сигнатуру метода класса zCWorld из .h файла в буфер обмена. Например:
Код:
 int __fastcall TraceRayNearestHitCache( zVEC3 const&, zVEC3 const&, zCArray<zCVob*> const*, int, zCRayCache* ) zCall( 0x006223F0 );
5. Вставляем новое содержимое буфера обмена куда нужно:
C++:
int __fastcall Hook_zCWorld_TraceRayNearestHitCache(zCWorld*, zVEC3 const&, zVEC3 const&, zCArray<zCVob*> const*, int, zCRayCache*);
CInvoke<int(__fastcall*)(zCWorld*, zVEC3 const&, zVEC3 const&, zCArray<zCVob*> const*, int, zCRayCache*)> Ivk_zCWorld_TraceRayNearestHitCache(0x006223F0, &Hook_zCWorld_TraceRayNearestHitCache);
int __fastcall Hook_zCWorld_TraceRayNearestHitCache(zCWorld* _this, zVEC3 const& a1, zVEC3 const& a2, zCArray<zCVob*> const* a3, int a4, zCRayCache* a5)
{
    int result = Ivk_zCWorld_TraceRayNearestHitCache(_this, a1, a2, a3, a4, a5);
    return result;
}

На полноту программа не претендует - корректно обрабатывает большинство __thiscall и __fastcall методов (ну, или я так думаю :D).
 

Вложения

  • HookGenerator.exe.zip
    7 KB · Просмотры: 47
Последнее редактирование:

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Гуд тема *thumbs up* К слову есть один фокус структуры TInstance, она слегка ломает логику плюсов.
В нее в качестве аргумента можно передать адрес функции без привязки к типу, например передавать в хук непосредственно нестатический метод класса.
C++:
CInvoke<void(__thiscall*)(zCMenu*)> Ivk_zCMenu_Render( 0x004DDC20, &zCMenu::Render_Union )

void zCMenu::Render_Union() {
    cmd << string::Hex32( (uint)this ) << endl;
    Ivk_zCMenu_Render( this );
}


Для более полного понимания к void* нельзя напрямую присвоить &zCMenu::Render_Union
Если только всякими костылями типа такого, и то при условии что так или иначе придется привязаться к типу.
C++:
    typedef void( __thiscall zCMenu::*TMenu_Method )( );
    TMenu_Method menu_method = &zCMenu::Render_Union;
    void* pointer = *(void**)&menu_method;
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.815
Баллы
240
например передавать в хук непосредственно нестатический метод класса.
Я сначала так и делал: заменял zCall-определение своим, пока урок про CInvoke не соизволил прочесть :D
К слову есть один фокус структуры TInstance
Интересный хак. Негоже ему за кулисами оставаться :)
C++:
    void* ToVoidPtr(...)
    {
        va_list args;
        args = (va_list)(_ADDRESSOF(args) + 12);

        void* result = va_arg(args, void*);
        va_end(args);
       
        return result;
    }
Пост автоматически объединён:

Я так понял, для виртуальных методов компилятор 2 функции генерит? Одна выполняет написанный код, другая шарит по виртуальной таблице.
 
Последнее редактирование:

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Я сначала так и делал: заменял zCall-определение своим, пока урок про CInvoke не соизволил прочесть :D

Интересный хак. Негоже ему за кулисами оставаться :)
C++:
    void* ToVoidPtr(...)
    {
        va_list args;
        args = (va_list)(_ADDRESSOF(args) + 12);

        void* result = va_arg(args, void*);
        va_end(args);
       
        return result;
    }
Пост автоматически объединён:

Я так понял, для виртуальных методов компилятор 2 функции генерит? Одна выполняет написанный код, другая шарит по виртуальной таблице.
Да, поэтому при перехвате функции ее нельзя перенаправлять в виртуальный метод, иначе она войдёт в рекурсию. Делай невиртуальный метод дублёр.
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.815
Баллы
240
Подумываю такие классы нагенерить:
C++:
    class CInvoke_zCWorld_VobRemovedFromWorld
    {
    private:
        enum State { ATTACHED, DETACHED, MOVEDOUT };

        static std::vector<CInvoke_zCWorld_VobRemovedFromWorld*> hooks;
        static CInvoke<void(__thiscall*)(zCWorld*, zCVob*)>* ivk;
        static int attachedCount;

        State state;
        int index;
        std::function<void(zCWorld*, zCVob*)> handler;

        static void __fastcall Hook(zCWorld* _this, void* vtable, zCVob* vob);
        static void IncreaseAttachedCount(int delta);

        void Attach();
        void Detach();

    public:
        CInvoke_zCWorld_VobRemovedFromWorld();
        CInvoke_zCWorld_VobRemovedFromWorld(const std::function<void(zCWorld*, zCVob*)>& handler);
        CInvoke_zCWorld_VobRemovedFromWorld(CInvoke_zCWorld_VobRemovedFromWorld&& source);

        void SetHandler(const std::function<void(zCWorld*, zCVob*)>& handler);
        void operator ()(zCWorld* _this, zCVob* vob);
     
        ~CInvoke_zCWorld_VobRemovedFromWorld();
    };
Смысл в том, что теперь не хук знает каким объектам он нужен, а объект знает какие хуки ему нужны - подрубает/отрубает хуки в соответствии со своей внутренней логике и независимо от других объектов. Плюс можно перенаправлять методы движка в экземплярные методы своего класса при помощи std::bind. Также не нужно будет возиться с адресами, фастколами и зысколами. Статические хуки станут попроще в написании:
C++:
    void Hook(zCWorld* world, zCVob* vob);
    Gothic_II_Addon::CInvoke_zCWorld_VobRemovedFromWorld ivk(&Hook);
    void Hook(zCWorld* world, zCVob* vob)
    {
        ivk(world, vob);
        Message::Info("Vob removed from the world!");
    }
Вроде есть в этом смысл...

Правда, нарисовалась проблема с порядком вызовов деструкторов при шатдауне...
UPD: Проблему с деструктором решил.
 
Последнее редактирование:

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Также не нужно будет возиться с адресами, фастколами и зысколами. Статические хуки станут попроще в написании:
Ты уходишь в C++11, это противоречит моему правилу стараться не привязывать к движку стандарты vcrt выше 2010.

Могу как вариант предложить реализацию проще. Тут тоже не нужно вручную прописывать указатель на метод, когда достаточно сделать что-то такое:
74586

Либо тоже самое, но с редиректом в обычную функцию
74587

Фактически конструкция CInvoke<full-pointer-type> name( from, to )
меняется на auto name = Intercept( from, to )
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.815
Баллы
240
Ты уходишь в C++11, это противоречит моему правилу стараться не привязывать к движку стандарты vcrt выше 2010.
Какое-то жесткое правило. Чем чревато его нарушение? Если я начинаю пользоваться фишками С++11, то мой плагин начинает линковаться с другой версией vcrt?
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Перемешиваться. Чревато плавающими крашами. Я в свое время устал их ловить и просто начал компилировать под 2010. Баг полностью исчез, да и пользователю не нужно страдать фигнёй и ставить редисты на игру 20ти летней давности.

Вопрос: НУЖЕН ли готике С++11?
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.815
Баллы
240
Я в свое время устал их ловить и просто начал компилировать под 2010.
Так я же используемый компилятор не меняю. По идее код должен по тем же правилам генериться.
Вопрос: НУЖЕН ли готике С++11?
Он больше мне нужен. Просто приятней на более современном языке кодить.

В настройках проекта у меня стоит Platform Toolset = Visual Studio 2010 (v100). Синтаксис С++11 я использовать не могу, но классы, введенные в этом стандарте, мне доступны.
 
Последнее редактирование:

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.371
Благодарности
7.802
Баллы
995
В настройках проекта у меня стоит Platform Toolset = Visual Studio 2010 (v100). Синтаксис С++11 я использовать не могу, но классы, введенные в этом стандарте, мне доступны.
Интересный вариант, может проскочить..
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.815
Баллы
240
Синтаксис С++11 я использовать не могу
Точнее могу, но не весь. Range-based for loops не могу использовать. enum class тоже.
Интересный вариант, может проскочить..
Dependecy Walker в зависимостях плагина тоже левых версий vcrt не находит. Если я правильно его понимаю.
74615
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Какие интересные и непонятные вещи :)
 
Сверху Снизу