Исходник Гайд LuaJIT libtcc | Пишем на C в Lua

Всем прив. Сначала я о том о сём, а в самом низу как установить

Это очередной мини гайд который теперь требует не только хоть какого-то знания-понимания ffi, но теперь еще и C ☠️
Я думаю часто у кого была мысль вот бы С компилер в ffi.cdef, а не просто эти объявления функций
Недавно я опять об этом подумал и решил что хватит
По этому я подружил libtcc (Tiny C Compiler) и luajit. Теперь lua открылась новая суперсила 🙀

Что тут:
  1. Raw string literal. Используя asm вставки в clang и gcc я привык к raw string literal из С++11 потому что это удобно. Оказывается в стандарте С99 и С11 этого нет, ну и в tcc соотвественно этого тоже нет(странно ведь у него много расширений). Ужаснулся и добавил эту возможность. К сожалению опции -masm=intel для tcc нет, по этому только at&t синтаксис асемблера.
  2. __naked функции. tcc любит безопасность, по этому в каждой функции генерит пролог и эпилог для сохранения стека. В задачах когда нужна только asm вставка это немного мешает, по этому я добавил атрибут __naked для функций
  3. В стандартный набор библиотек добавлены хедеры для работы с d3d9, luajit, minhook, imgui(билд на основе https://github.com/dearimgui/dear_bindings)
  4. TCCState закрывается сам, этим занимается ffi.gc. Если в С коде определена функция void OnGarbageCollection(), то при ffi.gc она вызовется(это некий аналог OnScriptTerminate, atexit). В ситуациях когда надо чтобы С код продолжил жить после завершения lua скрипта, нужно отключить это поведение с помощью ffi.gc(s, nil)
Вся эта темка не расчитана на юниоров(как и мои прошлые с хуками), по этому каждую строчку я не разжовываю
Минимальная документация есть в libtcc.h
А полная документация по TCC и libtcc в интернете, ее много(даже chatgpt может помочь)

Я даю простой пример как работать с этим в мунлоадер

1. Поделиться данными из Lua в C. В данном примере функцией
Lua:
В С коде объявляем
extern void print(const char*);
И в Lua коде добавляем
s:add_symbol("print", ffi.cast("void(*)(const char*)", function(str)
    print("{00BABe}[C]:{FFFFFF}", ffi.string(str))
end))
2. Поделиться из С в Lua. В данном примере функцией
C++:
В С коде
int add(int a, int b) {
        print(R"(kek
            "lol"
          
            mda))");

        int result;
        asm("addl %1, %0" : "=r"(result) : "r"(a), "0"(b));
        return result;
    }
В Lua коде
local add = s:get_symbol("add", "int(*)(int, int)");
print("add = ", add, "add(22,11) =", add(22,11))
Полный пример вот:
Lua:
local tcc = require("libtcc")
local ffi = require("ffi")

--tcc.cdef хак чтобы vs code включил подсветку кода С
local prog = tcc.cdef[[
    #include <windows.h>
    #include <winuser.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <lua.h>
    #include <d3d9.h>

    extern void print(const char*);

    void __thiscall CChat__addEntry(intptr_t this, int type, const char* text, const char* prefix, uint32_t tcolor, uint32_t pcolor) {
        char buf[256];
        _snprintf(buf, sizeof(buf), "this 0x%X | type %d | text{BABE00} %s {FFFFFF} | prefix %s | tc %X | pc %X", this, type, text, prefix, tcolor, pcolor);
        print(buf);
    }

    void swimFixLogic(uintptr_t esp) {
        float pCTimer__ms_fTimeStep = *(float*)0xB7CB5C;
        float magic = 1.0f / (pCTimer__ms_fTimeStep / (50.0f / 30.0f));
        *(float*)(esp + 0x18) *= magic * 15;
        *(float*)(esp + 0x1C) *= magic * 15;
        *(float*)(esp + 0x20) *= magic * 15;
    }

    void __naked swimFix() {  
        asm(R"(
            push %eax
            push %ecx
            push %edx
            mov %esp, %eax
            add $0xC, %eax
            push %eax
            call swimFixLogic
            add $4, %esp
            pop %edx
            pop %ecx
            pop %eax
            fld    %st(1)
            fmuls  (%eax)
            fld    %st(2)
            jmp 0x0068A514
        )");
    }

    void installHook() {
        uintptr_t hook_addr = (uintptr_t)GetModuleHandle("samp.dll") + 0x67460;

        DWORD oldProt;
      
        VirtualProtect((void*)hook_addr, 5, PAGE_EXECUTE_READWRITE, &oldProt);
        *(uint8_t*)hook_addr = 0xE9;
        *(uintptr_t*)(hook_addr + 1) = (uintptr_t)&CChat__addEntry - hook_addr - 5;
        VirtualProtect((void*)hook_addr, 5, oldProt, NULL);
      
        hook_addr = 0x68A50E;
        VirtualProtect((void*)hook_addr, 6, PAGE_EXECUTE_READWRITE, &oldProt);
        *(uint8_t*)hook_addr = 0xE9;
        *(uintptr_t*)(hook_addr + 1) = (uintptr_t)&swimFix - hook_addr - 5;
        *(uint8_t*)(hook_addr + 5) = 0x90;
        VirtualProtect((void*)hook_addr, 6, oldProt, NULL);

        print("Хуки установлены");
    }

    int add(int a, int b) {
        print(R"(kek
            "lol"
          
            mda))");

        int result;
        asm("addl %1, %0" : "=r"(result) : "r"(a), "0"(b));
        return result;
    }
]]

