Мини-гайд по хукам. [Теория + Практика]

C++
Забаненный
Забаненный
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
171
Реакции
58
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Кодеры разлогиньтесь! :SMOrc:
В последнее время я стал замечать, что абсолютное большинство использует всякие готовые библиотеки для хуков, и даже не заморачиваются о строении функций, типах хуков и прочим. Как таковых гайдов очень мало, а если и есть - на английском языке. В этой теме я расскажу обо всем просто и понятно.

Начнем с банального. Да кто такой все таки, этот ваш, хук?! Хук, с английского обозначает: "ловить". Назвали его так, потому что он ловит вызовы каких либо команд. Не забегая вперед, расскажу о основных командах, которые вы должны знать.
Абсолютно все процессы состоят из машинного кода. Это последовательность битов, но не бойтесь, с битами работать нам не потребуется (на начальных этапах). При помощи различных дизассемблеров (я использую IDA Pro), можно этот машинный код превратить в ассемблерный. Если вы собираетесь работать с хуками, вы не обойдетесь без IDA Pro и Cheat Engine, так что устанавливайте эти программы. Рассказывать, как с ними работать, я не буду, в интернете полно гайдов.
Нам предоставлен ассемблерный код программы, нам нужно знать две основные команды:
JMP - команда прыжка. Ее опкод - 0xE9. Выполняет прыжок с одного место, на другое. Если есть JMP на одно место, обязательно есть прыжок обратно, чтобы не прервать цикл игры.
CALL - команда вызова. Имет одинаковую инструкцию размером в 5 байт, как и JMP. Единственное, чем отличается, так это не обязательно прыгать обратно.
У этих команд одинаковые инструкции. Допустим, у нас есть адрес 0xFF00FF. Это адрес на инструкцию одной из этих команд. Если не прибавлять ничего, считывая 1 байт (uint8_t) с этого адреса мы получим как раз таки опкод одной из этих команд. По смещению +1 от адреса инструкции, мы получим релативный адрес, размером в 4 байта. Он отличается от обычного адреса, куда хочет прыгнуть или откуда хочет вызвать опкод. Он высчитывается по такой формуле: (куда_прыгаем или откуда_вызываем) - (откуда_прыгаем или где_вызываем) - 5.
Допустим, у нас есть такая штучка:

C++:
Expand Collapse Copy
.text:0053ECBD 004                 call    _Idle

Это вызов функции _Idle. Адрес функции _Idle - 0x0053E920. В данном случае, релативный адрес будет таким: 0x0053E920 - 0x0053ECBD - 5. Этот релативный адрес будет располагаться по адресу инструкции + 1. В данном случае, 0x0053ECBD + 1, и будет иметь размер 4 байта (uint32_t).
Получается, чтобы получить опкод, нам нужно выполнить такое чтение:

C++:
Expand Collapse Copy
uint8_t call_opcode = *reinterpret_cast<uint8_t *>(0x0053ECBD);

Чтобы получить релативный адрес, такое:

C++:
Expand Collapse Copy
uint32_t relative_address = *reinterpret_cast<uint32_t *>(0x0053ECBD + 1);

Я думаю, основную логику инструкций JMP и CALL вы поняли, расскажу про первый метод хука, как я его назвал, Redirect.
Суть хука заключается в том, чтобы подменить релативный адрес команды JMP/CALL на свой. Чтобы данное провернуть, нужно снять протекцию с региона функцией VirtualProtect, занопить всю инструкцию размером в 5 байт функцией memset (на всякий), подменить опкод на CALL/JMP (0xE8/0xE9), записав 1 байт, и подменить релативный адрес, который высчитать по формуле, которую я представил выше, и восстановить протекцию.

C++:
Expand Collapse Copy
unprotect_region protect_of_region(pointer_on_source, size_of_default_instruction); // Инициализируем класс unprotect_region. Снимаем защиту с региона памяти.
            
original_instructions.first = *reinterpret_cast<uint8_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source)); // Записываем в пару оригинальный опкод команды.
original_instructions.second = *reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source) + 0x01); // Записываем в пару оригинальный релативный адрес.

std::memset(pointer_on_source, no_operation_opcode, size_of_default_instruction); // Ноплю всю инструкцию JMP/CALL, её статичный размер для x86 - 5 байт.
*reinterpret_cast<uint8_t *>(pointer_on_source) = method_of_hook; // Меняем оригинальный опкод на опкод команды, которую мы указали в параметрах.

