Неактуально беда с аси

nonelike

Известный
Автор темы
40
2
код че:
using CChat__AddEntry = void(__thiscall*)(void*, void*, int, const char*, const char*, unsigned long, unsigned long);
CChat__AddEntry pOriginalFunction = nullptr;

void* SetCallHook(uintptr_t HookAddress, void* DetourFunction) {
    uintptr_t OriginalFunction = *reinterpret_cast<uintptr_t*>(HookAddress + 1) + HookAddress + 5;
    DWORD oldProt;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), PAGE_READWRITE, &oldProt);
    *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), oldProt, &oldProt);
    return reinterpret_cast<void*>(OriginalFunction);
}

void __fastcall HOOK_AddEntry(void* pChat, void* EDX, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    pOriginalFunction(pChat, EDX, nType, szText, szPrefix, textColor, prefixColor);
}

if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
    pOriginalFunction = reinterpret_cast<CChat__AddEntry>(SetCallHook(dwSAMP + 0x67460, &HOOK_AddEntry));
}

пытаюсь хукнуть CChat__AddEntry, чтоб сменить текст samp started бла бла бла, но игра после запуска просто крашится
 

ARMOR

011110000111100101101001
Модератор
4,943
6,771
код че:
using CChat__AddEntry = void(__thiscall*)(void*, void*, int, const char*, const char*, unsigned long, unsigned long);
CChat__AddEntry pOriginalFunction = nullptr;

void* SetCallHook(uintptr_t HookAddress, void* DetourFunction) {
    uintptr_t OriginalFunction = *reinterpret_cast<uintptr_t*>(HookAddress + 1) + HookAddress + 5;
    DWORD oldProt;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), PAGE_READWRITE, &oldProt);
    *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), oldProt, &oldProt);
    return reinterpret_cast<void*>(OriginalFunction);
}

void __fastcall HOOK_AddEntry(void* pChat, void* EDX, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    pOriginalFunction(pChat, EDX, nType, szText, szPrefix, textColor, prefixColor);
}

if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
    pOriginalFunction = reinterpret_cast<CChat__AddEntry>(SetCallHook(dwSAMP + 0x67460, &HOOK_AddEntry));
}

пытаюсь хукнуть CChat__AddEntry, чтоб сменить текст samp started бла бла бла, но игра после запуска просто крашится
Потому что ты установил __thiscall но почему-то указываешь void* EDX. Или убери параметр EDX, или поставь соглашение о вызове __fastcall
 
  • Нравится
Реакции: nonelike

nonelike

Известный
Автор темы
40
2
Потому что ты установил __thiscall но почему-то указываешь void* EDX. Или убери параметр EDX, или поставь соглашение о вызове __fastcall
оба твои варианта не сработали, крашнуло
 

ARMOR

011110000111100101101001
Модератор
4,943
6,771
C++:
using CChat__AddEntry = void(__thiscall*)(void*, int, const char*, const char*, unsigned long, unsigned long);
CChat__AddEntry pOriginalFunction = nullptr;

void* SetCallHook(uintptr_t HookAddress, void* DetourFunction) {
    uintptr_t OriginalFunction = *reinterpret_cast<uintptr_t*>(HookAddress + 1) + HookAddress + 5;
    DWORD oldProt;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), PAGE_READWRITE, &oldProt);
    *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), oldProt, &oldProt);
    return reinterpret_cast<void*>(OriginalFunction);
}

void __fastcall HOOK_AddEntry(void* pChat, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    pOriginalFunction(pChat, nType, szText, szPrefix, textColor, prefixColor);
}

if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
    pOriginalFunction = reinterpret_cast<CChat__AddEntry>(SetCallHook(dwSAMP + 0x67460, &HOOK_AddEntry));
}

Если все равно крашит - проблема в адресе или функции для хука
 
  • Нравится
Реакции: nonelike

nonelike

