Skip to main content

Executor APIs

Beyond standard Roblox, executors expose additional functions not available to normal scripts. These unlock powerful capabilities: scanning hidden objects, bypassing locked tables, file access, function interception, and environment inspection.

Check for availability before using:

if not getgc then warn("getgc not available in this executor") return end

getgc() — Scanning All Live Objects

getgc() returns all objects currently tracked by Lua's garbage collector — including instances that aren't accessible through the normal game tree (hidden, disconnected, or stored in script closures).

-- Find ALL RemoteEvents, even hidden ones
for _, v in getgc() do
if typeof(v) == "Instance" and v:IsA("RemoteEvent") then
print(v:GetFullName(), v.Name)
end
end

-- Find all tables with a specific key (good for finding module data)
for _, v in getgc() do
if type(v) == "table" and v.attackDamage then
print("Found stat table:", v.attackDamage)
end
end

-- Find all functions from a specific script environment
for _, v in getgc() do
if type(v) == "function" then
local info = debug and debug.getinfo(v)
-- inspect function source
end
end

getgc() is slow on large games — call it once and cache results, not in a per-frame loop.

getrawmetatable / setrawmetatable

Bypass metatable restrictions. Games protect objects by locking their metatables to prevent reading or writing certain properties. getrawmetatable retrieves the real metatable even if __metatable is set to hide it:

local mt = getrawmetatable(game)
-- mt is the raw metatable, even if game.__metatable returns something else

-- Unlock it to make changes
setreadonly(mt, false)

-- Save original __index
local originalIndex = mt.__index

-- Override property reads
mt.__index = function(self, key)
if key == "SomethingHidden" then
return "revealed"
end
return originalIndex(self, key)
end

setreadonly(mt, true) -- re-lock

setreadonly — Unlocking Frozen Tables

Some games mark tables as read-only so you can't modify them. setreadonly bypasses this:

local frozenTable = getSomeFrozenTable()

-- Check if it's read-only
-- (just try modifying it; if it errors, it's read-only)

setreadonly(frozenTable, false)
frozenTable.newKey = "injected value"
setreadonly(frozenTable, true) -- re-lock if you want

getsenv / getrenv — Script Environments

getsenv(script) returns the environment of a specific script. This gives you access to all its local functions and variables:

local gameScript = Players.LocalPlayer.PlayerScripts:FindFirstChild("Main")
if gameScript then
local env = getsenv(gameScript)

-- Call a function defined inside that script
if type(env.InitializePlayer) == "function" then
env.InitializePlayer()
end

-- Read a variable
print("Script's player data:", env.playerData)
end

getrenv() returns the global Roblox environment — useful for finding functions the Roblox runtime exposes:

local renv = getrenv()
-- renv contains all global Roblox functions

hookfunction — Replacing Functions

Replace any function globally:

-- Hook the game's damage function
local gameScript = Players.LocalPlayer.PlayerScripts:FindFirstChild("Combat")
if gameScript then
local env = getsenv(gameScript)
if type(env.DealDamage) == "function" then
local original = hookfunction(env.DealDamage, function(target, amount)
print("DealDamage called:", target, amount)
return original(target, amount * 2) -- double all damage
end)
end
end

newcclosure wraps your Lua function as a C closure. Some anti-cheats check whether callbacks are Lua functions vs C functions — wrapping can bypass that:

local myClosure = newcclosure(function(...)
-- same behavior, looks like a C function
end)

File System — readfile / writefile

Executors with file system access let you read and write files in the executor's workspace folder:

-- Write data to a file
writefile("my_data.json", '{"score": 100, "name": "Dave"}')

-- Read it back
local content = readfile("my_data.json")
print(content)

-- Check if file exists
if isfile("my_data.json") then
print("file exists")
end

-- List files in a folder
local files = listfiles("./")
for _, f in files do
print(f)
end

-- Create a folder
makefolder("saves")

-- Append to a file
appendfile("log.txt", "new line\n")

The exact path is relative to the executor's workspace (usually workspace/ in the executor's folder).

Practical use: Save game data, config, or logged remote traffic between sessions:

-- Save discovered remote names
local remoteLog = {}
local oldNamecall
oldNamecall = hookmetamethod(game, "__namecall", function(self, ...)
local method = getnamecallmethod()
if method == "FireServer" or method == "InvokeServer" then
remoteLog[self.Name] = true
end
return oldNamecall(self, ...)
end)

-- After playing for a while, save the list
task.delay(60, function()
local names = {}
for name in pairs(remoteLog) do
table.insert(names, name)
end
writefile("remotes_found.txt", table.concat(names, "\n"))
print("Saved", #names, "remote names")
end)

getscriptbytecode — Reading Compiled Scripts

When .Source is empty (Byfron-protected scripts), get the bytecode:

local script = Players.LocalPlayer.PlayerScripts:FindFirstChild("Main")
if script then
local bytecode = getscriptbytecode(script)
-- Pass to a decompiler (most executors have built-in decompile tools)
writefile("main_bytecode.bin", bytecode)
end

is_synapse_function / checkcaller

Some executors expose functions to check the origin of a call:

-- is_synapse_function: returns true if a function was created by the executor (not the game)
-- Useful for filtering your own hooks out of detection loops

-- checkcaller: returns true if the current call stack is from the executor, not the game
if checkcaller and checkcaller() then
-- we're inside an executor-originated call
end

Games use checkcaller to detect if their functions are being called from an executor. You can use it the same way to filter your own hooks from your own logging.