uint32_t relative_address =
    reinterpret_cast<uint32_t>(pointer_on_destination) -
    reinterpret_cast<uint32_t>(pointer_on_source) - 5; // Вычисляем релативный адрес прыжка от pointer_on_source до pointer_on_destination.
// Если говорить проще - прыгаем с оригинальной функции на нашу.

*reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source) + 0x01) = relative_address; // Перезаписываем релативный адрес на собственный.

protect_of_region.~unprotect_region(); // Вызываем деструктор класса, восстанавливаем оригинальный уровень протекции региона.

pointer_on_source - указатель на адрес инструкции JMP/CALL. pointer_on_destination - указатель на вашу функцию, на которую вы подменили вызов.

В вашей функции, вы должны вызвать оригинальную функцию, на которую был вызов.
В данном случае:

C++:
Expand Collapse Copy
//int __usercall Idle@<eax>(int a1@<ecx>, int a2@<edx>, int bp0@<ebp>, int a4@<edi>, int a5@<esi>, long double a6@<st0>, int a3)

using idle_t = int(__cdecl *)(int, int, int, int, int, long double, int);
idle_t idle = reinterpret_cast<idle_t>(0x0053E920);

int idle_hook(int a1, int a2, int bp0, int a4, int a5, long double a6, int a3) {
    return idle(a1, a2, bp0, a4, a5, a6, a3); // Вызываем оригинальную функцию.
}

Прошу заметить, в idle_hook я не указал __cdecl, только потому что в С++ функции по умолчанию имеют такое соглашение о вызовах. Если бы функция была __stdcall - вы бы записали __stdcall, если бы __thiscall, то пришлось бы поступить немного по-другому.
В idle_hook вы бы приписали соглашение о вызовах __fastcall, в idle_t - __thiscall, а в idle_hook первым параметром вы бы записали void *, и вторым тоже. В итоге у вас бы получилось:

C++:
Expand Collapse Copy
using idle_t = int(__thiscall *)(void *, int, int, int, int, int, long double, int);
idle_t idle = reinterpret_cast<idle_t>(0x0053E920);

int __fastcall idle_hook(void *_this, void *unused, int a1, int a2, int bp0, int a4, int a5, long double a6, int a3) {
    return idle(_this, a1, a2, bp0, a4, a5, a6, a3); // Вызываем оригинальную функцию.
}

Почему не __thiscall, спросите вы. А все потому, что компиляторы не дают статическим функциям иметь данное соглашение. __fastcall очень похож по строению пролога (поговорим чуть позже об этом) на __thiscall, только одна разница - во второй параметр добавляется еще один указатель, он не используется. Просто не трогайте его.

С Redirect хуками мы разобрались, теперь расскажу о Trampoline.
Trampoline от Redirect кардинально отличается. Если Redirect просто подменяет релативный адрес команды вызова или прыжка, то Trampoline взаимодействует с прологом функции.
Что такое пролог функции? Пролог функции - первые несколько байт функции, которые подготавливают стек, пушат регистры.
У пролога есть свой эпилог. Эпилог отличается тем, что он располагается в конце функции, и восстанавливает стек и регистры до того состояния, которое было до вызова.
Расскажу на примере функции void __cdecl CTimer__Update(void):

Пожалуйста, авторизуйтесь для просмотра ссылки.


Логика трамплин хука заключается в том, чтобы этот самый пролог сохранить в отдельную функцию, занопить весь пролог, поставить там прыжок на нашу функцию, в нашей функции вызвать ту, в которой мы сохранили пролог, и вдобавок приписать туда прыжок обратно.
Получается так: вместо пролога, jmp -> наша_функция -> jmp трамплин -> jmp обратно (+1, чтобы не было рекурсии).

C++:
Expand Collapse Copy
if (length_of_prologue < 5) { // РАЗМЕР ПРОЛОГА НЕ МОЖЕТ БЫТЬ МЕНЬШЕ 5! Запомните это раз и навсегда.
    return;
}

prologue_length = length_of_prologue;