Известный
Автор темы
40
2
C++:
using CChat__AddEntry = void(__thiscall*)(void*, int, const char*, const char*, unsigned long, unsigned long);
CChat__AddEntry pOriginalFunction = nullptr;

void* SetCallHook(uintptr_t HookAddress, void* DetourFunction) {
    uintptr_t OriginalFunction = *reinterpret_cast<uintptr_t*>(HookAddress + 1) + HookAddress + 5;
    DWORD oldProt;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), PAGE_READWRITE, &oldProt);
    *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), oldProt, &oldProt);
    return reinterpret_cast<void*>(OriginalFunction);
}

void __fastcall HOOK_AddEntry(void* pChat, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    pOriginalFunction(pChat, nType, szText, szPrefix, textColor, prefixColor);
}

if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
    pOriginalFunction = reinterpret_cast<CChat__AddEntry>(SetCallHook(dwSAMP + 0x67460, &HOOK_AddEntry));
}

Если все равно крашит - проблема в адресе или функции для хука
адрес брал с https://github.com/BlastHackNet/SAMP-API/blob/master/src/0.3.7-R3-1/CChat.cpp
1728411689397.png

также глянул в иде, адреса совпадают на р3
крашит до сих пор

плюс:
#include "plugin.h"

#include <samp.h>

using CChat__AddEntry = void(__thiscall*)(void*, int, const char*, const char*, unsigned long, unsigned long);
CChat__AddEntry pOriginalFunction = nullptr;

void* SetCallHook(uintptr_t HookAddress, void* DetourFunction) {
    uintptr_t OriginalFunction = *reinterpret_cast<uintptr_t*>(HookAddress + 1) + HookAddress + 5;
    DWORD oldProt;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), PAGE_READWRITE, &oldProt);
    *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
    VirtualProtect(reinterpret_cast<void*>(HookAddress + 1), sizeof(uintptr_t), oldProt, &oldProt);
    return reinterpret_cast<void*>(OriginalFunction);
}

void __fastcall HOOK_AddEntry(void* pChat, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    std::cout << "<" << std::string_view{ ((szPrefix) ? szPrefix : "") } << ">: " << std::string_view{ szText } << std::endl;
    pOriginalFunction(pChat, nType, szText, szPrefix, textColor, prefixColor);
}

void c_plugin::game_loop() {
    static bool initialized = false;

    if (initialized || !rakhook::initialize() || c_chat::get()->ref() == nullptr)
        return game_loop_hook.call_original();

    initialized = true;

    if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
        pOriginalFunction = reinterpret_cast<CChat__AddEntry>(SetCallHook(dwSAMP + 0x67460, &HOOK_AddEntry));
    }

    return game_loop_hook.call_original();
}

void c_plugin::attach_console() {
    if (!AllocConsole())
        return;

    FILE* f;
    freopen_s(&f, "CONOUT$", "w", stdout);
    freopen_s(&f, "CONOUT$", "w", stderr);
    freopen_s(&f, "CONIN$", "r", stdin);
}

c_plugin::c_plugin(HMODULE hmodule) : hmodule(hmodule)
{
    attach_console();
    game_loop_hook.add(&c_plugin::game_loop);
}


c_plugin::~c_plugin()
{
    rakhook::destroy();
}
 
Последнее редактирование:

fuflexxxx

Участник
28
12
У __fastcall'a в х86 архитектуре аргументы передаются в ecx, edx + стэк, тебе надо в хуке после объекта добавить ещё один аргумент любой(чтобы учесть edx). В оригинал соответственно edx передавать не надо, т.к у thiscall передача ecx + stack.
 
  • Нравится
Реакции: nonelike

nonelike

