Модератор раздела "Создание скриптов для читов"
-
Автор темы
- #1
Введение:
В этой статье я бы хотел рассмотреть интересный подход к модификации игровых функций, находящихся в виртуальной таблице, на языке программирования LUA. Пример кода, который будет разобран, демонстрирует способ хука внутриигровой функции write_user_cmd_delta_to_buffer. Используемый метод взят из скрипта "Item crash fix" от qhouz :
Описание кода:
Основная задача данного кода — модификация внутриигровой функции через "хукирование" виртуальной таблицы
Определение типа функции:
Этот код сообщает Lua, как выглядит функция
Получение интерфейса:
Дальше нам нужно получить доступ к самой функции. Функция
Работа с виртуальной таблицей:
Здесь мы получаем указатель на саму виртуальную таблицу
Создание и замена виртуальной таблицы:
Создаём новый массив с тем же размером, что и оригинальная таблица, а затем копируем туда все элементы. Это позволит безопасно подменять функцию, не затрагивая оригинальную таблицу.
Подменяем функцию:
В данном случае мы подменяем 24-ю функцию в таблице на нашу собственную. Теперь при вызове
Восстановление оригинальной таблицы:
Это позволяет вернуть оригинальную таблицу обратно и предотвратить сбои в работе игры после выгрузки скрипта (чаще всего будет происходить краш при повторной загрузке скрипта)
Заключение:
Хочу повторить, что данный метод хука был взят из скрипта qhouz'a. Я не претендую на авторство, а просто показываю примеры того, как еще можно использовать предоставленный метод.
Полный код:
P.S. Если у вас есть какие-либо замечания по поводу моего материала или предложения, пожалуйста, напишите их в теме.
В этой статье я бы хотел рассмотреть интересный подход к модификации игровых функций, находящихся в виртуальной таблице, на языке программирования LUA. Пример кода, который будет разобран, демонстрирует способ хука внутриигровой функции write_user_cmd_delta_to_buffer. Используемый метод взят из скрипта "Item crash fix" от qhouz :
Пожалуйста, авторизуйтесь для просмотра ссылки.
Описание кода:
Основная задача данного кода — модификация внутриигровой функции через "хукирование" виртуальной таблицы
Определение типа функции:
code_language.lua:
local write_user_cmd_delta_to_buffer_t = ffi.typeof [[
bool(__thiscall*)(void*, int, void*, int, int, bool) //we must define the function type
]]
WriteUserCmdDeltaToBuffer
: какие типы данных она принимает в качестве аргументов (void*, int, void*, int, int, bool)
и что возвращает (true или false)
.Получение интерфейса:
Дальше нам нужно получить доступ к самой функции. Функция
get.vclient()
предоставляет доступ к VClient018
code_language.lua:
local VClient018 = utils.create_interface("client.dll", "VClient018")
code_language.lua:
local pointer = ffi.cast("uintptr_t**", interface)
local vtable = ffi.cast("uintptr_t*", pointer[0])
Создание и замена виртуальной таблицы:
code_language.lua:
local hooked_vtable = ffi.new("uintptr_t[?]", size)
for i = 0, size - 1 do
hooked_vtable[i] = vtable[i]
end
Подменяем функцию:
code_language.lua:
hooked_vtable[24] = ffi.cast("uintptr_t", ffi.cast(write_user_cmd_delta_to_buffer_t, write_user_cmd_delta_to_buffer))
WriteUserCmdDeltaToBuffer
будет выполняться наш кодВосстановление оригинальной таблицы:
code_language.lua:
client.add_callback("unload", function()
hooked_vtable[24] = vtable[24]
pointer[0] = vtable
end)
Заключение:
Хочу повторить, что данный метод хука был взят из скрипта qhouz'a. Я не претендую на авторство, а просто показываю примеры того, как еще можно использовать предоставленный метод.
Полный код:
code_language.lua:
--[[
The main code used to write this code was written by qhouz: https://github.com/qhouz/scripting/blob/main/neverlose/item_crash_fix.lua.
I am not claiming authorship, but just showing examples of how else the provided method can be used
In this example we will be trying to hook the game function write_user_cmd_delta_to_buffer
]]
local ffi = require "ffi"
do
local write_user_cmd_delta_to_buffer_t = ffi.typeof [[
bool(__thiscall*)(void*, int, void*, int, int, bool) //we must define the function type
]]
local get = {
vclient = function() --@note: this function is used to get a pointer to the interface
local VClient018 = utils.create_interface("client.dll", "VClient018")
if VClient018 == nil then
print("failed to get interface") --@note: even though it's impossible, it's better to be safe
return
end
return VClient018
end,
vtable = {
_self = function(interface) --@note: this function is used to retrieve a pointer to a virtual table and a pointer to the interface itself from a given interface
local pointer = ffi.cast("uintptr_t**", interface)
local vtable = ffi.cast("uintptr_t*", pointer[0])
if vtable == nil or pointer == nil then
print("vtable: "..vtable.." | pointer: "..pointer) --@note: even though it's impossible, it's better to be safe
return
end
return pointer, vtable
end,
size = function(vtable) --@note: determine the size of the virtual table by traversing its elements until we find the null pointer (0x0), which marks the end of this virtual table
local size = 0
while vtable[size] ~= 0x0 do
size = size + 1
end
return size
end
}
}
local _create = {
hooked_vtable = function(vtable, size) --@note: this function creates a new array hooked_vtable of size and copies all elements from the original virtual table into it
local hooked_vtable = ffi.new("uintptr_t[?]", size)
for i = 0, size - 1 do
hooked_vtable[i] = vtable[i]
end
return hooked_vtable
end
}
local function hook_write_user_cmd_delta_to_buffer(vtable, hooked_vtable)
local original_write_user_cmd_delta_to_buffer = ffi.cast(write_user_cmd_delta_to_buffer_t, vtable[24])
local function write_user_cmd_delta_to_buffer(thisptr, slot, buf, from, to, is_new_cmd)
--@note: you can add your own code here
print("hooked") --@note: for example
return original_write_user_cmd_delta_to_buffer(thisptr, slot, buf, from, to, is_new_cmd) --@note: return original function
end
client.add_callback("unload", function()
hooked_vtable[24] = vtable[24]
pointer[0] = vtable
end)
hooked_vtable[24] = ffi.cast("uintptr_t", ffi.cast(write_user_cmd_delta_to_buffer_t, write_user_cmd_delta_to_buffer))
end
local VClient018 = get.vclient()
local pointer, vtable = get.vtable._self(VClient018)
local size = get.vtable.size(vtable)
local hooked_vtable = _create.hooked_vtable(vtable, size)
pointer[0] = hooked_vtable --@note: replaces the original virtual table with hooked virtual table
hook_write_user_cmd_delta_to_buffer(vtable, hooked_vtable)
end
P.S. Если у вас есть какие-либо замечания по поводу моего материала или предложения, пожалуйста, напишите их в теме.