local ffi = require("ffi")
ffi.cdef[[
typedef int BOOL;
typedef void* LPVOID;
typedef unsigned long DWORD;
typedef DWORD* PDWORD;
typedef unsigned long ULONG_PTR;
typedef ULONG_PTR SIZE_T;
BOOL VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
]]
local function reinterpret_cast(addr, cdecl_type)
return ffi.cast(cdecl_type, tonumber(ffi.cast("intptr_t", addr)))
end
local get_pattern = {
GetModuleHandlePtr = ffi.cast("void***", ffi.cast("uint32_t", client.find_signature("engine.dll", "\xFF\x15\xCC\xCC\xCC\xCC\x85\xC0\x74\x0B")) + 2)[0][0],
GetProcAddressPtr = ffi.cast("void***", ffi.cast("uint32_t", client.find_signature("engine.dll", "\xFF\x15\xCC\xCC\xCC\xCC\xA3\xCC\xCC\xCC\xCC\xEB\x05")) + 2)[0][0],
}
get_pattern.fnGetModuleHandle = reinterpret_cast(get_pattern.GetModuleHandlePtr, "void*(__cdecl*)(void*, const char*)")
get_pattern.fnGetProcAddress = reinterpret_cast(get_pattern.GetProcAddressPtr, "void*(__cdecl*)(void*, void*, const char*)")
get_pattern.lib = {
kernel32 = get_pattern.fnGetModuleHandle(nil, "kernel32.dll")
}
get_pattern.export = {
kernel32 = {
VirtualProtect = reinterpret_cast(
get_pattern.fnGetProcAddress(nil, get_pattern.lib.kernel32, "VirtualProtect"),
"BOOL(__stdcall*)(LPVOID, SIZE_T, DWORD, PDWORD)"
),
VirtualAlloc = reinterpret_cast(
get_pattern.fnGetProcAddress(nil, get_pattern.lib.kernel32, "VirtualAlloc"),
"LPVOID(__stdcall*)(LPVOID, SIZE_T, DWORD, DWORD)"
)
}
}
local hook = { all = {} }
do
hook.new = function(vtable)
local chook = {
data = {},
ogfunc = {},
oldProto = ffi.new("DWORD[1]"),
vtable = vtable,
}
chook.data.hook = function(typestr, ind, __func, len)
if len < 5 then
return nil
end
chook.ogfunc[ind] = { vtable[ind], len }
get_pattern.export.kernel32.VirtualProtect(
ffi.cast("void*", ffi.cast("intptr_t*", vtable) + ind),
ffi.sizeof("intptr_t"),
0x40,
chook.oldProto
)
vtable[ind] = ffi.cast("intptr_t", ffi.cast(typestr, __func))
get_pattern.export.kernel32.VirtualProtect(
ffi.cast("void*", ffi.cast("intptr_t*", vtable) + ind),
ffi.sizeof("intptr_t"),
chook.oldProto[0],
chook.oldProto
)
return ffi.cast(typestr, chook.ogfunc[ind][1])
end
chook.data.unhook = function(ind)
if not chook.ogfunc[ind] then return end
get_pattern.export.kernel32.VirtualProtect(
ffi.cast("void*", ffi.cast("intptr_t*", chook.vtable) + ind),
ffi.sizeof("intptr_t"),
0x40,
chook.oldProto
)
local trampoline_size = chook.ogfunc[ind][2]
local alloc = get_pattern.export.kernel32.VirtualAlloc(nil, trampoline_size, 0x1000, 0x40)
local trampoline = ffi.new("uint8_t[?]", trampoline_size, 0x90)
trampoline[0] = 0xE9
ffi.cast("int32_t*", trampoline + 1)[0] =
tonumber(ffi.cast("intptr_t", chook.ogfunc[ind][1])) - tonumber(ffi.cast("intptr_t", alloc)) - 5
ffi.copy(alloc, trampoline, trampoline_size)
chook.vtable[ind] = ffi.cast("intptr_t", alloc)
get_pattern.export.kernel32.VirtualProtect(
ffi.cast("void*", ffi.cast("intptr_t*", chook.vtable) + ind),
ffi.sizeof("intptr_t"),
chook.oldProto[0],
chook.oldProto
)
chook.ogfunc[ind] = nil
end
chook.data.unhook_all = function()
for ind in pairs(chook.ogfunc) do
chook.data.unhook(ind)
end
end
table.insert(hook.all, chook.data.unhook_all)
return chook.data
end
end
return hook