![]() |
Всем привет. В последнее время я стал замечать, что абсолютное большинство использует всякие готовые библиотеки для хуков, и даже не заморачиваются о строении функций, типах хуков и прочим. Как таковых гайдов очень мало, а если и есть - на английском языке. В этой теме я расскажу обо всем просто и понятно.
Начнем с банального. Да кто такой все таки, этот ваш, хук?! Хук, с английского обозначает: "ловить". Назвали его так, потому что он ловит вызовы каких либо команд. Не забегая вперед, расскажу о основных командах, которые вы должны знать. Абсолютно все процессы состоят из машинного кода. Это последовательность битов, но не бойтесь, с битами работать нам не потребуется (на начальных этапах). При помощи различных дизассемблеров (я использую IDA Pro), можно этот машинный код превратить в ассемблерный. Если вы собираетесь работать с хуками, вы не обойдетесь без IDA Pro и Cheat Engine, так что устанавливайте эти программы. Рассказывать, как с ними работать, я не буду, в интернете полно гайдов. Нам предоставлен ассемблерный код программы, нам нужно знать две основные команды: JMP - команда прыжка. Ее опкод - 0xE9. Выполняет прыжок с одного место, на другое. Если есть JMP на одно место, обязательно есть прыжок обратно, чтобы не прервать цикл игры. CALL - команда вызова. Имет одинаковую инструкцию размером в 5 байт, как и JMP. Единственное, чем отличается, так это не обязательно прыгать обратно. У этих команд одинаковые инструкции. Допустим, у нас есть адрес 0xFF00FF. Это адрес на инструкцию одной из этих команд. Если не прибавлять ничего, считывая 1 байт (uint8_t) с этого адреса мы получим как раз таки опкод одной из этих команд. По смещению +1 от адреса инструкции, мы получим релативный адрес, размером в 4 байта. Он отличается от обычного адреса, куда хочет прыгнуть или откуда хочет вызвать опкод. Он высчитывается по такой формуле: (куда_прыгаем или откуда_вызываем) - (откуда_прыгаем или где_вызываем) - 5. Допустим, у нас есть такая штучка: Код: Код:
.text:0053ECBD 004 call _IdleЭто вызов функции _Idle. Адрес функции _Idle - 0x0053E920. В данном случае, релативный адрес будет таким: 0x0053E920 - 0x0053ECBD - 5. Этот релативный адрес будет располагаться по адресу инструкции + 1. В данном случае, 0x0053ECBD + 1, и будет иметь размер 4 байта (uint32_t). Получается, чтобы получить опкод, нам нужно выполнить такое чтение: C++: Код:
uint8_tЧтобы получить релативный адрес, такое: C++: Код:
uint32_tЯ думаю, основную логику инструкций JMP и CALL вы поняли, расскажу про первый метод хука, как я его назвал, Redirect. Суть хука заключается в том, чтобы подменить релативный адрес команды JMP/CALL на свой. Чтобы данное провернуть, нужно снять протекцию с региона функцией VirtualProtect, занопить всю инструкцию размером в 5 байт функцией memset (на всякий), подменить опкод на CALL/JMP (0xE8/0xE9), записав 1 байт, и подменить релативный адрес, который высчитать по формуле, которую я представил выше, и восстановить протекцию. C++: Код:
unprotect_regionpointer_on_source - указатель на адрес инструкции JMP/CALL. pointer_on_destination - указатель на вашу функцию, на которую вы подменили вызов. В вашей функции, вы должны вызвать оригинальную функцию, на которую был вызов. В данном случае: C++: Код:
//int __usercall Idle@(int a1@, int a2@, int bp0@, int a4@, int a5@, long double a6@, int a3)Прошу заметить, в idle_hook я не указал __cdecl, только потому что в С++ функции по умолчанию имеют такое соглашение о вызовах. Если бы функция была __stdcall - вы бы записали __stdcall, если бы __thiscall, то пришлось бы поступить немного по-другому. В idle_hook вы бы приписали соглашение о вызовах __fastcall, в idle_t - __thiscall, а в idle_hook первым параметром вы бы записали void *, и вторым тоже. В итоге у вас бы получилось: C++: Код:
usingПочему не __thiscall, спросите вы. А все потому, что компиляторы не дают статическим функциям иметь данное соглашение. __fastcall очень похож по строению пролога (поговорим чуть позже об этом) на __thiscall, только одна разница - во второй параметр добавляется еще один указатель, он не используется. Просто не трогайте его. С Redirect хуками мы разобрались, теперь расскажу о Trampoline. Trampoline от Redirect кардинально отличается. Если Redirect просто подменяет релативный адрес команды вызова или прыжка, то Trampoline взаимодействует с прологом функции. Что такое пролог функции? Пролог функции - первые несколько байт функции, которые подготавливают стек, пушат регистры. У пролога есть свой эпилог. Эпилог отличается тем, что он располагается в конце функции, и восстанавливает стек и регистры до того состояния, которое было до вызова. Расскажу на примере функции void __cdecl CTimer__Update(void): https://forum.antichat.xyz/attachments/27539125/ Логика трамплин хука заключается в том, чтобы этот самый пролог сохранить в отдельную функцию, занопить весь пролог, поставить там прыжок на нашу функцию, в нашей функции вызвать ту, в которой мы сохранили пролог, и вдобавок приписать туда прыжок обратно. Получается так: вместо пролога, jmp -> наша_функция -> jmp трамплин -> jmp обратно (+1, чтобы не было рекурсии). C++: Код:
ifВ использовании: C++: Код:
#include "hook/hook.h"Если останутся вопросы, напишите в комментарии. Исходный код на GitHub - https://github.com/dev-Const/hook/blob/master/hook.h Пара примеров: C++: Код:
#include "hook/hook.h"C++: Код:
#include "hook/hook.h" |
опкодер разлогинься
|
Цитата:
|
мит хук?
|
| Время: 04:07 |