function _getMonoType(f) local t = f.monotype if t == MONO_TYPE_STRING then return 100 end local isRef = (t == MONO_TYPE_CLASS or t == MONO_TYPE_OBJECT or t == MONO_TYPE_ARRAY or t == MONO_TYPE_SZARRAY or t == MONO_TYPE_PTR) if isRef then return vtPointer end if t == MONO_TYPE_I4 or t == MONO_TYPE_U4 then return vtDword end if t == MONO_TYPE_R4 then return vtSingle end if t == MONO_TYPE_R8 then return vtDouble end if t == MONO_TYPE_BOOLEAN then return vtByte end if t == MONO_TYPE_I8 or t == MONO_TYPE_U8 then return vtQword end return vtDword end function _createMonoWrapper(address, classHandle, depth, maxDepth) if not address or address == 0 then return nil end local obj = { _address = address, _class = classHandle, _fields = {}, _nameMap = {} } local curr = classHandle while curr and curr ~= 0 do local fl = mono_class_enumFields(curr) if fl then for _, f in ipairs(fl) do if not f.isStatic then obj._fields[f.name] = { offset = f.offset, type = _getMonoType(f), fieldHandle = f.field } local cleanName = f.name:match("<(.+)>k__BackingField") if cleanName then obj._nameMap[cleanName] = f.name end end end end curr = mono_class_getParent(curr) end setmetatable(obj, { __index = function(t, key) local fieldName = t._nameMap[key] or key local f = t._fields[fieldName] if not f then return rawget(t, key) end local addr = t._address + f.offset if f.type == 100 then local strPtr = readPointer(addr) if not strPtr or strPtr == 0 then return nil end return mono_string_readString(strPtr) end local val if f.type == vtSingle then val = readFloat(addr) elseif f.type == vtDouble then val = readDouble(addr) elseif f.type == vtQword then val = readQword(addr) elseif f.type == vtByte then val = (readBytes(addr, 1) or 0) ~= 0 elseif f.type == vtPointer then val = readPointer(addr) else val = readInteger(addr) end if f.type == vtPointer and val and val ~= 0 and (depth or 0) < (maxDepth or 0) then local fClass = mono_field_getClass(f.fieldHandle) if fClass and fClass ~= 0 then return _createMonoWrapper(val, fClass, (depth or 0) + 1, maxDepth) end end return val end, __newindex = function(t, key, val) local fieldName = t._nameMap[key] or key local f = t._fields[fieldName] if not f then return end local addr = t._address + f.offset if f.type == vtSingle then writeFloat(addr, val) elseif f.type == vtDouble then writeDouble(addr, val) elseif f.type == vtQword then writeQword(addr, val) elseif f.type == vtByte then writeBytes(addr, val ~= nil and 1 or 0) elseif f.type == vtDword then writeInteger(addr, val) end end, __tostring = function(t) return string.format("MonoObject(%X)", t._address) end }) return obj end function getMonoInstances(ns, cls, maxDepth, filterStr) if not LaunchMonoDataCollector() then return {} end local classHandle = nil local assemblies = mono_enumAssemblies() or {} for _, a in ipairs(assemblies) do local img = mono_getImageFromAssembly(a) if img and img ~= 0 then classHandle = mono_image_findClass(img, ns or "", cls) if classHandle and classHandle ~= 0 then break end end end if not classHandle or classHandle == 0 then return {} end local fieldMap = {} local curr = classHandle while curr and curr ~= 0 do local fl = mono_class_enumFields(curr) if fl then for _, f in ipairs(fl) do if not f.isStatic then fieldMap[f.name] = f end end end curr = mono_class_getParent(curr) end local filterFunc = nil if filterStr and filterStr:trim() ~= "" and filterStr ~= "instance." then local code = "return function(instance) return (" .. filterStr .. ") end" local chunk = load(code) if chunk then filterFunc = chunk() end end local instances = mono_class_findInstancesOfClassListOnly(nil, classHandle) or {} local results = {} for _, addr in ipairs(instances) do local keep = true if filterFunc then local proxy = {} setmetatable(proxy, { __index = function(_, key) local f = fieldMap[key] if not f then return nil end local t = f.typename:lower() local fieldAddr = addr + f.offset if t:find("string") then local strPtr = readPointer(fieldAddr) if not strPtr or strPtr == 0 then return nil end return mono_string_readString(strPtr) elseif t:find("int32") or t:find("i4") then return readInteger(fieldAddr) elseif t:find("single") or t:find("r4") or t:find("float") then return readFloat(fieldAddr) elseif t:find("bool") then return readBytes(fieldAddr, 1) ~= 0 elseif t:find("double") or t:find("r8") then return readDouble(fieldAddr) elseif t:find("int64") or t:find("i8") then return readQword(fieldAddr) end local ptr = readPointer(fieldAddr) if ptr and ptr ~= 0 then local fClass = mono_field_getClass(f.field) if fClass and fClass ~= 0 then return _createMonoWrapper(ptr, fClass, 1, maxDepth) end end return ptr end }) local status, res = pcall(filterFunc, proxy) keep = status and (res == true) end if keep then table.insert(results, _createMonoWrapper(addr, classHandle, 0, maxDepth or 0)) end end return results end