Skip to content

Instantly share code, notes, and snippets.

@eprosync
Last active September 1, 2025 19:35
Show Gist options
  • Select an option

  • Save eprosync/97c0c2f4bf82898e70b2b7d1ca2d5fd6 to your computer and use it in GitHub Desktop.

Select an option

Save eprosync/97c0c2f4bf82898e70b2b7d1ca2d5fd6 to your computer and use it in GitHub Desktop.
Something about accessing mono via lua
local mono = {}
_G.mono = mono
function mono.initialize(module)
mono.module = module
-- You could use FFI here but for now FFI isn't cross-compatible with memory
local subroutine = {}
mono.subroutine = subroutine
subroutine.get_root_domain = memory.fetch(module, "mono_get_root_domain")
if not subroutine.get_root_domain then return error("mono: unable to locate mono_get_root_domain") end
subroutine.thread_attach = memory.fetch(module, "mono_thread_attach")
if not subroutine.thread_attach then return error("mono: unable to locate mono_thread_attach") end
subroutine.assembly_get_image = memory.fetch(module, "mono_assembly_get_image")
if not subroutine.assembly_get_image then return error("mono: unable to locate mono_assembly_get_image") end
subroutine.image_get_name = memory.fetch(module, "mono_image_get_name")
if not subroutine.image_get_name then return error("mono: unable to locate mono_image_get_name") end
subroutine.runtime_invoke = memory.fetch(module, "mono_runtime_invoke")
if not subroutine.runtime_invoke then return error("mono: unable to locate mono_runtime_invoke") end
subroutine.image_get_table_rows = memory.fetch(module, "mono_image_get_table_rows")
if not subroutine.image_get_table_rows then return error("mono: unable to locate mono_image_get_table_rows") end
subroutine.object_get_class = memory.fetch(module, "mono_object_get_class")
if not subroutine.object_get_class then return error("mono: unable to locate mono_object_get_class") end
subroutine.class_get = memory.fetch(module, "mono_class_get")
if not subroutine.class_get then return error("mono: unable to locate mono_class_get") end
subroutine.class_get_namespace = memory.fetch(module, "mono_class_get_namespace")
if not subroutine.class_get_namespace then return error("mono: unable to locate mono_class_get_namespace") end
subroutine.class_get_name = memory.fetch(module, "mono_class_get_name")
if not subroutine.class_get_name then return error("mono: unable to locate mono_class_get_name") end
subroutine.class_vtable = memory.fetch(module, "mono_class_vtable")
if not subroutine.class_vtable then return error("mono: unable to locate mono_class_vtable") end
subroutine.class_get_fields = memory.fetch(module, "mono_class_get_fields")
if not subroutine.class_get_fields then return error("mono: unable to locate mono_class_get_fields") end
subroutine.field_get_name = memory.fetch(module, "mono_field_get_name")
if not subroutine.field_get_name then return error("mono: unable to locate mono_field_get_name") end
subroutine.field_get_offset = memory.fetch(module, "mono_field_get_offset")
if not subroutine.field_get_offset then return error("mono: unable to locate mono_field_get_offset") end
subroutine.field_get_value = memory.fetch(module, "mono_field_get_value")
if not subroutine.field_get_value then return error("mono: unable to locate mono_field_get_value") end
subroutine.field_set_value = memory.fetch(module, "mono_field_set_value")
if not subroutine.field_set_value then return error("mono: unable to locate mono_field_set_value") end
subroutine.field_static_get_value = memory.fetch(module, "mono_field_static_get_value")
if not subroutine.field_static_get_value then return error("mono: unable to locate mono_field_static_get_value") end
subroutine.field_static_set_value = memory.fetch(module, "mono_field_static_set_value")
if not subroutine.field_static_set_value then return error("mono: unable to locate mono_field_static_set_value") end
subroutine.class_get_methods = memory.fetch(module, "mono_class_get_methods")
if not subroutine.class_get_methods then return error("mono: unable to locate mono_class_get_methods") end
subroutine.method_get_name = memory.fetch(module, "mono_method_get_name")
if not subroutine.method_get_name then return error("mono: unable to locate mono_method_get_name") end
end
function mono.get_domain()
return memory.subroutine.emit(mono.subroutine.get_root_domain)
end
function mono.attach()
return memory.subroutine.emit(mono.subroutine.thread_attach, mono.get_domain())
end
function mono.invoke(method, instance, ...)
local argc = #({...})
local arg_block = nil
if argc > 0 then
arg_block = memory.allocate(argc * memory.size.address)
for i, arg in ipairs({...}) do
local slot = arg_block + (i - 1) * memory.size.address
memory.write.pointer(slot, arg)
end
end
local exception_ptr = memory.allocate(memory.size.address)
memory.write.address(exception_ptr, memory.address(0))
local result = memory.subroutine.emit(
mono.subroutine.runtime_invoke,
method,
instance or memory.address(0),
arg_block or memory.address(0),
exception_ptr
)
local exc = memory.read.address(exception_ptr)
if exc.raw ~= 0 then
error("managed exception thrown.")
end
return result
end
function mono.get_assemblies_offset()
local domain = mono.get_domain()
for i=0, 0x160, 8 do
local domain_assemblies = memory.offset(domain, i)
local _domain_assemblies = memory.offset(domain_assemblies, 0)
local failure = false
for i=0, 20 do
local ran, assembly = pcall(memory.read.address, _domain_assemblies)
if not ran then failure = true break end
_domain_assemblies = memory.read.address(memory.offset(_domain_assemblies, memory.size.address))
end
if not failure then
return i
end
end
end
function mono.get_assemblies()
local domain = mono.get_domain()
local domain_assemblies = memory.offset(domain, jit.arch == "x64" and 0x98 or 0x88)
local _domain_assemblies = memory.offset(domain_assemblies, 0)
local assemblies = {}
local skip = true
while true do
local assembly = memory.read.address(_domain_assemblies)
if not skip then assemblies[#assemblies+1] = assembly end
_domain_assemblies = memory.read.address(memory.offset(_domain_assemblies, memory.size.address))
if _domain_assemblies.raw == 0 then break end
skip = false
end
return assemblies
end
function mono.get_image_name(image)
return memory.read.string(memory.subroutine.emit(mono.subroutine.image_get_name, image))
end
function mono.get_image(assembly)
return memory.subroutine.emit(mono.subroutine.assembly_get_image, assembly)
end
function mono.find_image(name)
local assemblies = mono.get_assemblies()
for i=1, #assemblies do
local assembly = assemblies[i]
local image = mono.get_image(assembly)
local image_name = mono.get_image_name(image)
if image_name == name then
return image
end
end
end
function mono.get_class_name(class)
return memory.read.string(memory.subroutine.emit(mono.subroutine.class_get_name, class))
end
function mono.get_class_namespace(class)
return memory.read.string(memory.subroutine.emit(mono.subroutine.class_get_namespace, class))
end
function mono.get_class_vtable(class)
local domain = mono.get_domain()
return memory.subroutine.emit(mono.subroutine.class_vtable, domain, class)
end
function mono.get_object_class(object)
return memory.subroutine.emit(mono.subroutine.object_get_class, object)
end
function mono.get_classes(image)
local rows = memory.subroutine.emit(mono.subroutine.image_get_table_rows, image, memory.address(0x2)) -- MONO_TABLE_TYPEDEF
local classes = {}
for i=1, rows.int do
local class = memory.subroutine.emit(mono.subroutine.class_get, image, memory.address(bit.bor(0x02000000, i))) -- MONO_TOKEN_TYPE_DEF
classes[i] = class
end
return classes
end
function mono.find_class(image, name)
local rows = memory.subroutine.emit(mono.subroutine.image_get_table_rows, image, memory.address(0x2)) -- MONO_TABLE_TYPEDEF
for i=1, rows.int do
local class = memory.subroutine.emit(mono.subroutine.class_get, image, memory.address(bit.bor(0x02000000, i))) -- MONO_TOKEN_TYPE_DEF
local class_namespace = mono.get_class_namespace(class)
local class_name = mono.get_class_name(class)
if class_namespace == name or class_name == name then
return class
end
end
end
function mono.set_field_value(object, field, value)
return memory.subroutine.emit(mono.subroutine.field_set_value, object, field, value)
end
function mono.get_field_value(object, field)
local ref = memory.allocate(memory.size.address)
memory.subroutine.emit(mono.subroutine.field_get_value, object, field, ref)
return ref
end
function mono.set_field_static_value(vt, field, value)
return memory.subroutine.emit(mono.subroutine.field_static_set_value, vt, field, value)
end
function mono.get_field_static_value(vt, field)
local ref = memory.allocate(memory.size.address)
memory.subroutine.emit(mono.subroutine.field_static_get_value, vt, field, ref)
return ref
end
function mono.get_field_offset(field)
return memory.subroutine.emit(mono.subroutine.field_get_offset, field)
end
function mono.get_field_name(field)
return memory.read.string(memory.subroutine.emit(mono.subroutine.field_get_name, field))
end
function mono.get_fields(class)
local ref = memory.allocate(memory.size.address)
memory.write.address(ref, memory.address(0))
local fields = {}
while true do
local field = memory.subroutine.emit(mono.subroutine.class_get_fields, class, ref)
if field.raw == 0 then break end
fields[#fields+1] = field
end
return fields
end
function mono.find_field(class, name)
local ref = memory.allocate(memory.size.address)
memory.write.address(ref, memory.address(0))
while true do
local field = memory.subroutine.emit(mono.subroutine.class_get_fields, class, ref)
if field.raw == 0 then break end
if mono.get_field_name(field) == name then return field end
end
end
function mono.get_method_name(method)
return memory.read.string(memory.subroutine.emit(mono.subroutine.method_get_name, method))
end
function mono.get_methods(class)
local ref = memory.allocate(memory.size.address)
memory.write.address(ref, memory.address(0))
local methods = {}
while true do
local method = memory.subroutine.emit(mono.subroutine.class_get_methods, class, ref)
if method.raw == 0 then break end
methods[#methods+1] = method
end
return methods
end
function mono.find_method(class, name)
local ref = memory.allocate(memory.size.address)
memory.write.address(ref, memory.address(0))
while true do
local method = memory.subroutine.emit(mono.subroutine.class_get_methods, class, ref)
if method.raw == 0 then break end
if mono.get_method_name(method) == name then return method end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment