CheatMenuSA/moonloader/lib/cheat-menu/libraries/memory.lua
2020-12-03 03:18:34 +06:00

239 lines
7.8 KiB
Lua

-- Virtual memory I/O module for internal process.
--
-- This file is part of SA MoonLoader package.
-- Licensed under the MIT License.
-- Copyright (c) 2019, BlastHack Team <blast.hk>
local ffi = require 'ffi'
local memory = {} -- all function must accept number-type address
local page_access = {
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
GUARD = 0x100,
NOCACHE = 0x200,
WRITECOMBINE = 0x400,
}
local pvoid_t = ffi.typeof('void*')
ffi.cdef [[
int __stdcall VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
int memcmp(const void* ptr1, const void* ptr2, size_t num);
]]
local function set_protection(address, size, access)
local old = ffi.new('unsigned long[1]')
-- TODO ?
--[[
MEMORY_BASIC_INFORMATION mbi = query(address);
if (mbi.BaseAddress == NULL)
return 0;
--]]
if ffi.C.VirtualProtect(address, size, access, old) == 0 then
return nil
end
return old[0]
end
local function unprotect(address, size)
return set_protection(address, size, page_access.EXECUTE_READWRITE)
end
local function unprotect_maybe(address, size, unprot)
if unprot then
return unprotect(address, size)
end
end
local function protect_maybe(address, size, prot)
if prot then
return set_protection(address, size, prot)
end
end
function memory.read(address, size, unprot)
if size > 0 then
if size > 8 then
size = 8
end
address = ffi.cast(pvoid_t, address)
local value = ffi.new('int64_t[1]')
local prot = unprotect_maybe(address, size, unprot)
if not unprot or prot then
ffi.copy(value, address, size)
protect_maybe(address, size, prot)
if size <= 4 then
return tonumber(value[0])
end
return value[0]
end
end
end
function memory.write(address, value, size, unprot)
if size > 0 then
if size > 8 then
size = 8
end
address = ffi.cast(pvoid_t, address)
local val = ffi.new('int64_t[1]', value)
local prot = unprotect_maybe(address, size, unprot)
if not unprot or prot then
ffi.copy(address, val, size)
protect_maybe(address, size, prot)
end
end
end
function memory.unprotect(address, size)
address = ffi.cast(pvoid_t, address)
return unprotect(address, size)
end
function memory.protect(address, size, prot)
address = ffi.cast(pvoid_t, address)
return set_protection(address, size, prot)
end
function memory.copy(dst, src, size, unprot)
dst = ffi.cast(pvoid_t, dst)
if type(src) ~= 'string' then
src = ffi.cast(pvoid_t, src)
end
local prot = unprotect_maybe(dst, size, unprot)
if not unprot or prot then
ffi.copy(dst, src, size)
protect_maybe(dst, size, prot)
end
end
function memory.fill(address, value, size, unprot)
address = ffi.cast(pvoid_t, address)
local prot = unprotect_maybe(address, size, unprot)
if not unprot or prot then
ffi.fill(address, size, value)
protect_maybe(address, size, prot)
end
end
function memory.tostring(address, size, unprot)
address = ffi.cast(pvoid_t, address)
local prot = unprotect_maybe(address, size, unprot)
if not unprot or prot then
local str = ffi.string(address, size)
protect_maybe(address, size, prot)
return str
end
end
function memory.compare(m1, m2, size)
m1 = ffi.cast(pvoid_t, m1)
m2 = ffi.cast(pvoid_t, m2)
return ffi.C.memcmp(m1, m2, size) == 0
end
function memory.strptr(str)
return tonumber(ffi.cast('intptr_t', ffi.cast('const char*', str)))
end
function memory.tohex(data, size, unprot)
data = ffi.cast('const uint8_t*', data)
local prot = unprotect_maybe(data, size, unprot)
if not unprot or prot then
local str = {}
for i = 0, size - 1 do
str[#str + 1] = bit.tohex(data[i], 2)
end
protect_maybe(data, size, prot)
return table.concat(str):upper()
end
end
function memory.hex2bin(hex, dst, size)
if #hex == 0 or #hex % 2 ~= 0 then
return false
end
if dst then
if not size or size == 0 then
return false
end
dst = ffi.cast('uint8_t*', dst)
local idx = 0
for i = 1, #hex, 2 do
local byte = tonumber(hex:sub(i, i + 1), 16)
if not byte then
return false
end
dst[idx] = byte
idx = idx + 1
if idx >= size then
return true
end
end
return true
else
local str = {}
for i = 1, #hex, 2 do
local byte = tonumber(hex:sub(i, i + 1), 16)
if not byte then
return nil
end
str[#str + 1] = string.char(byte)
end
return table.concat(str)
end
end
local function get_value(ctype, address, unprot)
address = ffi.cast(pvoid_t, address)
local size = ffi.sizeof(ctype)
local prot = unprotect_maybe(address, size, unprot)
if not unprot or prot then
local val = ffi.cast(ctype..'*', address)[0]
protect_maybe(address, size, prot)
return val
end
end
local function set_value(ctype, address, value, unprot)
address = ffi.cast(pvoid_t, address)
local size = ffi.sizeof(ctype)
local prot = unprotect_maybe(address, size, unprot)
if not unprot or prot then
ffi.cast(ctype..'*', address)[0] = value
protect_maybe(address, size, prot)
end
end
memory.getvalue = get_value
memory.setvalue = set_value
memory.getint8 = function(address, unprot) return get_value('int8_t', address, unprot) end
memory.getint16 = function(address, unprot) return get_value('int16_t', address, unprot) end
memory.getint32 = function(address, unprot) return get_value('int32_t', address, unprot) end
memory.getint64 = function(address, unprot) return get_value('int64_t', address, unprot) end
memory.getuint8 = function(address, unprot) return get_value('uint8_t', address, unprot) end
memory.getuint16 = function(address, unprot) return get_value('uint16_t', address, unprot) end
memory.getuint32 = function(address, unprot) return get_value('uint32_t', address, unprot) end
memory.getuint64 = function(address, unprot) return get_value('uint64_t', address, unprot) end
memory.getfloat = function(address, unprot) return get_value('float', address, unprot) end
memory.getdouble = function(address, unprot) return get_value('double', address, unprot) end
memory.setint8 = function(address, value, unprot) return set_value('int8_t', address, value, unprot) end
memory.setint16 = function(address, value, unprot) return set_value('int16_t', address, value, unprot) end
memory.setint32 = function(address, value, unprot) return set_value('int32_t', address, value, unprot) end
memory.setint64 = function(address, value, unprot) return set_value('int64_t', address, value, unprot) end
memory.setuint8 = function(address, value, unprot) return set_value('uint8_t', address, value, unprot) end
memory.setuint16 = function(address, value, unprot) return set_value('uint16_t', address, value, unprot) end
memory.setuint32 = function(address, value, unprot) return set_value('uint32_t', address, value, unprot) end
memory.setuint64 = function(address, value, unprot) return set_value('uint64_t', address, value, unprot) end
memory.setfloat = function(address, value, unprot) return set_value('float', address, value, unprot) end
memory.setdouble = function(address, value, unprot) return set_value('double', address, value, unprot) end
memory.pageaccess = page_access
return memory