Classes/structures
Code: Select all
function GameClass(baseAddress, structure)
local cls = {
base = getAddressSafe(baseAddress),
structure = structure
}
function cls:getBaseAddress()
return rawget(self, "base")
end
function cls:getFieldAddress(field)
return cls:getBaseAddress() + self.structure[field].offset
end
local function setStructureInheritance(structure)
if structure._parent then
setStructureInheritance(structure._parent)
for key, field in pairs(structure._parent) do
if not structure[key] and key ~= "_parent" then
structure[key] = field
end
end
end
end
if structure._parent then
setStructureInheritance(structure)
end
if not cls.base or cls.base == 0 then
return nil
end
setmetatable(cls, {
__index = function(self, key)
local function readBasedOnType(self, field, type)
if field.type == "byte" then
return readByte(self.base + field.offset)
elseif field.type == "short" then
return readSmallInteger(self.base + field.offset)
elseif field.type == "int" then
return readInteger(self.base + field.offset)
elseif field.type == "long" then
return readQword(self.base + field.offset)
elseif field.type == "float" then
return readFloat(self.base + field.offset)
elseif field.type == "double" then
return readDouble(self.base + field.offset)
elseif field.type == "string" then
return readString(self.base + field.offset, field.length) or ""
elseif field.type == "pointer" then
local pointerAddress = readPointer(self.base + field.offset)
if pointerAddress == nil or pointerAddress == 0 then
return nil
elseif field.structure == "self" then
return GameClass(pointerAddress, self.structure)
elseif field.structure then
return GameClass(pointerAddress, field.structure)
else
return pointerAddress
end
elseif field.type == "class" or field.type == "struct" then
local structAddr = getAddress(self.base + field.offset)
if field.structure then
return GameClass(structAddr, field.structure)
else
return structAddr
end
elseif field.type == "array" then
local array = {}
local elemSize = field.elemSize or 4
local numElem = field.num
local elemType = field.elemType
local baseAddr = self.base + field.offset
for i = 0, numElem - 1 do
local elemAddr = baseAddr + i * elemSize
if elemType == "byte" then
table.insert(array, readByte(elemAddr))
elseif elemType == "short" then
table.insert(array, readSmallInteger(elemAddr))
elseif elemType == "int" then
table.insert(array, readInteger(elemAddr))
elseif elemType == "long" then
table.insert(array, readQword(elemAddr))
elseif elemType == "float" then
table.insert(array, readFloat(elemAddr))
elseif elemType == "double" then
table.insert(array, readDouble(elemAddr))
elseif elemType == "pointer" then
table.insert(array, readPointer(elemAddr))
elseif elemType == "class" or elemType == "struct" then
table.insert(array, GameClass(elemAddr, field.elemStructure))
else
error("Unsupported array element type: " .. elemType)
end
end
return array
else
error("Unsupported type: " .. field.type)
end
end
local field = self.structure[key]
if type(field) == "function" then
return field
end
if (type(field) ~= "table") then
return rawget(self, key)
end
if not field then
error("Field '" .. key .. "' does not exist!")
end
return readBasedOnType(self, field, field.type)
end,
__newindex = function(self, key, value)
local field = self.structure[key]
if not field then
error("Field '" .. key .. "' does not exist or is not writable!")
end
if field.type == "byte" then
writeByte(self.base + field.offset, value)
elseif field.type == "short" then
writeSmallInteger(self.base + field.offset, value)
elseif field.type == "int" then
writeInteger(self.base + field.offset, value)
elseif field.type == "long" then
writeQword(self.base + field.offset, value)
elseif field.type == "float" then
writeFloat(self.base + field.offset, value)
elseif field.type == "double" then
writeDouble(self.base + field.offset, value)
elseif field.type == "string" then
writeString(self.base + field.offset, value, field.length)
elseif field.type == "pointer" then
error("Cannot directly write to a pointer field!") -- Optional safety
else
error("Unsupported type: " .. field.type)
end
end,
__eq = function(self, other)
if (type(other) == "table") then
return self.base == other.base
end
return self.base == other
end
})
return cls
end
-- How it works?
-- Simple as it is
-- Let's say we want to declare a structure
PlayerBase =
{
m_fHealth = {type = "float", offset = 0x80},
m_fMaxHealth = {type = "float", offset = 0x84}
m_Info = {type = "pointer", offset = 0x90, structure = { -- Can be also treated as `structure = SomeClass`
m_Ammo = {type = "int", offset = 0x14},
m_CurrentWeapon = {type = "int", offset = 0x4}
}
}
}
local Player = GameClass(0x12345678, PlayerBase) -- And if it didn't initialized, the Player will be nil
Player.m_fHealth = 50.0
Player.m_Info.m_Ammo = 900
printf("Now we have %f health", Player.m_fHealth)
printf("And some ammo(probably too much) %d", Player.m_Info.m_Ammo)
-- For inheritance
-- Let's take the example below
BaseEntity =
{
m_iHealth = {type = "int", offset = 0x50},
m_iMaxHealth = {type = "int", offset = 0x54}
}
BasePlayer =
{
_parent = BaseEntity,
m_iAmmo = {type = "int", offset = 0x60},
m_Weapons = {type = "array", elemSize = 4, elemType = "pointer", num = 6, offset = 0x80} -- accessed like a typical table if it's not type of `class` or `struct`
}
-- The whole explanation:
-- [[
The main code accesses the structure of a player to get appropriate type with offsets
The values of fields are being changed according to their type and setted value, so you don't need to write functions like writeXXXXX
GameClass constructor takes the first argument for a base of a structure located in the memory
-- ]]
Virtual Table Method Navigation
Code: Select all
function GetVMT(object, index)
if (not index) then
return readPointer(object)
end
return function(...) return executeMethod(0, nil, readPointer(GetVMT(object, nil) + (targetIs64Bit() and 8 or 4) * index), object, ...) end
end
-- To access any virtual table method at the moment
local setHealth = GetVMT(player, 16) -- Or 0x40 / 4 assuming that the target is 32 bit
setHealth(128)
-- Or
GetVMT(player, 16)(128)