/*
    Выделяем виртуальную память размером с размер пролога + 5.
    Представим ситуацию, мы хукаем функцию, у которой размер пролога 10,
    но мы выделяем 10 + 5, а не 10. Почему?
    Потому-что в первые 10 байт запишется пролог, а в остальные 5 байт
    запишется прыжок на оригинальную функцию, чтобы не сохранять в трамплин весь ее код.
*/
pointer_on_gateway = VirtualAlloc(nullptr, length_of_prologue + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(pointer_on_gateway, pointer_on_source, length_of_prologue); // Копируем байты пролога в указатель.

uint32_t relative_address =
    reinterpret_cast<uint32_t>(pointer_on_source) -
    reinterpret_cast<uint32_t>(pointer_on_gateway) - 5; // Вычисляем релативный адрес прыжка от трамплина до оригинальной функции.

*reinterpret_cast<uint8_t *>(
    reinterpret_cast<uint32_t>(pointer_on_gateway) +
    length_of_prologue) = 0xE9; // Записываем опкод прыжка, +1 байт после пролога в трамплине.

*reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_gateway) +
    length_of_prologue + 0x01) = relative_address; // Записываем релативный адрес прыжка на ориг. функцию, +2 байта после пролога.

unprotect_region protect_of_region(pointer_on_source, length_of_prologue); // Инициализируем класс, снимаем защиту памяти.
            
std::memset(pointer_on_source, 0x90, length_of_prologue); // Обнуляем весь пролог оригинальной функции.
*reinterpret_cast<uint8_t *>(pointer_on_source) = 0xE9; // Подменяем первый байт пролога на опкод прыжка.

relative_address =
    reinterpret_cast<uint32_t>(pointer_on_destination) -
    reinterpret_cast<uint32_t>(pointer_on_source) - 5; // Вычисляем релативный адрес прыжка от оригинальной функции на нашу.

*reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source) + 0x01) = relative_address; // Перезаписываем релативный адрес.

protect_of_region.~unprotect_region(); // Восстанавливаем протекцию региона.

В использовании:

C++:
Expand Collapse Copy
#include "hook/hook.h"

struct vec3d {
    float x, y, z;
};

using process_aim_t = void(__thiscall *)(void *cam_pointer, vec3d *position_of_camera,
    float *, float *, float *);
process_aim_t process_aim;

void __fastcall process_aim_hook(void *cam_pointer, void *not_used, vec3d *position_of_camera,
        float *_1, float *_2, float *_3) {
    process_aim(cam_pointer, position_of_camera, _1, _2, _3);
}

class guide_of_hooking {
public:
    hook *hooked_call_process_aim;

    guide_of_hooking() {
        hooked_call_process_aim = new hook(0x00521500, process_aim_hook, 13); // 13 - размер пролога.
        process_aim = hooked_call_process_aim->get_trampoline<process_aim_t>();
    }
    ~guide_of_hooking() {
        delete hooked_call_process_aim;
    }
} guide_of_hooking;

Если останутся вопросы, напишите в комментарии.
Исходный код на GitHub -
Пожалуйста, авторизуйтесь для просмотра ссылки.


Пара примеров:

C++:
Expand Collapse Copy
#include "hook/hook.h"

using timer_update_t = void(__cdecl *)();
timer_update_t timer_update =
    reinterpret_cast<timer_update_t>(0x00561B10); // void __cdecl CTimer__Update()
    

void timer_update_hook() {
    timer_update();
}

class guide_of_hooking {
public:
    hook *hooked_call_timer_update;

    guide_of_hooking() {
        // .text:0053E968 | 00C | call _ZN6CTimer6UpdateEv
        hooked_call_timer_update = new hook(0x0053E968, timer_update_hook,
            0, redirect_hook, call_method); // Размер пролога 0, потому что он здесь не требуется, мы подменяем CALL.
    }
    ~guide_of_hooking() {
        delete hooked_call_timer_update;
    }
} guide_of_hooking;

C++:
Expand Collapse Copy
#include "hook/hook.h"

using timer_update_t = void(__cdecl *)();
timer_update_t timer_update;
    
void timer_update_hook() {
    timer_update(); // Вызываем его.
}

class guide_of_hooking {
public:
    hook *hooked_call_timer_update;

    guide_of_hooking() {
        /*
            .text:00561B10 | 000 | mov ecx, _timerFunction
            .text:00561B16 | 000 | sub esp, 0Ch
        */
        hooked_call_timer_update = new hook(0x00561B10, timer_update_hook, 6);
        timer_update = hooked_call_timer_update->get_trampoline<timer_update_t>(); // Получаем трамплин.
    }
    ~guide_of_hooking() {
        delete hooked_call_timer_update;
    }
} guide_of_hooking;
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
спасибо теперь буду говорить что умею хукать перед однокамерниками!!!
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Кодеры разлогиньтесь! :SMOrc:
В последнее время я стал замечать, что абсолютное большинство использует всякие готовые библиотеки для хуков, и даже не заморачиваются о строении функций, типах хуков и прочим. Как таковых гайдов очень мало, а если и есть - на английском языке. В этой теме я расскажу обо всем просто и понятно.