local s = tcc.new()
s:compile_string(prog)

s:add_symbol("print", ffi.cast("void(*)(const char*)", function(str)
    print("{00BABe}[C]:{FFFFFF}", ffi.string(str))
end))

s:relocate()

local add = s:get_symbol("add", "int(*)(int, int)");
print("add = ", add, "add(22,11) =", add(22,11))

function main()
    while not isSampAvailable() do wait(0) end
    local installHook = s:get_symbol("installHook", "void(*)()")
    installHook()
    wait(-1)
end
Lua:
local tcc = require("libtcc")
local ffi = require("ffi")
--tcc.cdef хак чтобы vs code включил подсветку кода С
local prog = tcc.cdef[[
    #include <windows.h>
    #include <winuser.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <lua.h>
    #include <d3d9.h>
    #include <minhook.h>
    
    extern void print(const char*);
    void __thiscall CChat__addEntry(intptr_t this, int type, const char* text, const char* prefix, uint32_t tcolor, uint32_t pcolor) {
        char buf[256];
        _snprintf(buf, sizeof(buf), "this 0x%X | type %d | text{BABE00} %s {FFFFFF} | prefix %s | tc %X | pc %X", this, type, text, prefix, tcolor, pcolor);
        print(buf);
    }
    void swimFixLogic(uintptr_t esp) {
        float pCTimer__ms_fTimeStep = *(float*)0xB7CB5C;
        float magic = 1.0f / (pCTimer__ms_fTimeStep / (50.0f / 30.0f));
        *(float*)(esp + 0x18) *= magic * 15;
        *(float*)(esp + 0x1C) *= magic * 15;
        *(float*)(esp + 0x20) *= magic * 15;
    }
    void __naked swimFix() {   
        asm(R"(
            push %eax
            push %ecx
            push %edx
            mov %esp, %eax
            add $0xC, %eax
            push %eax
            call swimFixLogic
            add $4, %esp
            pop %edx
            pop %ecx
            pop %eax
            fld    %st(1)
            fmuls  (%eax)
            fld    %st(2)
            jmp 0x0068A514
        )");
    }
    
    int add(int a, int b) {
        int result;
        asm("addl %1, %0" : "=r"(result) : "r"(a), "0"(b));
        return result;
    }
    #include "dcimgui_helper.h"
    LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {   
        if (cImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam))
            return 1;
        
        return HELPER_IMGUI_WND_PROC(hWnd, uMsg, wParam, lParam);
    }
    HRESULT WINAPI myPresent(IDirect3DDevice9* pDevice, const RECT *pSourceRect, const RECT *pDestRect, HWND hdest, const RGNDATA *pDirtyRegion)
    {
        static bool inited = false;
        if (!inited) {
            Helper_ImGui_Init(pDevice, (void*)&WndProc);
            inited = true;
        }
        cImGui_ImplDX9_NewFrame();
        cImGui_ImplWin32_NewFrame();
        ImGui_NewFrame();
        
        {
            ImGui_Begin("ImGui_Begin", (bool*)NULL, 0);
            ImGui_TextUnformatted("Hello world");
            ImGui_End();
            ImGui_ShowDemoWindow(NULL);
        }
        
        ImGui_Render();
        cImGui_ImplDX9_RenderDrawData(ImGui_GetDrawData());
        return HELPER_IMGUI_PRESENT(pDevice, pSourceRect, pDestRect, hdest, pDirtyRegion);
    }
    HRESULT WINAPI myReset(IDirect3DDevice9* pDevice, D3DPRESENT_PARAMETERS *pPresentationParameters)
    {
        cImGui_ImplDX9_InvalidateDeviceObjects();
        return HELPER_IMGUI_RESET(pDevice, pPresentationParameters);
    }
    
    void Main() {
        if (MH_Initialize() != MH_OK)
            return;
        
        void* hook_addr = (void*)((uintptr_t)GetModuleHandle("samp.dll") + 0x67460);
        if (MH_CreateHook(hook_addr, &CChat__addEntry, NULL) != MH_OK)
            return;
        if (MH_EnableHook(hook_addr) != MH_OK)
            return;
        if (MH_CreateHook((void*)0x68A50E, &swimFix, NULL) != MH_OK)
            return;
        if (MH_EnableHook((void*)0x68A50E) != MH_OK)
            return;
        Helper_ImGui_Install_Present_Hook(myPresent, myReset);
        print("Хуки установлены");
    }
    void OnGarbageCollection() { 
        MH_DisableHook((void*)((uintptr_t)GetModuleHandle("samp.dll") + 0x67460));
        MH_DisableHook((void*)0x68A50E);
        
        Helper_ImGui_Destroy();
        MH_Uninitialize();
    }
]]
local s = tcc.new()
s:compile_string(prog)
s:add_symbol("print", ffi.cast("void(*)(const char*)", function(str)
    print("{00BABe}[C]:{FFFFFF}", ffi.string(str))
end))
s:relocate()
local add = s:get_symbol("add", "int(*)(int, int)");
print("add = ", add, "add(22,11) =", add(22,11))
function main()
    while not isSampAvailable() do wait(0) end
    local Main = s:get_symbol("Main", "void(*)()");
    if Main then
        Main()
    end
    wait(-1)
end

Пишем tcc.cdef (vs code тригерится на .cdef по этому я добавил функцию cdef, которая возвращает то что принимает)
До:
1720099750371.png

После:
1720099781952.png


Установка переместить папку libtcc из архива в папку /moonloader/lib/
 

Вложения

  • libtcc.zip
    1.1 MB · Просмотры: 32
Последнее редактирование:

RTD

Потужно
Автор темы
Модератор
401
474
Обновил до версии 1.1
- Если в С коде определена функция void OnGarbageCollection(), то при ffi.gc она вызовется(это некий аналог OnScriptTerminate, atexit)
- В стандартный набор добавлен minhook и imgui(билд на основе https://github.com/dearimgui/dear_bindings)
В теме добавлен пример с minhook и imgui

Добавленный имгуи находится в состоянии черновика который я забросил. Он работает, но не поддерживает мультиконтекст, то есть только один скрипт может им пользоваться. Кому не в падло можете попытаться доделать его
 
  • Нравится
Реакции: whyega52 и santov

Gorskin

🖕
Проверенный
1,350
1,199
Скрытое содержимое для пользователя(ей):

Привет я не силен в языках С, но мне очень нужно сделать hfist с взаимодействием на lua.
Что-то такое страшное получилось, но кроме фриза камеры и неверного определения cam mode я ничего не получил.

Lua:
local tcc = require("libtcc")
local ffi = require("ffi")

local prog = tcc.cdef[[
    #include <windows.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>

    uintptr_t hook_addr = 0x51565C;
    uintptr_t trampoline_addr;
    uint8_t original_bytes[8];
    BOOL hook_installed = FALSE;
    BOOL enabled = TRUE;

    extern void print(const char*);
    
    void handle_translation(uint32_t mode);

    DWORD WINAPI WeaponFixThread(LPVOID lpParam) {
        uint32_t* weapon_data = (uint32_t*)lpParam;
        Sleep(75);
        *(BOOL*)(*weapon_data + 0x84) = TRUE;
        Sleep(20);
        *(BOOL*)(*weapon_data + 0x84) = FALSE;
        return 0;
    }

    void handle_translation(uint32_t mode) {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "Mode: %u", mode);
        print(buffer);  // Выводим текущий режим
    
        static uint16_t prev_mode = 0;
        if (!enabled) {
            prev_mode = mode;
            return;
        }
    
        if (prev_mode == 53 && mode != 53) {
            uint32_t* ped = (uint32_t*)0xB6F5F0;
            if (*ped) {
                uint32_t* weapon_data = (uint32_t*)(*ped + 0x480);
                Sleep(75);
                *(BOOL*)(*weapon_data + 0x84) = TRUE;
                Sleep(20);
                *(BOOL*)(*weapon_data + 0x84) = FALSE;
                print("HeavyFist triggered");
            }
        }
        prev_mode = mode;
    }

    void __stdcall camera_translation_hook(uint32_t mode) {
        handle_translation(mode);
    }

    void installHeavyFistHook() {
        if (hook_installed) return;

        DWORD oldProt;
        VirtualProtect((void*)hook_addr, 8, PAGE_EXECUTE_READWRITE, &oldProt);
        memcpy(original_bytes, (void*)hook_addr, 8);

        trampoline_addr = hook_addr + 8;
        
        uint8_t jump_code[5] = { 0xE9 };
        *(uintptr_t*)(jump_code + 1) = (uintptr_t)&camera_translation_hook - hook_addr - 5;
        memcpy((void*)hook_addr, jump_code, 5);

        VirtualProtect((void*)hook_addr, 8, oldProt, NULL);
        hook_installed = TRUE;
        print("Heavy Fist Hook Installed");
    }

    void removeHeavyFistHook() {
        if (!hook_installed) return;

        DWORD oldProt;
        VirtualProtect((void*)hook_addr, 8, PAGE_EXECUTE_READWRITE, &oldProt);
        memcpy((void*)hook_addr, original_bytes, 8);
        VirtualProtect((void*)hook_addr, 8, oldProt, NULL);

        hook_installed = FALSE;
        print("Heavy Fist Hook Removed");
    }

    void toggleHeavyFist() {
        enabled = !enabled;
        if (enabled)
            print("Хевифист включён");
        else
            print("Хевифист выключен");
    }
]]

