Как стать автором
Обновить

Как написать свою кроссплатформенную библиотеку для хуков и не отстрелить себе ноги

Время на прочтение15 мин
Количество просмотров5.1K
Всего голосов 20: ↑19 и ↓1+18
Комментарии10

Комментарии 10

Парсить файл /proc/self/maps и доставать оттуда информацию о занятых регионах.

И это не будет работать во FreeBSD

чтобы работало на всех unix-like системах.

Так unix-like или все таки linux ?

Благодарю за внимательность! Обновил парсинг в библиотеке под FreeBSD

Так unix-like или все таки linux ?

На момент написания, казалось что оно одинаковое для всех unix-like систем. Оказывается у Linux свое, у FreeBSD свое.

На самом деле там все "тоньше". procfs во фряхе как бы вообще не существует. Есть порт навроде "linux-procfs". Но это было когда я с ней последний раз общался.

Просто не кидайтесь словами "unix-like совместимо" если ваш скрипт на bash например )

Ну а по серьёзке - кому нужны хуки, если есть сорцы ?

Спасибо за статью. Вспомнил молодость при прочтении, первые шаги с библиотекой AdvApiHook от Ms-Rem, открывшей тогда дорогу в мир хуков )

За упорство конечно плюс! Но над интерфейсом либы кажется стоит еще поработать. У меня есть большой опыт пользования всяких разных либ и фреймворков для хукинга, но глядя на примеры в гитхабе, волосы начинают шевелиться даже там где их нет.

Примеры в README к сожалению полуактуальные. В какой-то момент сделал глобальный рефакторинг, но не поправил readme. Слишком объемная работа на тот и в данный момент.
Наверное лучше в tests смотреть.

Передавать в обработчик хука какие то лишние параметры? Странная идея. Тем более, что, обработчик один, стало быть указатель на обьект будет всегда один и тот же. Смысл его передавать?

Что бы не приходилось пересобирать инструкции в трамплине и суспендить потоки, лучше использовать Hot Patching в Windows, он для этого и был создан.

Передавать в обработчик хука какие то лишние параметры? Странная идея. Тем более, что, обработчик один, стало быть указатель на обьект будет всегда один и тот же. Смысл его передавать?

Вроде в самом начале статьи же про глобальное состояние...

Что бы не приходилось пересобирать инструкции в трамплине и суспендить потоки, лучше использовать Hot Patching в Windows, он для этого и был создан.

Ага. А что делать с кодом, к которому нет доступа, и перекомпилировать с включенным Hot Patching нет возможности?
И даже если так. Писать отдельный велосипед для винды, и отдельный для unix'ов? Мне кажется все же проще один общий велосипед написать, чем два отдельных, зависящих от настроек компиляции кода

Вроде в самом начале статьи же про глобальное состояние..

Это костыль.

А что делать с кодом, к которому нет доступа, и перекомпилировать с включенным Hot Patching нет возможности?

Не совсем понимаю о чем вы. Hot pathing присутствует во всех системных библиотеках Windows. Ничего не надо перекомпилировать.

Это костыль.

Почему же? Позволяет например, абстрагироваться от конечной "хукаемой" функции, и написать шаблонную лямбду, которая решит все сама. Как пример, хук на все LoadLibrary. Пример конечно абстрактный, можно было-бы полезть в недра kernel32.dll, и найти там функцию LdrLoadDll, и поставить хук на нее, но все же. На практике даже такие функции иногда полезны.

auto dispatch_load_calls = [](const auto& hook, auto str_ptr, auto&&... args) {
    auto in_cef_module = [](std::uintptr_t addr) {
        auto dll = reinterpret_cast<std::uintptr_t>(GetModuleHandleA("libcef.dll"));

        auto ntheader = reinterpret_cast<IMAGE_NT_HEADERS*>(
            dll + reinterpret_cast<IMAGE_DOS_HEADER*>(dll)->e_lfanew);

        if (dll < addr && addr < dll + ntheader->OptionalHeader.SizeOfImage) {
            return true;
        }
        return false;
    };

    if (in_cef_module(hook.get_return_address())) {
        constexpr bool is_single_byte = std::is_same_v<decltype(str_ptr), LPCSTR>;

        using str_view = std::conditional_t<is_single_byte, std::string_view, std::wstring_view>;
        using str = std::conditional_t<is_single_byte, std::string, std::wstring>;
        constexpr auto cef_directory = names_choose<is_single_byte>::prefix;
        constexpr auto& names_array_ref = names_choose<is_single_byte>::value;

        str_view load_name{str_ptr};

        for (auto name : names_array_ref) {
            if (load_name.find(name) != str_view::npos) {
                str fname{cef_directory};

                fname += name;

                return hook.get_trampoline()(fname.c_str(), args...);
            }
        }
    }
    return hook.get_trampoline()(str_ptr, args...);
};

load_liba_hook.set_dest(reinterpret_cast<void*>(GetProcAddress(kernel32, "LoadLibraryA")));
load_libw_hook.set_dest(reinterpret_cast<void*>(GetProcAddress(kernel32, "LoadLibraryW")));
load_libexa_hook.set_dest(reinterpret_cast<void*>(GetProcAddress(kernel32, "LoadLibraryExA")));
load_libexw_hook.set_dest(reinterpret_cast<void*>(GetProcAddress(kernel32, "LoadLibraryExW")));

load_liba_hook.set_cb(dispatch_load_calls);
load_libw_hook.set_cb(dispatch_load_calls);
load_libexa_hook.set_cb(dispatch_load_calls);
load_libexw_hook.set_cb(dispatch_load_calls);

Если бы не было абстракции от глобального состояния, пришлось бы коллбэк под каждую функцию писать. Также уход от глобального состояния позволяет делать проект модульным, и внутри разных модулей использовать хуки на одну и ту же конечную функцию. Чего, например, нельзя сделать используя MinHook, или PolyHook

Не совсем понимаю о чем вы. Hot pathing присутствует во всех системных библиотеках Windows. Ничего не надо перекомпилировать.

Процитирую себя еще раз, и дополню:

Ага. А что делать с кодом, к которому нет доступа, и перекомпилировать с включенным Hot Patching нет возможности?И даже если так. Писать отдельный велосипед для винды, и отдельный для unix'ов? Мне кажется все же проще один общий велосипед написать, чем два отдельных, зависящих от настроек компиляции кода

Зачем писать разный код для выполнения одного и того же действия на разных платформах. Гораздо проще описать общий принцип, нет?

И все еще остается проблема с перехватами стороннего кода, вне стандартных библиотек Windows, или например на Linux. Или например мне захочется добавить функциональность к существующей функции игры(гейммоддинг). Игры с Hot Patching'ом никто не компилирует. Предлагаете сидеть с разбитым корытом, из-за того, что библиотека прикручена к функции Hot patching?

Также, не у всех системных библиотек и их функций есть HotPatching.
Как пример - d3d9.dll, а именно функция IDirect3DDevice9::Present.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории