Показать сообщение отдельно

  #9  
Старый 22.04.2025, 16:31
Tema05
Познавший АНТИЧАТ
Регистрация: 05.10.2019
Сообщений: 1,649
С нами: 3477805

Репутация: 168


По умолчанию

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

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' (не используется)
[1] = 0x00000001 (не используется)
[2] = 0x00000001 (не используется)

[3] = адрес оригинальной функции (используется для перезаписи памяти)

[4] = указатель на новый 5-байтовый участок памяти куда будут скопированны данные из предыдущего
[5] = указатель + 5 (записывается уже после выделения памяти)
[6] = указатель + 5 (записывается в функции выделения памяти)

[7] = указатель на новый 5-байтовый участок памяти куда будут скопированны данные из оригинальной функции
[8] = указатель + 5 (записывается уже после выделения памяти)
[9] = указатель + 5 (записывается в функции выделения памяти)
[10] = 0x00000001 (если 1 то по адресу samp.dll + 0xA1E0 наш хук, если 0 то там оригинальные байты)


Также в нёй снимается защита памяти. 5 байтом начиная с samp.dll + 0xA1E0 устанавливается значение 0x40 (можно исполнять, читать и записывать)

И туда записываются эти 5 байт с джампом на функцию обработчик

На выходе в libcef.asi + 0x7E5FC записывается адрес 3 индекса нашего массика, который структура хука

На выходе в libcef.asi + 0x7E600 записывается адрес начала нашего массика, который структура хука

А далее самые первые 5 байт который мы выделили освобождаются. Мы их всё равно скопипастили в участок на который указывает 4 элемент массива



Теперь собственно что в функции обработчике:

Все обращения к структуре тут через 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 хук, чтобы все работало как надо.

Приветствую исправления моего объяснения на более грамотное.
 
Ответить с цитированием