local s = tcc.new()
s:compile_string(prog)

s:add_symbol("print", ffi.cast("void(*)(const char*)", function(str)
    print("{00BABe}[C]:{FFFFFF}", ffi.string(str))
end))

s:relocate()

local installHook = s:get_symbol("installHeavyFistHook", "void(*)()")
local removeHook = s:get_symbol("removeHeavyFistHook", "void(*)()")
local toggleHook = s:get_symbol("toggleHeavyFist", "void(*)()")

function main()
    while not isSampAvailable() do wait(0) end

    sampRegisterChatCommand("hfist", function()
        toggleHook()
    end)

    installHook()
    wait(-1)
end

function onScriptTerminate(s)
    if s == thisScript() then
        removeHook()
    end
end
А вот то что я хотел сделать
Lua:
local ffi = require "ffi"

ffi.cdef[[
int __stdcall VirtualProtect(void* address, size_t size, unsigned long prot, unsigned long* prev_prot);
void* __stdcall VirtualAlloc(void* address, size_t size, unsigned long alloc_type, unsigned long prot);
int __stdcall VirtualFree(void* address, size_t size, unsigned long free_type);
]]
local Kernel32 = ffi.load("Kernel32.dll")

local installHeavyFistHooks, removeHeavyFistHooks

local enabled = true

local CFG = {}
CFG.HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX = 19
CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE = 8
CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX = 34
CFG.HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX = CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX + CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE + 1
CFG.HEAVYFIST_NOSCROLL_HOOK_DATA = "\x50\x53\x51\x52\x56\x57\x66\x9c\x01\xf0\x05\x80\x01\x00\x00\x66\x55\x50\xe8\x01\x01\x01\x01\x83\xc4\x06\x66\x9d\x5f\x5e\x5a\x59\x5b\x58" .. ("\x01"):rep(CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE) .. "\xe9\x01\x01\x01\x01"

function main()
  while not isSampAvailable() do wait(0) end

  sampRegisterChatCommand("hfist", function()
    enabled = not enabled
    if enabled then
      sampAddChatMessage("Хевифист включён", -1)
    else
      sampAddChatMessage("Хевифист выключен", -1)
    end
  end)

  installHeavyFistHooks()

  wait(-1)
end

function onScriptTerminate(s)
  if s == thisScript() then
    removeHeavyFistHooks()
  end
end