Известный
Автор темы
40
2
У __fastcall'a в х86 архитектуре аргументы передаются в ecx, edx + стэк, тебе надо в хуке после объекта добавить ещё один аргумент любой(чтобы учесть edx). В оригинал соответственно edx передавать не надо, т.к у thiscall передача ecx + stack.
C++:
void __fastcall HOOK_AddEntry(void* pChat, void* EDX, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    std::cout << "<" << std::string_view{ ((szPrefix) ? szPrefix : "") } << ">: " << std::string_view{ szText } << std::endl;
    pOriginalFunction(pChat, nType, szText, szPrefix, textColor, prefixColor);
}
внес правки, samp started бла бла бла прошло, а когда начала выбиваться следующая строка(коннект на сервер) крашнуло
 

nonelike

Известный
Автор темы
40
2
Закоментируй свой вывод в хуке и посмотри что будет. И попробуй отладчиком посмотреть, где крашит.
вывод в хуке не повлиял, крашнуло, щас попробовал дебагер прикрутить к аси, весь проект пошел по очку
 

Musaigen

abobusnik
Проверенный
1,608
1,365
Ты ставишь хук на пролог функции, а не на один из ее вызовов, да и по коду непонятно как у тебя вообще твоя функция вызывается, ибо ты даже не пишешь 0xE8 или 0xE9.

Второе, тебе нужен JMP hook (для него есть библиотека minhook например) в таком случае (если ты хукаешь пролог функции), иначе ты просто караптнешь стек и краш
 
  • Нравится
Реакции: nonelike

nonelike

Известный
Автор темы
40
2
Ты ставишь хук на пролог функции, а не на один из ее вызовов, да и по коду непонятно как у тебя вообще твоя функция вызывается, ибо ты даже не пишешь 0xE8 или 0xE9.

Второе, тебе нужен JMP hook (для него есть библиотека minhook например) в таком случае (если ты хукаешь пролог функции), иначе ты просто караптнешь стек и краш
в планах было ловить эту функцию, после считывать нужный текст который в неё приходит и редачить его под нужный мне (по аналогии samp started -> custom_text, connected to server -> custom text)
 

fuflexxxx

Участник
28
12
У тебя функция по реализации меняет rva(когда ты передаёшь адрес пролога функции), либо перепиши и измени оффсет, либо юзай такое или минхук

C++:
static void RedirectJump(DWORD address, void* foo)
{
    DWORD protect;
    VirtualProtect((LPVOID)address, 5, PAGE_EXECUTE_READWRITE, &protect);
    *reinterpret_cast<BYTE*>(address) = 0xE9;
    *reinterpret_cast<DWORD*>(address + 1) = reinterpret_cast<DWORD>(foo) - address - 5;
    VirtualProtect((LPVOID)address, 5, protect, &protect);
}

Только надо добавить сохранение указателя на ориг функцию, ибо код из инета взял для примера.
 
  • Нравится
Реакции: nonelike

nonelike

Известный
Автор темы
40
2
код:
#include "plugin.h"

#include <samp.h>

void* pOriginalFunction = nullptr;

