Написал код для установки 5-и байтового jmp хука. Переводить в библиотеку не вижу смысла, так что нужно просто скопировать его.
upd:или вижу... если хочется вариант с либой - https://www.blast.hk/threads/55743/
Хуки при завершении скрипта сами снимутся
Lua:
--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    jit.off(callback, true) --off jit compilation | thx FYP
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end
--HOOKS
Что нужно для использования?
Знать FFI
Знать прототип функции
Знать адрес функции
Использование
Для примера установим хук на showDialog
Его прототип
C++:
void(__thiscall *)(void *this, uint16_t wID, uint8_t iStyle, char *szCaption, char *szText, char *szButton1, char *szButton2, bool bSend)
И его адрес
Lua:
getModuleHandle('samp.dll') + 0x6B9C0
Это все что нужно, для установки хука используем
Lua:
tableHook = hook.new(прототип, калбэк, адрес, размер) --Размер по умолчанию = 5
Эта функция устанавливает хук и возвращает таблицу с управлением над ним, ее методы
Lua:
tableHook.status --статус хука(true/false)
tableHook.start() -- устанавливает хук
tableHook.stop() -- снимает хук
res = tableHook.call(...) --вызывает оригинальную функцию, если не void - возвратит результат
res = tableHook(...) --снимает хук, вызывает оригинальную ф-ю и ставит обратно
--То есть tableHook(...) = stop() call(...) start()
Пишем код
Обязательно подключаем библиотеку ffi
Lua:
local ffi = require 'ffi'
Пишем колбэк для хука по прототипу
Lua:
--void(__thiscall *)(void *this, uint16_t wID, uint8_t iStyle, char *szCaption, char *szText, char *szButton1, char *szButton2, bool bSend)
function dialogHook(this, id, style, caption, text, button1, button2, send)
    print(id, style, ffi.string(caption), ffi.string(text), ffi.string(button1), ffi.string(button2), send) --выводим параметры с которыми была вызвана оригинальная функция
end
И ставим хук
Lua:
function main()
    dialogHook = hook.new('void(__thiscall *)(void *this, uint16_t wID, uint8_t iStyle, char *szCaption, char *szText, char *szButton1, char *szButton2, bool bSend)', dialogHook, getModuleHandle('samp.dll') + 0x6B9C0)
    --Узнать его статус можно так dialogHook.status
end
Все хук установлен и работает, но диалог не показывается. Чтобы он показался: нужно снять хук, вызвать оригинальную функцию и поставить обратно хук, для этого изменяем калбэк
Lua:
function dialogHook(this, id, style, caption, text, button1, button2, send)
    print(id, style, ffi.string(caption), ffi.string(text), ffi.string(button1), ffi.string(button2), send)
    dialogHook(this, id, style, caption, text, button1, button2, send)
    --Или
    --dialogHook.stop()
    --dialogHook.call(this, id, style, caption, text, button1, button2, send)
    --dialogHook.start()
end
Можем подменить заголовок диалога
Lua:
function dialogHook(this, id, style, caption, text, button1, button2, send)
    dialogHook(this, id, style, ffi.cast('char*', ffi.string(caption)..' | Hooked'), text, button1, button2, send) --К заголовку диалога будет дописываться " | Hooked"
end
Полный код

Lua:
local ffi = require 'ffi'
--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    jit.off(callback, true) --off jit compilation | thx FYP
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end
--HOOKS

function main()
    dialogHook = hook.new('void(__thiscall *)(void *this, uint16_t wID, uint8_t iStyle, char *szCaption, char *szText, char *szButton1, char *szButton2, bool bSend)', dialogHook, getModuleHandle('samp.dll') + 0x6B9C0)
end

function dialogHook(this, id, style, caption, text, button1, button2, send)
    dialogHook(this, id, style, ffi.cast('char*', ffi.string(caption)..' | Hooked'), text, button1, button2, send)
end
При таком хуке для вызова диалога можно использовать sampShowDialog(...) или dialogHook.call(...) (в этом случае нужно еще передать указатель this первым параметром, т.к. функция thiscall, если что, то это sampDialogInfoPtr)

И еще один пример по хук win api функции
Lua:
function main()
    local res, addr = getDynamicLibraryProcedure("MessageBoxA", getModuleHandle('user32.dll'))
    if not res then return end
    msgBoxHook = hook.new('int (__stdcall *)(void *w, const char *txt, const char *cap, int type)', msgBoxHook, addr)

    --Вызываем для теста msgbox
    ffi.cdef[[
        int MessageBoxA(void *w, const char *txt, const char *cap, int type);
    ]]
    ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0)
    --Или просто
    msgBoxHook.call(nil, "Hello world!", "Test", 0)
end

function msgBoxHook(w, txt, cap, type)
    print(w, ffi.string(txt), ffi.string(cap), type)
    return msgBoxHook(w, txt, cap, type) --Т.к. функцию в прототипе указано int, а не void, нужно вернуть результат
end
Последнее редактирование: 8 Дек 2021


используя этот пример перепиши мой код максимально близко к примеру

local ffi = require("ffi")


--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    jit.off(callback, true) --off jit compilation | thx FYP
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end
--HOOKS


local playerTagsDrawerCallPtr = getModuleHandle('samp.dll') + 0x70F96
print(string.format("Player Tags Drawer Call Ptr: 0x%X", playerTagsDrawerCallPtr))


-- Callback function with void* parameters
function drawLabelHook(pPosition, szText, color, fDistanceToCamera, bDrawStatus, nStatus)
	print('hey')
    ---- Cast void* back to appropriate types within the function
    --local position = ffi.cast("CVector*", pPosition)
    --local text = ffi.string(ffi.cast("const char*", szText))
	print"Hooked DrawLabel"
    --print(string.format("Hooked DrawLabel: Position=(%f, %f, %f) Text=%s", position.x, position.y, position.z, text))
    -- Original function invocation (if needed)
    -- drawLabelHook(pPosition, szText, color, fDistanceToCamera, bDrawStatus, nStatus)
end

-- Hook setup
function main()
	wait(500)
    local functionPrototype = 'void(__thiscall *)(void* pPosition, void* szText, unsigned long color, float fDistanceToCamera, bool bDrawStatus, int nStatus)'
    local drawLabelHook = hook.new(functionPrototype, drawLabelHook, playerTagsDrawerCallPtr)
end

-- Note: You need to call `main()` explicitly to set up the hook

сам hooks не присылай мне повторно