do
  local hook_installed = false
  local hook_info = {
    helper_address = 0,
    original_data = 0,
    hooker = nil
  }

  local prev_mode = 0
  local function camera_translation_hook(_, mode)
    if not enabled then
      prev_mode = mode
      return
    end

    if prev_mode == 53 and mode ~= 53 then
        local ped = ffi.cast("uint32_t*", 0xB6F5F0)[0]
        if ped == 0 then
          prev_mode = mode
          return
        end

        local weapon_data = ffi.cast("uint32_t*", ped + 0x480)[0]

        lua_thread.create(function()
          wait(75)
          ffi.cast("bool*", weapon_data+0x84)[0] = true
          wait(20)
          ffi.cast("bool*", weapon_data+0x84)[0] = false
        end)
    end
    prev_mode = mode
  end

  installHeavyFistHooks = function()
    if hook_installed then
      return
    end

    hook_info.helper_address = Kernel32.VirtualAlloc(nil, #CFG.HEAVYFIST_NOSCROLL_HOOK_DATA, 0x00001000, 0x40)
    ffi.copy(hook_info.helper_address, CFG.HEAVYFIST_NOSCROLL_HOOK_DATA)
    local helper_address_number = tonumber(ffi.cast("uintptr_t", hook_info.helper_address))
    hook_info.original_data = ffi.new("uint8_t[?]", CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    ffi.copy(hook_info.original_data, ffi.cast("const void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    jit.off(camera_translation_hook)
    hook_info.hooker = ffi.cast("void(__cdecl*)(unsigned long, unsigned short)", camera_translation_hook)
    local hooker_number = tonumber(ffi.cast("uintptr_t", hook_info.hooker))
    ffi.cast("uint32_t*", helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX)[0] = hooker_number - (helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX - 1) - 5
    ffi.cast("uint32_t*", helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX)[0] = (0x51565C + CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE) - (helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX - 1) - 5
    ffi.copy(ffi.cast("void*", helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX), ffi.cast("const void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    local old_prot = ffi.new("unsigned long[1]")
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, 0x40, old_prot)
    ffi.fill(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, 0x90)
    ffi.cast("uint8_t*", 0x51565C)[0] = 0xE9
    ffi.cast("uint32_t*", 0x51565C + 1)[0] = helper_address_number - 0x51565C - 5
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, old_prot[0], old_prot)

    hook_installed = true
  end

  removeHeavyFistHooks = function()
    if not hook_installed then
      return
    end

    local old_prot = ffi.new("unsigned long[1]")
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, 0x40, old_prot)
    ffi.copy(ffi.cast("void*", 0x51565C), hook_info.original_data, CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, old_prot[0], old_prot)
    Kernel32.VirtualFree(hook_info.helper_address, #CFG.HEAVYFIST_NOSCROLL_HOOK_DATA, 0x00004000)
    hook_info.hooker:free()

    hook_installed = false
  end
end
 

RTD

Потужно
Автор темы
Модератор
401
474
*** Скрытый текст не может быть процитирован. ***
Привет я не силен в языках С, но мне очень нужно сделать hfist с взаимодействием на lua.
Что-то такое страшное получилось, но кроме фриза камеры и неверного определения cam mode я ничего не получил.

Lua:
local tcc = require("libtcc")
local ffi = require("ffi")

local prog = tcc.cdef[[
    #include <windows.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>

    uintptr_t hook_addr = 0x51565C;
    uintptr_t trampoline_addr;
    uint8_t original_bytes[8];
    BOOL hook_installed = FALSE;
    BOOL enabled = TRUE;

    extern void print(const char*);
   
    void handle_translation(uint32_t mode);

    DWORD WINAPI WeaponFixThread(LPVOID lpParam) {
        uint32_t* weapon_data = (uint32_t*)lpParam;
        Sleep(75);
        *(BOOL*)(*weapon_data + 0x84) = TRUE;
        Sleep(20);
        *(BOOL*)(*weapon_data + 0x84) = FALSE;
        return 0;
    }

    void handle_translation(uint32_t mode) {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "Mode: %u", mode);
        print(buffer);  // Выводим текущий режим
   
        static uint16_t prev_mode = 0;
        if (!enabled) {
            prev_mode = mode;
            return;
        }
   
        if (prev_mode == 53 && mode != 53) {
            uint32_t* ped = (uint32_t*)0xB6F5F0;
            if (*ped) {
                uint32_t* weapon_data = (uint32_t*)(*ped + 0x480);
                Sleep(75);
                *(BOOL*)(*weapon_data + 0x84) = TRUE;
                Sleep(20);
                *(BOOL*)(*weapon_data + 0x84) = FALSE;
                print("HeavyFist triggered");
            }
        }
        prev_mode = mode;
    }

    void __stdcall camera_translation_hook(uint32_t mode) {
        handle_translation(mode);
    }

    void installHeavyFistHook() {
        if (hook_installed) return;

        DWORD oldProt;
        VirtualProtect((void*)hook_addr, 8, PAGE_EXECUTE_READWRITE, &oldProt);
        memcpy(original_bytes, (void*)hook_addr, 8);

        trampoline_addr = hook_addr + 8;
       
        uint8_t jump_code[5] = { 0xE9 };
        *(uintptr_t*)(jump_code + 1) = (uintptr_t)&camera_translation_hook - hook_addr - 5;
        memcpy((void*)hook_addr, jump_code, 5);

        VirtualProtect((void*)hook_addr, 8, oldProt, NULL);
        hook_installed = TRUE;
        print("Heavy Fist Hook Installed");
    }

    void removeHeavyFistHook() {
        if (!hook_installed) return;

        DWORD oldProt;
        VirtualProtect((void*)hook_addr, 8, PAGE_EXECUTE_READWRITE, &oldProt);
        memcpy((void*)hook_addr, original_bytes, 8);
        VirtualProtect((void*)hook_addr, 8, oldProt, NULL);

        hook_installed = FALSE;
        print("Heavy Fist Hook Removed");
    }

    void toggleHeavyFist() {
        enabled = !enabled;
        if (enabled)
            print("Хевифист включён");
        else
            print("Хевифист выключен");
    }
]]

local s = tcc.new()
s:compile_string(prog)

s:add_symbol("print", ffi.cast("void(*)(const char*)", function(str)
    print("{00BABe}[C]:{FFFFFF}", ffi.string(str))
end))

s:relocate()

local installHook = s:get_symbol("installHeavyFistHook", "void(*)()")
local removeHook = s:get_symbol("removeHeavyFistHook", "void(*)()")
local toggleHook = s:get_symbol("toggleHeavyFist", "void(*)()")

function main()
    while not isSampAvailable() do wait(0) end

    sampRegisterChatCommand("hfist", function()
        toggleHook()
    end)

    installHook()
    wait(-1)
end

function onScriptTerminate(s)
    if s == thisScript() then
        removeHook()
    end
end
А вот то что я хотел сделать
Lua:
local ffi = require "ffi"

ffi.cdef[[
int __stdcall VirtualProtect(void* address, size_t size, unsigned long prot, unsigned long* prev_prot);
void* __stdcall VirtualAlloc(void* address, size_t size, unsigned long alloc_type, unsigned long prot);
int __stdcall VirtualFree(void* address, size_t size, unsigned long free_type);
]]
local Kernel32 = ffi.load("Kernel32.dll")

local installHeavyFistHooks, removeHeavyFistHooks

local enabled = true

local CFG = {}
CFG.HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX = 19
CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE = 8
CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX = 34
CFG.HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX = CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX + CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE + 1
CFG.HEAVYFIST_NOSCROLL_HOOK_DATA = "\x50\x53\x51\x52\x56\x57\x66\x9c\x01\xf0\x05\x80\x01\x00\x00\x66\x55\x50\xe8\x01\x01\x01\x01\x83\xc4\x06\x66\x9d\x5f\x5e\x5a\x59\x5b\x58" .. ("\x01"):rep(CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE) .. "\xe9\x01\x01\x01\x01"

function main()
  while not isSampAvailable() do wait(0) end

  sampRegisterChatCommand("hfist", function()
    enabled = not enabled
    if enabled then
      sampAddChatMessage("Хевифист включён", -1)
    else
      sampAddChatMessage("Хевифист выключен", -1)
    end
  end)

  installHeavyFistHooks()

  wait(-1)
end

function onScriptTerminate(s)
  if s == thisScript() then
    removeHeavyFistHooks()
  end
end

do
  local hook_installed = false
  local hook_info = {
    helper_address = 0,
    original_data = 0,
    hooker = nil
  }

  local prev_mode = 0
  local function camera_translation_hook(_, mode)
    if not enabled then
      prev_mode = mode
      return
    end

    if prev_mode == 53 and mode ~= 53 then
        local ped = ffi.cast("uint32_t*", 0xB6F5F0)[0]
        if ped == 0 then
          prev_mode = mode
          return
        end

        local weapon_data = ffi.cast("uint32_t*", ped + 0x480)[0]

        lua_thread.create(function()
          wait(75)
          ffi.cast("bool*", weapon_data+0x84)[0] = true
          wait(20)
          ffi.cast("bool*", weapon_data+0x84)[0] = false
        end)
    end
    prev_mode = mode
  end

  installHeavyFistHooks = function()
    if hook_installed then
      return
    end

    hook_info.helper_address = Kernel32.VirtualAlloc(nil, #CFG.HEAVYFIST_NOSCROLL_HOOK_DATA, 0x00001000, 0x40)
    ffi.copy(hook_info.helper_address, CFG.HEAVYFIST_NOSCROLL_HOOK_DATA)
    local helper_address_number = tonumber(ffi.cast("uintptr_t", hook_info.helper_address))
    hook_info.original_data = ffi.new("uint8_t[?]", CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    ffi.copy(hook_info.original_data, ffi.cast("const void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    jit.off(camera_translation_hook)
    hook_info.hooker = ffi.cast("void(__cdecl*)(unsigned long, unsigned short)", camera_translation_hook)
    local hooker_number = tonumber(ffi.cast("uintptr_t", hook_info.hooker))
    ffi.cast("uint32_t*", helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX)[0] = hooker_number - (helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX - 1) - 5
    ffi.cast("uint32_t*", helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX)[0] = (0x51565C + CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE) - (helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX - 1) - 5
    ffi.copy(ffi.cast("void*", helper_address_number + CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX), ffi.cast("const void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    local old_prot = ffi.new("unsigned long[1]")
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, 0x40, old_prot)
    ffi.fill(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, 0x90)
    ffi.cast("uint8_t*", 0x51565C)[0] = 0xE9
    ffi.cast("uint32_t*", 0x51565C + 1)[0] = helper_address_number - 0x51565C - 5
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, old_prot[0], old_prot)

    hook_installed = true
  end

  removeHeavyFistHooks = function()
    if not hook_installed then
      return
    end

    local old_prot = ffi.new("unsigned long[1]")
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, 0x40, old_prot)
    ffi.copy(ffi.cast("void*", 0x51565C), hook_info.original_data, CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE)
    Kernel32.VirtualProtect(ffi.cast("void*", 0x51565C), CFG.HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, old_prot[0], old_prot)
    Kernel32.VirtualFree(hook_info.helper_address, #CFG.HEAVYFIST_NOSCROLL_HOOK_DATA, 0x00004000)
    hook_info.hooker:free()

    hook_installed = false
  end
end
Я хз что это, не шарю в гето штуках и как их тестить. Ну чатжпт так перевел (он спамит потоками в хуке я хз это так надо или нет, мб надо попросить его чтобы он использовал один WorkerThread)
Lua:
local tcc = require("libtcc")
assert(tcc.VERSION_NUM >= 2, "Update libtcc")
local ffi = require("ffi")
local prog = tcc.cdef[[
    extern void print(const char*);
    // heavyfist_hook.c
// Компиляция под Windows (Tiny C Compiler, MSVC, MinGW и т.п.)
#include <windows.h>
#include <stdint.h>
#include <string.h>
// --- Конфигурация (аналог CFG из Lua) ---
#define HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX      19
#define HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE          8
#define HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX         34
#define HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX  (HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX + HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE + 1)
// hookData состоит из:
// - 34 байта: фиксированный префикс,
// - 8 байт: заполненные 0x01,
// - 5 байт: суффикс.
unsigned char hookData[] = {
    0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x66, 0x9C, 0x01, 0xF0, 0x05, 0x80, 0x01, 0x00, 0x00, 0x66,
    0x55, 0x50, 0xE8, 0x01, 0x01, 0x01, 0x01, 0x83, 0xC4, 0x06, 0x66, 0x9D, 0x5F, 0x5E, 0x5A, 0x59,
    0x5B, 0x58,
    // 8 байт 0x01:
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0xE9, 0x01, 0x01, 0x01, 0x01
};
const size_t hookDataSize = sizeof(hookData);
// Глобальные переменные для управления хуком
_Bool  enabled = 1;      // переключатель "HeavyFist" (аналог enabled в Lua)
unsigned short prev_mode = 0; // для отслеживания предыдущего режима
// Тип функции-хука (подобно ffi.cast("void(__cdecl*)(unsigned long, unsigned short)")
typedef void (__cdecl *CameraTranslationHook_t)(unsigned long, unsigned short);
// Структура для хранения информации о хуке
struct HookInfo {
    void *helper_address;    // адрес выделенной памяти для "helper"
    unsigned char original_data[HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE]; // оригинальные байты
    CameraTranslationHook_t hooker; // указатель на функцию-хук
};
static int hook_installed = 0;
static struct HookInfo hook_info = {0};
// ===================================================================
// Функция, выполняющая задержку и изменение значения в weapon_data+0x84
DWORD WINAPI HookThread(LPVOID lpParameter) {
    // lpParameter – указатель на weapon_data
    unsigned char *weapon_data = (unsigned char *)lpParameter;
    Sleep(75);  // ожидание 75 мс
    weapon_data[0x84] = 1;  // установить значение true
    Sleep(20);  // ожидание 20 мс
    weapon_data[0x84] = 0;  // вернуть false
    return 0;
}
// ===================================================================
// Функция-хук, вызываемая вместо оригинальной camera translation
void __cdecl camera_translation_hook(unsigned long arg, unsigned short mode) {
    if (!enabled) {
        prev_mode = mode;
        return;
    }
    print("kek");
    if (prev_mode == 53 && mode != 53) {
        // Получаем указатель на пед по адресу 0xB6F5F0
        uint32_t *ped_ptr = (uint32_t *)0xB6F5F0;
        if (*ped_ptr == 0) {
            prev_mode = mode;
            return;
        }
        // weapon_data находится по смещению 0x480 от ped
        unsigned char *weapon_data = (unsigned char *)(*ped_ptr + 0x480);
        // Создаём поток, который выполнит задержку и переключение флага
        CreateThread(NULL, 0, HookThread, weapon_data, 0, NULL);
    }
    prev_mode = mode;
}
// ===================================================================
// Функция установки хуков (аналог installHeavyFistHooks в Lua)
void installHeavyFistHooks() {
    if (hook_installed)
        return;
    
    // Выделяем исполняемую память для helper-кода
    void *helper_address = VirtualAlloc(NULL, hookDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!helper_address)
        return;
    
    memcpy(helper_address, hookData, hookDataSize);
    hook_info.helper_address = helper_address;
    
    // Сохраняем оригинальные байты по адресу, который будем перезаписывать (0x51565C)
    void *target_address = (void *)0x51565C;
    memcpy(hook_info.original_data, target_address, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE);
    
    // Отключение JIT для camera_translation_hook не требуется в C
    hook_info.hooker = camera_translation_hook;
    
    uintptr_t helper_addr_num = (uintptr_t)helper_address;
    uintptr_t hooker_num = (uintptr_t)hook_info.hooker;
    
    // Записываем в helper-код относительный адрес для вызова нашей функции-хука.
    // Смещение вычисляется по формуле:
    //   relative_offset = hooker_num - (helper_addr_num + FN_ADDRESS_INDEX - 1) - 5;
    uint32_t *fn_addr_ptr = (uint32_t *)(helper_addr_num + HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX);
    *fn_addr_ptr = (uint32_t)(hooker_num - (helper_addr_num + HEAVYFIST_NOSCROLL_HOOK_FN_ADDRESS_INDEX - 1) - 5);
    
    // Записываем относительный адрес для прыжка назад (jump back).
    uint32_t *jump_back_ptr = (uint32_t *)(helper_addr_num + HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX);
    *jump_back_ptr = (uint32_t)((0x51565C + HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE) - (helper_addr_num + HEAVYFIST_NOSCROLL_HOOK_JUMP_BACK_ADDRESS_INDEX - 1) - 5);
    
    // Копируем оригинальные инструкции из целевого адреса в helper-код (начиная с ORIGINAL_INDEX)
    memcpy((void *)(helper_addr_num + HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_INDEX), target_address, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE);
    
    // Переписываем код по адресу 0x51565C, чтобы сделать JMP на helper-код.
    DWORD oldProtect;
    if (VirtualProtect(target_address, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect)) {
        // Заполняем область NOP-ами (0x90)
        memset(target_address, 0x90, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE);
        // Записываем JMP:
        // 0xE9 + 4-байтовое смещение
        unsigned char *patch = (unsigned char *)target_address;
        patch[0] = 0xE9;
        *(uint32_t *)(patch + 1) = (uint32_t)((helper_addr_num - (uintptr_t)target_address) - 5);
        // Восстанавливаем оригинальную защиту памяти
        VirtualProtect(target_address, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, oldProtect, &oldProtect);
    }
    
    hook_installed = 1;
}
// ===================================================================
// Функция удаления хуков (аналог removeHeavyFistHooks в Lua)
void removeHeavyFistHooks() {
    if (!hook_installed)
        return;
    
    void *target_address = (void *)0x51565C;
    DWORD oldProtect;
    if (VirtualProtect(target_address, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect)) {
        // Восстанавливаем оригинальные байты
        memcpy(target_address, hook_info.original_data, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE);
        VirtualProtect(target_address, HEAVYFIST_NOSCROLL_HOOK_ORIGINAL_SIZE, oldProtect, &oldProtect);
    }
    VirtualFree(hook_info.helper_address, 0, MEM_RELEASE);
    
    hook_installed = 0;
}
    
    void Main() {
        installHeavyFistHooks();
    }
    void OnGarbageCollection() {
        removeHeavyFistHooks();   
    }
]]
local s = tcc.new()
s:compile_string(prog)
s:add_symbol("print", ffi.cast("void(*)(const char*)", function(str)
    print("{00BABe}[C]:{FFFFFF}", ffi.string(str))
end))
s:relocate()
function main()
    while not isSampAvailable() do wait(0) end
    local Main = s:get_symbol("Main", "void(*)()");
    if Main then
        Main()
    end
    sampRegisterChatCommand("hfist", function()
        local enabled = s:get_symbol("enabled", "bool*")
        enabled[0] = not enabled[0];
        print(enabled[0])
    end)
    wait(-1)
end