void* SetJmpHook(uintptr_t HookAddress, size_t HookSize, void* DetourFunction) {
    void* Trampoline = VirtualAlloc(0, HookSize + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (Trampoline) {
        uintptr_t TrampolineJmpBack = reinterpret_cast<uintptr_t>(Trampoline) + HookSize;
        memcpy(Trampoline, reinterpret_cast<void*>(HookAddress), HookSize);

        DWORD oldProt;
        VirtualProtect(reinterpret_cast<void*>(HookAddress), HookSize, PAGE_READWRITE, &oldProt);
        memset(reinterpret_cast<void*>(HookAddress), 0x90, HookSize);
        *reinterpret_cast<unsigned char*>(HookAddress) = 0xE9;
        *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
        VirtualProtect(reinterpret_cast<void*>(HookAddress), HookSize, oldProt, &oldProt);

        *reinterpret_cast<unsigned char*>(TrampolineJmpBack) = 0xE9;
        *reinterpret_cast<uintptr_t*>(TrampolineJmpBack + 1) = (HookAddress + HookSize) - TrampolineJmpBack - 5;
        return Trampoline;
    }
    return nullptr;
}

void HOOK_AddChatMessage(void* pChat, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    std::cout << "<" << std::string_view{ ((szPrefix) ? szPrefix : "") } << ">: " << std::string_view{ szText } << std::endl;
}

void __declspec(naked) HOOK_Raw_AddChatMessage(void) {
    static void* pChat;
    static int nType;
    static const char* szText;
    static const char* szPrefix;
    static unsigned long textColor, prefixColor;
    __asm {
        mov eax, [esp + 0x04]
        mov nType, eax
        mov eax, [esp + 0x08]
        mov szText, eax
        mov eax, [esp + 0x0C]
        mov szPrefix, eax
        mov eax, [esp + 0x10]
        mov textColor, eax
        mov eax, [esp + 0x14]
        mov prefixColor, eax
        pushad
    }

    HOOK_AddChatMessage(pChat, nType, szText, szPrefix, textColor, prefixColor);

    __asm {
        popad
        jmp pOriginalFunction
    }
}

void c_plugin::game_loop() {
    static bool initialized = false;

    if (initialized || !rakhook::initialize() || c_chat::get()->ref() == nullptr)
        return game_loop_hook.call_original();

    if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
        SetJmpHook(dwSAMP + 0x67460, 5, &HOOK_Raw_AddChatMessage);
    }

    initialized = true;

    return game_loop_hook.call_original();
}

void c_plugin::attach_console() {
    if (!AllocConsole())
        return;

    FILE* f;
    freopen_s(&f, "CONOUT$", "w", stdout);
    freopen_s(&f, "CONOUT$", "w", stderr);
    freopen_s(&f, "CONIN$", "r", stdin);
}

c_plugin::c_plugin(HMODULE hmodule) : hmodule(hmodule)
{
    attach_console();
    game_loop_hook.add(&c_plugin::game_loop);
}


c_plugin::~c_plugin()
{
    rakhook::destroy();
}
че то все равно крашит
 

g305noobo

Известный
Модератор
305
477
код:
#include "plugin.h"

#include <samp.h>

void* pOriginalFunction = nullptr;