Начнем с банального. Да кто такой все таки, этот ваш, хук?! Хук, с английского обозначает: "ловить". Назвали его так, потому что он ловит вызовы каких либо команд. Не забегая вперед, расскажу о основных командах, которые вы должны знать.
Абсолютно все процессы состоят из машинного кода. Это последовательность битов, но не бойтесь, с битами работать нам не потребуется (на начальных этапах). При помощи различных дизассемблеров (я использую IDA Pro), можно этот машинный код превратить в ассемблерный. Если вы собираетесь работать с хуками, вы не обойдетесь без IDA Pro и Cheat Engine, так что устанавливайте эти программы. Рассказывать, как с ними работать, я не буду, в интернете полно гайдов.
Нам предоставлен ассемблерный код программы, нам нужно знать две основные команды:
JMP - команда прыжка. Ее опкод - 0xE9. Выполняет прыжок с одного место, на другое. Если есть JMP на одно место, обязательно есть прыжок обратно, чтобы не прервать цикл игры.
CALL - команда вызова. Имет одинаковую инструкцию размером в 5 байт, как и JMP. Единственное, чем отличается, так это не обязательно прыгать обратно.
У этих команд одинаковые инструкции. Допустим, у нас есть адрес 0xFF00FF. Это адрес на инструкцию одной из этих команд. Если не прибавлять ничего, считывая 1 байт (uint8_t) с этого адреса мы получим как раз таки опкод одной из этих команд. По смещению +1 от адреса инструкции, мы получим релативный адрес, размером в 4 байта. Он отличается от обычного адреса, куда хочет прыгнуть или откуда хочет вызвать опкод. Он высчитывается по такой формуле: (куда_прыгаем или откуда_вызываем) - (откуда_прыгаем или где_вызываем) - 5.
Допустим, у нас есть такая штучка:

C++:
Expand Collapse Copy
.text:0053ECBD 004                 call    _Idle

Это вызов функции _Idle. Адрес функции _Idle - 0x0053E920. В данном случае, релативный адрес будет таким: 0x0053E920 - 0x0053ECBD - 5. Этот релативный адрес будет располагаться по адресу инструкции + 1. В данном случае, 0x0053ECBD + 1, и будет иметь размер 4 байта (uint32_t).
Получается, чтобы получить опкод, нам нужно выполнить такое чтение:

C++:
Expand Collapse Copy
uint8_t call_opcode = *reinterpret_cast<uint8_t *>(0x0053ECBD);

Чтобы получить релативный адрес, такое:

C++:
Expand Collapse Copy
uint32_t relative_address = *reinterpret_cast<uint32_t *>(0x0053ECBD + 1);

Я думаю, основную логику инструкций JMP и CALL вы поняли, расскажу про первый метод хука, как я его назвал, Redirect.
Суть хука заключается в том, чтобы подменить релативный адрес команды JMP/CALL на свой. Чтобы данное провернуть, нужно снять протекцию с региона функцией VirtualProtect, занопить всю инструкцию размером в 5 байт функцией memset (на всякий), подменить опкод на CALL/JMP (0xE8/0xE9), записав 1 байт, и подменить релативный адрес, который высчитать по формуле, которую я представил выше, и восстановить протекцию.

C++:
Expand Collapse Copy
unprotect_region protect_of_region(pointer_on_source, size_of_default_instruction); // Инициализируем класс unprotect_region. Снимаем защиту с региона памяти.
           
original_instructions.first = *reinterpret_cast<uint8_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source)); // Записываем в пару оригинальный опкод команды.
original_instructions.second = *reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source) + 0x01); // Записываем в пару оригинальный релативный адрес.

std::memset(pointer_on_source, no_operation_opcode, size_of_default_instruction); // Ноплю всю инструкцию JMP/CALL, её статичный размер для x86 - 5 байт.
*reinterpret_cast<uint8_t *>(pointer_on_source) = method_of_hook; // Меняем оригинальный опкод на опкод команды, которую мы указали в параметрах.

uint32_t relative_address =
    reinterpret_cast<uint32_t>(pointer_on_destination) -
    reinterpret_cast<uint32_t>(pointer_on_source) - 5; // Вычисляем релативный адрес прыжка от pointer_on_source до pointer_on_destination.
