![]() |
Я хочу при помощи мемвраппера хукнуть CNetGame::ShutdownForRestart. Прототип "void(__thiscall*)(CNetGame*)", адрес samp+0xA1E0 для R3. Посмотрев через чит энжин этот адрес я увидел, что там уже стоит джамп хук от лаучеровского плагина libcef.asi.
1) Как быть в данной ситуации если я хочу сохранить оба хука? Я не планирую нопать метод, а лишь выполнить другое действие при его вызове 2) Первое что мне приходит в голову это читать из памяти конечный адрес функции в libcef.asi и использовать его для своего хука. Но в зависимости от названия файла моего плагина он может загружаться позже или раньше libcef.asi, а в 2 случаи там ещё не будет джампа с адресом. Мне это не нравится. Ещё рассматривал вариант сразу искать адрес в libcef.asi по сигнатуре так как это файл потенциально может меняться. Но это решение мне тоже кажется не оптимальным. Как правильнее будет сделать? Мне кажется на такие случаи уже придумано оптимальное решение https://forum.antichat.xyz/attachments/28600424/ https://forum.antichat.xyz/attachments/28600424/ https://forum.antichat.xyz/attachments/28600424/ |
ну так хукни, никаких проблем не будет.
|
Цитата:
|
Цитата:
https://forum.antichat.xyz/attachments/28600454/ |
Цитата:
Про срабатывание 1 раз я имел ввиду хук. Т.е. только при первом вызове ShutdownForRestart мой хук срабатывает. При последующих вызовах ShutdownForRestart мой хук не срабатывает |
Ну значит кто-то снимает твой хук с цепочки вызовов. Как вариант попробуй поставить хук ниже по листингу, либо сделай таймаут на N секунд и только потом поставь хук.
|
Так ты и смотри в дебаггере перезаписывается ли хук или нет
|
Как вариант, чтобы не морочить голову с нейкид хуком из под луа, можешь либо поставить хук на call какой-то функции внутри
Код:
CNetGame::ShutdownForRestartКод:
CLocalPlayer::ResetDataКод:
ShutdownForRestartКод:
sub_1009C580Код:
CPlayerPool::Deactivateтолько щас заметил, что это тема по плюсам и если реализация правда на них, то можешь заюзать какой-нибудь kthook::kthook_naked, который ставится почти в любое место и ничего не ломает, но при этом. можно вызвать свою логику |
Значит я разобрался. Пишу это больше для самого себя чтоб закрепить, но может и кому-то другому пригодиться так как вероятно устройство остальных хуков в лаунчере подобны этим.
P.S. я опушу различные проверки так как они всегда выполняются успешно если не будет стороннего вмешательства. У хука есть 3 глобальных переменных, которые идут друг за другом и используются в нём (адреса могут меняться) libcef.asi + 0x7E5F8 хранит адрес начала оригинальной функции samp.dll + 0xA1E0 libcef.asi + 0x7E5FC хранит адрес на структуру хука с оффсетом 12 байт от начала (4 элемент) libcef.asi + 0x7E600 хранит адрес на начало структуры хука Хуки ставятся в функции libcef.asi + 0x35610 и что в неё происходит: Получаем и записываем в libcef.asi + 0x7E5F8 адрес оригинальной функции Выделяем участок памяти из 5 байт, далее передаём указатель на него функции, которая заполнит его нопами (0x90 nop) В 1 байт участка записываем джамп (0xE9 jmp), далее считаем адрес относительно адрес до функции обработчика и записывает в остальные 4 байта (не забываем -5 из-за размера команды) Выделяем участок памяти в 44 байта. Это по сути та самая структура с данными хука. Представим как массив из 11 элементов 4-байтовых значений После идёт функция куда передаются указатели и она заполняет эту структуру: Код: Код:
[0] = адрес std::_Ref_count_obj2::`vftable' (не используется)Также в нёй снимается защита памяти. 5 байтом начиная с samp.dll + 0xA1E0 устанавливается значение 0x40 (можно исполнять, читать и записывать) И туда записываются эти 5 байт с джампом на функцию обработчик На выходе в libcef.asi + 0x7E5FC записывается адрес 3 индекса нашего массика, который структура хука На выходе в libcef.asi + 0x7E600 записывается адрес начала нашего массика, который структура хука А далее самые первые 5 байт который мы выделили освобождаются. Мы их всё равно скопипастили в участок на который указывает 4 элемент массива https://forum.antichat.xyz/attachments/28601201/ Теперь собственно что в функции обработчике: Все обращения к структуре тут через libcef.asi + 0x7E5FC (т.е. начиная с 3 индекса) При срабатывании первым делом возвращаются оригинальные байты в функцию (указатель на них по 7 индексу массива) Далее при помощи libcef.asi + 0x7E5F8 мы вызываем оригинальную функцию и она выполняется как должно (ведь оригинальные байты мы вернули) После мы копируем оригинальные байты обратно в памяти указатель на которую в 7 индексе и ставим обратно наш джамп из памяти указатель на которую в 4 индексе Дальше выполняется собственная логика скрипта (получается уже после выполнения оригинальной функции) Ровно такой же хук ставится из vorbisFile.dll и он ставится раньше. Получается что libcef.asi сохраняет нее оригинальные 5 байт, а 5 байт джампа, которые записал vorbisFile.dll И при выполнении якобы оригинала функции в libcef.asi мы на самом деле триггерим хук vorbisFile.dll обработчик libcef.asi -> обработчик vorbisFile.dll -> выполнение оригинала -> выполнение логики vorbisFile.dll -> выполнение логики libcef.asi Таким образом у нас 2 независимых скрипта успешно хукают 1 и ту же функцию. И потенциально могут сколько угодно скриптом делать также. Теперь почему мой хук срабатывал только 1 раз: vorbisFile.dll по определению загружается и поставит хук раньше любых asi. Мой asi загружается вторым. libcef.asi последним Выходит libcef.asi сохраняет мой джамп как оригинальные байты и ставит свой хук. При выполнении libcef.asi восстанавливаем мой джамп, запускает его, и тригеррит мой обработчик. Он сразу выполняет свою логику и после хочет выполнить в трамплине оригинальные 5 байт прежде чем вернуться, но это оказываются байты обработчика из vorbisFile.dll и он срабатывает. vorbisFile.dll возвращает уже реально оригинальные байты, выполняет оригинальные скрипт и ставит свой хук обратно. После исполнения его логики происходит retn, который возвращает нас в libcef.asi так как там была инструкции call, а моём хуке возврат был через jmp. libcef.asi обратно сохраняет оригинальные байты (которые хук vorbisFile.dll) и ставит свой хук, после выполняет свою логику. Получается состояние после 1 исполнения ровно такой же как если бы моего хука вовсе не было. Он тупо затирается. vorbisFile.dll и libcef.asi ставят inline хуки. А мой asi ставит jmp хук. В этом и проблема. Мне нужно также использовать inline хук, чтобы все работало как надо. Приветствую исправления моего объяснения на более грамотное. |
| Время: 19:32 |