void* SetJmpHook(uintptr_t HookAddress, size_t HookSize, void* DetourFunction) {
    void* Trampoline = VirtualAlloc(0, HookSize + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (Trampoline) {
        uintptr_t TrampolineJmpBack = reinterpret_cast<uintptr_t>(Trampoline) + HookSize;
        memcpy(Trampoline, reinterpret_cast<void*>(HookAddress), HookSize);

        DWORD oldProt;
        VirtualProtect(reinterpret_cast<void*>(HookAddress), HookSize, PAGE_READWRITE, &oldProt);
        memset(reinterpret_cast<void*>(HookAddress), 0x90, HookSize);
        *reinterpret_cast<unsigned char*>(HookAddress) = 0xE9;
        *reinterpret_cast<uintptr_t*>(HookAddress + 1) = reinterpret_cast<uintptr_t>(DetourFunction) - HookAddress - 5;
        VirtualProtect(reinterpret_cast<void*>(HookAddress), HookSize, oldProt, &oldProt);

        *reinterpret_cast<unsigned char*>(TrampolineJmpBack) = 0xE9;
        *reinterpret_cast<uintptr_t*>(TrampolineJmpBack + 1) = (HookAddress + HookSize) - TrampolineJmpBack - 5;
        return Trampoline;
    }
    return nullptr;
}

void HOOK_AddChatMessage(void* pChat, int nType, const char* szText, const char* szPrefix, unsigned long textColor, unsigned long prefixColor) {
    std::cout << "<" << std::string_view{ ((szPrefix) ? szPrefix : "") } << ">: " << std::string_view{ szText } << std::endl;
}

void __declspec(naked) HOOK_Raw_AddChatMessage(void) {
    static void* pChat;
    static int nType;
    static const char* szText;
    static const char* szPrefix;
    static unsigned long textColor, prefixColor;
    __asm {
        mov eax, [esp + 0x04]
        mov nType, eax
        mov eax, [esp + 0x08]
        mov szText, eax
        mov eax, [esp + 0x0C]
        mov szPrefix, eax
        mov eax, [esp + 0x10]
        mov textColor, eax
        mov eax, [esp + 0x14]
        mov prefixColor, eax
        pushad
    }

    HOOK_AddChatMessage(pChat, nType, szText, szPrefix, textColor, prefixColor);

    __asm {
        popad
        jmp pOriginalFunction
    }
}

void c_plugin::game_loop() {
    static bool initialized = false;

    if (initialized || !rakhook::initialize() || c_chat::get()->ref() == nullptr)
        return game_loop_hook.call_original();

    if (uintptr_t dwSAMP = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll")); dwSAMP != 0) {
        SetJmpHook(dwSAMP + 0x67460, 5, &HOOK_Raw_AddChatMessage);
    }

    initialized = true;

    return game_loop_hook.call_original();
}

void c_plugin::attach_console() {
    if (!AllocConsole())
        return;

    FILE* f;
    freopen_s(&f, "CONOUT$", "w", stdout);
    freopen_s(&f, "CONOUT$", "w", stderr);
    freopen_s(&f, "CONIN$", "r", stdin);
}

c_plugin::c_plugin(HMODULE hmodule) : hmodule(hmodule)
{
    attach_console();
    game_loop_hook.add(&c_plugin::game_loop);
}


c_plugin::~c_plugin()
{
    rakhook::destroy();
}
че то все равно крашит
у тебя ж мой враппер MinHook в проекте o_O, используй его или напрямую саму библиотеку, зачем тебе это говно которое ты юзаешь с калхуками или даже тем, что скинули с сырым джамп хуком?

также учти, точно ли ты юзаешь верную версию сампа, ведь у тебя адрес для р3.
с моим проектом это должно выглядеть так(должно работать, не тестил):
C++:
// объявляю функцию
// объявление функции нужно, чтобы можно было использовать decltype для создания
// объекта хука, не указывая тип явно через using. это упрощает код, так как
// вывод типа происходит автоматически, однако, можно было бы и явно определить
// тип с помощью using
void __fastcall add_chat_entry(void* pChat, void* edx, int nType,
                               const char* szText, const char* szPrefix,
                               unsigned long textColor,
                               unsigned long prefixColor);

// создаю объект хука, в котором указываю тип функции, с помощью decltype
inline c_hook<decltype(&add_chat_entry)> add_chat_entry_hook{};

void __fastcall add_chat_entry(void* pChat, void* edx, int nType,
                               const char* szText, const char* szPrefix,
                               unsigned long textColor,
                               unsigned long prefixColor) {
  //вывод текста в чате
  std::printf("add_chat_entry: %s\n", szText);
  // вызов оригинала
  return add_chat_entry_hook.call_original(pChat, edx, nType, szText, szPrefix,
                                           textColor, prefixColor);
}

// edx - неиспользуемый параметр, так как функция использует __fastcall.
// этот регистр добавлен для совместимости с thiscall-функцией,
// поскольку __fastcall требует двух регистров (ecx и edx),
// что позволяет перехватывать вызовы thiscall функций
c_plugin::c_plugin(HMODULE hmodule) : _hmodule(hmodule) {
  attach_console();

  std::uintptr_t address_func_to_hook =
      (std::uintptr_t)GetModuleHandleA("samp.dll") + 0x67460;

  add_chat_entry_hook.set_adr(address_func_to_hook);
  add_chat_entry_hook.add(&add_chat_entry);
}

p.s лучше почитай про хуки, какие типы бывают и как работают, а также используй готовые решения, как например писал раньше - MinHook, это намного удобнее и круче
 
  • Нравится
Реакции: nonelike

nonelike

Известный
Автор темы
40
2
у тебя ж мой враппер MinHook в проекте o_O, используй его или напрямую саму библиотеку, зачем тебе это говно которое ты юзаешь с калхуками или даже тем, что скинули с сырым джамп хуком?

также учти, точно ли ты юзаешь верную версию сампа, ведь у тебя адрес для р3.
с моим проектом это должно выглядеть так(должно работать, не тестил):
C++:
// объявляю функцию
// объявление функции нужно, чтобы можно было использовать decltype для создания
// объекта хука, не указывая тип явно через using. это упрощает код, так как
// вывод типа происходит автоматически, однако, можно было бы и явно определить
// тип с помощью using
void __fastcall add_chat_entry(void* pChat, void* edx, int nType,
                               const char* szText, const char* szPrefix,
                               unsigned long textColor,
                               unsigned long prefixColor);

// создаю объект хука, в котором указываю тип функции, с помощью decltype
inline c_hook<decltype(&add_chat_entry)> add_chat_entry_hook{};

void __fastcall add_chat_entry(void* pChat, void* edx, int nType,
                               const char* szText, const char* szPrefix,
                               unsigned long textColor,
                               unsigned long prefixColor) {
  //вывод текста в чате
  std::printf("add_chat_entry: %s\n", szText);
  // вызов оригинала
  return add_chat_entry_hook.call_original(pChat, edx, nType, szText, szPrefix,
                                           textColor, prefixColor);
}

// edx - неиспользуемый параметр, так как функция использует __fastcall.
// этот регистр добавлен для совместимости с thiscall-функцией,
// поскольку __fastcall требует двух регистров (ecx и edx),
// что позволяет перехватывать вызовы thiscall функций
c_plugin::c_plugin(HMODULE hmodule) : _hmodule(hmodule) {
  attach_console();

  std::uintptr_t address_func_to_hook =
      (std::uintptr_t)GetModuleHandleA("samp.dll") + 0x67460;

  add_chat_entry_hook.set_adr(address_func_to_hook);
  add_chat_entry_hook.add(&add_chat_entry);
}

p.s лучше почитай про хуки, какие типы бывают и как работают, а также используй готовые решения, как например писал раньше - MinHook, это намного удобнее и круче
ща попробовал с твоим кодом, ваще нет сообщений в консоли (я про враппер в проекте забыл ваще, говно вытворял лютое)
C++:
#include "plugin.h"

#include <samp.h>

void __fastcall add_chat_entry(void* pChat, void* edx, int nType,
    const char* szText, const char* szPrefix,
    unsigned long textColor,
    unsigned long prefixColor);

inline c_hook<decltype(&add_chat_entry)> add_chat_entry_hook{};

void __fastcall add_chat_entry(void* pChat, void* edx, int nType,
    const char* szText, const char* szPrefix,
    unsigned long textColor,
    unsigned long prefixColor) {
    //вывод текста в чате
    std::printf("add_chat_entry: %s\n", szText);
    // вызов оригинала
    return add_chat_entry_hook.call_original(pChat, edx, nType, szText, szPrefix,
        textColor, prefixColor);
}

void c_plugin::game_loop() {
    static bool initialized = false;

    if (initialized || !rakhook::initialize() || c_chat::get()->ref() == nullptr)
        return game_loop_hook.call_original();

    initialized = true;

    return game_loop_hook.call_original();
}

void c_plugin::attach_console() {
    if (!AllocConsole())
        return;

    FILE* f;
    freopen_s(&f, "CONOUT$", "w", stdout);
    freopen_s(&f, "CONOUT$", "w", stderr);
    freopen_s(&f, "CONIN$", "r", stdin);
}

c_plugin::c_plugin(HMODULE hmodule) : hmodule(hmodule)
{
    attach_console();

    std::uintptr_t address_func_to_hook =
        (std::uintptr_t)GetModuleHandleA("samp.dll") + 0x67460;

    add_chat_entry_hook.set_adr(address_func_to_hook);
    add_chat_entry_hook.add(&add_chat_entry);

    game_loop_hook.add(&c_plugin::game_loop);
}

c_plugin::~c_plugin()
{
    rakhook::destroy();
}