// Если говорить проще - прыгаем с оригинальной функции на нашу.

*reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source) + 0x01) = relative_address; // Перезаписываем релативный адрес на собственный.

protect_of_region.~unprotect_region(); // Вызываем деструктор класса, восстанавливаем оригинальный уровень протекции региона.

pointer_on_source - указатель на адрес инструкции JMP/CALL. pointer_on_destination - указатель на вашу функцию, на которую вы подменили вызов.

В вашей функции, вы должны вызвать оригинальную функцию, на которую был вызов.
В данном случае:

C++:
Expand Collapse Copy
//int __usercall Idle@<eax>(int a1@<ecx>, int a2@<edx>, int bp0@<ebp>, int a4@<edi>, int a5@<esi>, long double a6@<st0>, int a3)

using idle_t = int(__cdecl *)(int, int, int, int, int, long double, int);
idle_t idle = reinterpret_cast<idle_t>(0x0053E920);

int idle_hook(int a1, int a2, int bp0, int a4, int a5, long double a6, int a3) {
    return idle(a1, a2, bp0, a4, a5, a6, a3); // Вызываем оригинальную функцию.
}

Прошу заметить, в idle_hook я не указал __cdecl, только потому что в С++ функции по умолчанию имеют такое соглашение о вызовах. Если бы функция была __stdcall - вы бы записали __stdcall, если бы __thiscall, то пришлось бы поступить немного по-другому.
В idle_hook вы бы приписали соглашение о вызовах __fastcall, в idle_t - __thiscall, а в idle_hook первым параметром вы бы записали void *, и вторым тоже. В итоге у вас бы получилось:

C++:
Expand Collapse Copy
using idle_t = int(__thiscall *)(void *, int, int, int, int, int, long double, int);
idle_t idle = reinterpret_cast<idle_t>(0x0053E920);

int __fastcall idle_hook(void *_this, void *unused, int a1, int a2, int bp0, int a4, int a5, long double a6, int a3) {
    return idle(_this, a1, a2, bp0, a4, a5, a6, a3); // Вызываем оригинальную функцию.
}

Почему не __thiscall, спросите вы. А все потому, что компиляторы не дают статическим функциям иметь данное соглашение. __fastcall очень похож по строению пролога (поговорим чуть позже об этом) на __thiscall, только одна разница - во второй параметр добавляется еще один указатель, он не используется. Просто не трогайте его.

С Redirect хуками мы разобрались, теперь расскажу о Trampoline.
Trampoline от Redirect кардинально отличается. Если Redirect просто подменяет релативный адрес команды вызова или прыжка, то Trampoline взаимодействует с прологом функции.
Что такое пролог функции? Пролог функции - первые несколько байт функции, которые подготавливают стек, пушат регистры.
У пролога есть свой эпилог. Эпилог отличается тем, что он располагается в конце функции, и восстанавливает стек и регистры до того состояния, которое было до вызова.
Расскажу на примере функции void __cdecl CTimer__Update(void):

Пожалуйста, авторизуйтесь для просмотра ссылки.


Логика трамплин хука заключается в том, чтобы этот самый пролог сохранить в отдельную функцию, занопить весь пролог, поставить там прыжок на нашу функцию, в нашей функции вызвать ту, в которой мы сохранили пролог, и вдобавок приписать туда прыжок обратно.
Получается так: вместо пролога, jmp -> наша_функция -> jmp трамплин -> jmp обратно (+1, чтобы не было рекурсии).

C++:
Expand Collapse Copy
if (length_of_prologue < 5) { // РАЗМЕР ПРОЛОГА НЕ МОЖЕТ БЫТЬ МЕНЬШЕ 5! Запомните это раз и навсегда.
    return;
}

prologue_length = length_of_prologue;

