Function Dumper
Function Dumper
local Settings = {
return_values = true,
max_table_size = 3,
IncludeFunctionEnv = false,
decode_bytes = true, -- converts stuff like \110 to 'n'
table_display = 'maximized' --[[
maximized:
{
[1] = 1,
[2] = 2,
[3] = 3,
...
}
minimized:
{ [1] = 1, [2] = 2, [3] = 3, ... }
]]
}
type userdata = {}
type _function = (...any) -> (...any)
function GetFullName(instance)
local p = instance
local lo = {}
while (p ~= game and p.Parent ~= nil) do
table.insert(lo, p)
p = p.Parent
end
local fullName
if #lo == 0 then
return "nil --[[ PARENTED TO NIL OR DESTROYED ]]"
end
if lo[#lo].ClassName ~= "Workspace" then
fullName = 'game:GetService("' .. lo[#lo].ClassName .. '")'
else
fullName = "workspace"
end
for i = #lo - 1, 1, -1 do
fullName = fullName .. ':FindFirstChild("' .. lo[i].Name .. '")'
end
return fullName
end
for k, v in pairs(t1) do
count = count + 1
if count > tot1 then
break
end
if count >= fromt1 then
result[k] = v
end
end
local Metatable = {
--[[
A list of metamethod emulators used to call individual
metamethods efficiently.
]]
metamethods = {
__index = function(self, key)
return self[key]
end,
__newindex = function(self, key, value)
self[key] = value
end,
__call = function(self, ...)
return self(...)
end,
__concat = function(self, b)
return self..b
end,
__add = function(self, b)
return self + b
end,
__sub = function(self, b)
return self - b
end,
__mul = function(self, b)
return self * b
end,
__div = function(self, b)
return self / b
end,
__idiv = function(self, b)
return self // b
end,
__mod = function(self, b)
return self % b
end,
__pow = function(self, b)
return self ^ b
end,
__tostring = function(self)
return tostring(self)
end,
__eq = function(self, b)
return self == b
end,
__lt = function(self, b)
return self < b
end,
__le = function(self, b)
return self <= b
end,
__len = function(self)
return #self
end,
__iter = function(self)
return next, self
end,
__namecall = function(self, ...)
return self:_(...)
end,
__metatable = function(self)
return getmetatable(self)
end,
}
}
--// methods
--[[
Interceptive hook.
xpcall(function()
metamethod_emulator(obj)
end, function()
hooked = debug.info(2, "f")
end)
return hooked
end
--[[
Interceptive hook.
return metamethods
end
--[[
Non-recursive hook.
return Metatable.metahook({}, f)
end
end
--// Localization
local nonluaglobals = {}
local libs = {
coroutine = coroutine,
math = math,
buffer = buffer,
table = table,
string = string,
os = os,
utf8 = utf8,
bit32 = bit32,
debug = debug,
task = task
}
table.rawlength = function(t)
local r = 0
for _, _ in next, t do
r = r + 1
end
return r
end
for _, v in next, t do
result = result .. tostring(v)
if count ~= length then
result = result .. sep
end
count = count + 1
end
return result
end
local
noreturnvalue={'print','warn','error','task.spawn','assert','delay','task.delay','p
rintidentity','spawn','collectgarbage','debug.profilebegin','debug.profileend','deb
ug.resetmemorycategory','debug.dumpcodesize','math.randomseed','table.clear','table
.foreach','table.foreachi','table.insert','table.sort','task.synchronize','task.des
ynchronize','task.cancel','buffer.writei8','buffer.writeu8','buffer.writei16','buff
er.writeu16','buffer.writei32','buffer.writeu32','buffer.writef32','buffer.writef64
','buffer.writestring','buffer.copy','buffer.fill'}
local stack = {}
--// Init
return r
end
local lens = {}
--// Sandbox
local function Sandbox(f: (...any) -> (...any), upvalues: {any}?, constants:
{string}?, protos: {(...any) -> (...any)}?, i: number?, params)
upvalues = upvalues or {}
constants = constants or {}
protos = protos or {}
params = params or {}
i = i or 1
local root = {
root = true,
children = {},
stack = {},
params = {},
constants = constants,
upvalues = upvalues,
protos = protos,
pc = 0,
function_info = {debug.info(f, "na")},
f = f,
i = i,
error_message = '',
fenv = getfenv(f)
}
local id_i = 0
local last_func
local pc = root.pc
local stack = root.stack
local self = {pc = pc, children = {}, parent = parent,
arguments = {...}, metamethod = metamethod}
parent.children[pc] = self
return wrap(self)
end
end
if root.pc ~= 0 then
root.stack[t] = root.pc
else
root.stack[t] = id_i
root.params[t] = id_i
id_i += 1
end
table.insert(params, arg)
root.params[arg] = root.i
root.i += 1
end
if vararg then
local vararg = wrap(root)
table.insert(params, vararg)
root.params[vararg] = "..."
end
if Settings.return_values then
local original_env = getfenv(f)
local return_value
task.spawn(function()
return_value = {pcall(setfenv(f, env), unpack(params))}
end)
for _ = 1, 100 do
if return_value then break end
task.wait(.1)
end
if not return_value then
warn("ERR_1: Function decompile timeout.")
return
end
if return_value[1] then setfenv(f, original_env) end
return root
end
local function Clean(a) -- Remove spaces or illegal charcters from the text
return a:match("%w+")
end
--// Disassembler
local function Disassemble(tree: {any}, tabs: number?)
tabs = tabs or 1
local tab_formatting = (" "):rep(tabs)
local stack_offset do
stack_offset = (final_pc > 0 and 1) or 0
end
local disassembly = {}
local constants = {}
local protos = {}
local pc = 0
if p_index then
if p_index ~= "..." then
return ("_p%d"):format(tonumber(p_index) or 1)
end
return p_index
elseif s_index and tonumber(s_index) then
return ("v%d"):format(tonumber(s_index))
elseif uv_index then
if not upvalues[value] then
disassembly[1] = ("local _uv_%s = v%d[%s]\
n"):format(uv_index, uv_index)..(disassembly[1] or "")
else
return ("_p%s"):format(uv_index)
end
return ("_uv_%d"):format(uv_index)
elseif not p_index and not s_index and not uv_index and type ==
'number' then
return tostring(value)
end
table.insert(constants, value)
if Settings.decode_bytes then
s = string.gsub(s, "\\(%d+)", string.char)
end
return ('"%s"'):format(s)
elseif type == "table" then
local t = ""
local mt = getmetatable(value)
local count = 0
local total = 0
local a = ' '
local tabf = a:rep(tabs)
return t
elseif type == "function" then
if not table.find(protos, value) and value ~= tree.f then
table.insert(protos, value)
if isvrg then
pars = '...'
else
for i = 1, paramcount do
pars = pars .. '_p' .. tostring(i)
if i ~= paramcount then
pars = pars .. ', '
end
end
end
pc += 1 + stack_offset
return sandbox.function_info[1]
end
elseif value == tree.f then
local func_name = debug.info(value, "n")
return tostring(value)
end
local last = 0
for i, index in {...} do
if i - last > 1 then
local void_size = i - last - 1
table.move(table.create(void_size, "nil"), i, void_size, 1,
t)
end
table.insert(t, format(index))
last = i
end
pc = branch.pc
a = a or '(???)'
table.insert(constants, a)
stack[pc] = a
else
if not nonluaglobals[a] then
local thing = ''
if Clean(a) == a then
thing = string.format('.%s', tostring(a))
else
thing = string.format("[%s]",
tostring(format(a)))
end
push = ("local v%d = %s%s"):format(pc, 'v' ..
tostring(parent_pc), thing)
else
local result
if stack[pc-1] then
local lib = stack[pc-1]
result = nonluaglobals[a]:gsub(lib, 'v' ..
tostring(pc-1))
end
push = ("local v%d = %s"):format(pc, result or a)
end
table.insert(constants, a)
table.insert(constants, a)
stack[pc] = format(b)
else
push = ("v%d[%s] = %s"):format(parent_pc, format(a),
format(b))
table.insert(constants, a)
stack[pc] = format(b)
end
elseif metamethod == "__call" then
local namecall_info = ""
local namecall_stack = stack[args[1] or -1]
local is_namecall = namecall_stack == parent_pc - 1
if is_namecall and (parent and parent.parent and
parent.parent.arguments) then
local a = parent.parent.arguments[1] or "(???)"
local b = parent.arguments[1] or "(???)"
pcall(function()
namecall_info = ("-- %s.%s(%s)"):format(a, b,
format_tuple(unpack(args)))
end)
end
local tpl = format_tuple(unpack(args))
local rgs = tpl:split(',')
local result = ''
for ff, arg in rgs do
local arg2 = arg:gsub(' ', '')
if arg2:sub(1, 2) == '_p' and arg2:sub(3,
#arg):match("%d+") == arg2:sub(3, #arg) then
local index = tonumber(arg2:sub(3, #arg))
local nIndex = index
if argCount < index then
nIndex = (index - 1) % argCount + 1
end
arg2 = arg2:gsub(index, nIndex)
end
result = result .. arg2
if ff < #rgs then
result = result .. ', '
end
end
push = ("local v%d = %s(%s)"):format(pc, parent_pc > 0 and 'v' ..
tostring(parent_pc) or '_p' .. tostring(adjusted_pc), tpl)
elseif metamethod == '__tostring' then
local thing = ''
if parent_pc > 0 then
thing = thing .. 'v'.. tostring(parent_pc)
else
thing = thing .. '_p'.. tostring(adjusted_pc)
end
push = ("local v%d = tostring(%s)"):format(pc, thing)
else
local only_self = {
__len = "#",
__unm = "-",
}
local math = {
__add = "+",
__sub = "-",
__mul = "*",
__div = "/",
__idiv = "//",
__pow = "^",
__eq = "==",
__lt = "<",
__le = "<=",
__mod = "%",
__concat = '..'
}
local self_index, math_index = only_self[metamethod],
math[metamethod]
if self_index then
local thing = self_index
if parent_pc > 0 then
thing = thing .. 'v'.. tostring(parent_pc)
else
thing = thing .. '_p'.. tostring(adjusted_pc)
end
push = ("local v%d = %s"):format(
pc,
lens['v' .. tostring(parent_pc)] or thing
)
table.insert(constants, 'v' .. tostring(pc))
elseif math_index then
push = ("local v%d = v%d %s %s"):format(pc, parent_pc,
math_index, format(a))
end
end
if success then
local return_value = tree.return_value
return ((not success and ("%s-- An error occured while decompiling (@pc %d):
%s\n"):format(tab_formatting, final_pc, tree.error_message)) or "")..disassembly ..
'\n', constants, protos, success
end
if Settings.IncludeFunctionEnv then
Output = '-- fenv start\n'
for Index, Value in next, Sandboxed.fenv do
if type(Value) ~= 'function' then
local v = typefind(Value, 'function')
Output = Output .. string.format("%s = %s\n",
tostring(Index), format(v))
end
end
Output = Output .. '\n\n-- fenv end\n\n'
end
return match
end)
return Decompile