/*
    Выделяем виртуальную память размером с размер пролога + 5.
    Представим ситуацию, мы хукаем функцию, у которой размер пролога 10,
    но мы выделяем 10 + 5, а не 10. Почему?
    Потому-что в первые 10 байт запишется пролог, а в остальные 5 байт
    запишется прыжок на оригинальную функцию, чтобы не сохранять в трамплин весь ее код.
*/
pointer_on_gateway = VirtualAlloc(nullptr, length_of_prologue + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(pointer_on_gateway, pointer_on_source, length_of_prologue); // Копируем байты пролога в указатель.

uint32_t relative_address =
    reinterpret_cast<uint32_t>(pointer_on_source) -
    reinterpret_cast<uint32_t>(pointer_on_gateway) - 5; // Вычисляем релативный адрес прыжка от трамплина до оригинальной функции.

*reinterpret_cast<uint8_t *>(
    reinterpret_cast<uint32_t>(pointer_on_gateway) +
    length_of_prologue) = 0xE9; // Записываем опкод прыжка, +1 байт после пролога в трамплине.

*reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_gateway) +
    length_of_prologue + 0x01) = relative_address; // Записываем релативный адрес прыжка на ориг. функцию, +2 байта после пролога.

unprotect_region protect_of_region(pointer_on_source, length_of_prologue); // Инициализируем класс, снимаем защиту памяти.
           
std::memset(pointer_on_source, 0x90, length_of_prologue); // Обнуляем весь пролог оригинальной функции.
*reinterpret_cast<uint8_t *>(pointer_on_source) = 0xE9; // Подменяем первый байт пролога на опкод прыжка.

relative_address =
    reinterpret_cast<uint32_t>(pointer_on_destination) -
    reinterpret_cast<uint32_t>(pointer_on_source) - 5; // Вычисляем релативный адрес прыжка от оригинальной функции на нашу.

*reinterpret_cast<uint32_t *>(
    reinterpret_cast<uint32_t>(pointer_on_source) + 0x01) = relative_address; // Перезаписываем релативный адрес.

protect_of_region.~unprotect_region(); // Восстанавливаем протекцию региона.

В использовании:

C++:
Expand Collapse Copy
#include "hook/hook.h"

struct vec3d {
    float x, y, z;
};

using process_aim_t = void(__thiscall *)(void *cam_pointer, vec3d *position_of_camera,
    float *, float *, float *);
process_aim_t process_aim;

void __fastcall process_aim_hook(void *cam_pointer, void *not_used, vec3d *position_of_camera,
        float *_1, float *_2, float *_3) {
    process_aim(cam_pointer, position_of_camera, _1, _2, _3);
}

class guide_of_hooking {
public:
    hook *hooked_call_process_aim;

    guide_of_hooking() {
        hooked_call_process_aim = new hook(0x00521500, process_aim_hook, 13); // 13 - размер пролога.
        process_aim = hooked_call_process_aim->get_trampoline<process_aim_t>();
    }
    ~guide_of_hooking() {
        delete hooked_call_process_aim;
    }
} guide_of_hooking;

Если останутся вопросы, напишите в комментарии.
Исходный код на GitHub -
Пожалуйста, авторизуйтесь для просмотра ссылки.


Пара примеров:

C++:
Expand Collapse Copy
#include "hook/hook.h"

using timer_update_t = void(__cdecl *)();
timer_update_t timer_update =
    reinterpret_cast<timer_update_t>(0x00561B10); // void __cdecl CTimer__Update()
   

void timer_update_hook() {
    timer_update();
}

class guide_of_hooking {
public:
    hook *hooked_call_timer_update;

    guide_of_hooking() {
        // .text:0053E968 | 00C | call _ZN6CTimer6UpdateEv
        hooked_call_timer_update = new hook(0x0053E968, timer_update_hook,
            0, redirect_hook, call_method); // Размер пролога 0, потому что он здесь не требуется, мы подменяем CALL.
    }
    ~guide_of_hooking() {
        delete hooked_call_timer_update;
    }
} guide_of_hooking;

C++:
Expand Collapse Copy
#include "hook/hook.h"

using timer_update_t = void(__cdecl *)();
timer_update_t timer_update;
   
void timer_update_hook() {
    timer_update(); // Вызываем его.
}

class guide_of_hooking {
public:
    hook *hooked_call_timer_update;

    guide_of_hooking() {
        /*
            .text:00561B10 | 000 | mov ecx, _timerFunction
            .text:00561B16 | 000 | sub esp, 0Ch
        */
        hooked_call_timer_update = new hook(0x00561B10, timer_update_hook, 6);
        timer_update = hooked_call_timer_update->get_trampoline<timer_update_t>(); // Получаем трамплин.
    }
    ~guide_of_hooking() {
        delete hooked_call_timer_update;
    }
} guide_of_hooking;
тебе че делать нехуй? какой пост подряд с гайдом
Да благославит абу этот пост.
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
жду дабл тап хук на кряк в2
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Спасибо за интересный гид.
 
Назад
Сверху Снизу