## Exploiting Debug Library Guide Written by Greenman#0001 Last Updated Thursday, 05-Sep-2019 11:59:24 GMT-0400 ## Information Before you continue, please understand these things: * This guide is written based on Synapse X's debug functions * Luau has changed how some of these functions work * Some of these functions work differently or produce different results that differ from vanilla Lua * If a new debug function is added to an exploit, it will not be here if it's not documented or I can't figure out what it does Documentation is formatted like this: ``` return_type debug.function_name( arg1 [, arg2]) ``` Arguments enclosed in square brackets are **optional**. ## Lua Terminology It's important to understand some terminology so you don't get confused when reading this guide. ### Local(s) **Local** means a local variable and the plural version is **locals**. Example: ```lua local x = 10 -- This is a local y = 20 -- This is not a local ``` ### Upvalue An upvalue is an **external local variable**. This means that you are accessing a local variable that is above the scope of the current block (in the case of the debug lib, a function). Example: ```lua local x = 10 y = 20 function test() local z = 30 print(x) -- x is an upvalue print(y) -- y is not an upvalue because it's a global print(z) -- z is not an upvalue because it's an internal local variable end ``` If you reassign or reference a local variable that was defined outside the function inside the function, it's an upvalue. ### Stack Level The stack level is a number that returns a specific function or thread. * `0` - The debug function that was called * `1` - The thread that called this function * `2` - The thread calling the function you are in Example (Levels 0-2): ```lua local a = 10 -- stack level 2 function x() local b = 20 -- stack level 1 debug.getinfo(0) -- stack level 0 = this function end x() -- stack level 2 ``` **Note**: There can technically be infinite stack levels but the idea with stack levels is that you are "moving up" threads/functions until you get to the last thread. ### Object An object refers to a table, userdata, thread, or function. ### Int Int is short for **integer** which means a whole number. Example: ``` 1 Int 1.4 Not an int ``` ## Table of Contents This guide will overview the following debug functions: * [debug.getconstant](#debuggetconstant) * [debug.getconstants](#debuggetconstants) * [debug.getfenv](#debuggetfenv) * [debug.getinfo](#debuggetinfo) * [debug.getlocal](#debuggetlocal) * [debug.getlocals](#debuggetlocals) * [debug.getmetatable](#debuggetmetatable) * [debug.getproto](#debuggetproto) * [debug.getprotos](#debuggetprotos) * [debug.getregistry](#debuggetregistry) * [debug.getstack](#debuggetstack) * [debug.getupvalue](#debuggetupvalue) * [debug.getupvalues](#debuggetupvalues) * [debug.setconstant](#debugsetconstant) * [debug.setlocal](#debugsetlocal) * [debug.setmetatable](#debugsetmetatable) * [debug.setproto](#debugsetproto) * [debug.setstack](#debugsetstack) * [debug.setupvalue](#debugsetupvalue) * [debug.setupvaluename](#debugsetupvaluename) * [debug.traceback](#debugtraceback) ## debug.getconstant Documentation: ``` any debug.getconstant( f, idx) ``` Returns the constant at the specified index in function or stack level `f`. Example: ```lua function test() print(10) end print(debug.getconstant(test,2)) -- 10 ``` Keep in mind that function names count as constants and are returned as strings. Example 2: ```lua function test() print(10) end local x = debug.getconstant(test,1) print(x) -- print print("Datatype of constant: "..type(x)) -- Datatype of constant: string ``` ## debug.getconstants Documentation: ``` table debug.getconstants( f) ``` Returns a table containing the constants of function or stack level `f`. Example: ```lua function test() print(10) end for k,v in pairs(debug.getconstants(test)) do print(k,v) end -- Output -- 1 print -- 2 10 ``` This function can be useful when you need to get the index for a constant. ## debug.getfenv Alias: `getfenv` Documentation: ``` table getfenv( f) ``` Returns the global environment table of function or stack level `f`. Same as `getfenv` except it ignores all the safety checks. Example: ```lua function timer() print("5 Second Timer Started!") wait(5) print("Time's Up!") end debug.getfenv(timer).wait = function() end timer() ``` This script will have no delay by adding a new function called wait to the global environment of `timer` that does nothing. ## debug.getinfo Documentation: ``` table debug.getinfo( f [, what]) ``` Returns a table with information about the function or stack level `f`. The `what` argument can be used to select specific pieces of information that will go in the returned table. For information on how to use it, read this [page](https://www.lua.org/pil/23.1.html). Example: ```lua function test() print("hi") end for k,v in pairs(debug.getinfo(test)) do print(k,v) end -- Output -- func function: 0xADDRESS -- nups 0 -- source @NAME -- what Lua -- currentline -1 -- lastlinedefined NUMBER -- linedefined NUMBER -- short_src NAME ``` To understand what this information means, please read [this](https://www.lua.org/manual/5.1/manual.html#lua_Debug) (ignore the code and look for the part where it says "The fields of lua_Debug have the following meaning"). ## debug.getlocal This will return an error due to a replacement function being made for Luau. `debug.getstack` should be used instead. ## debug.getlocals This will return an error due to a replacement function being made for Luau. `debug.getstack` should be used instead. ## debug.getmetatable Alias: `getrawmetatable` Documentation: ``` object debug.getmetatable(object o) ``` Returns the metatable of object `o` or **nil** if `o` has no metatable. This function is **not** the same as `getmetatable` because it will always return the table even if there is `__metatable` in the metatable. Example: ```lua local myTable = {1,2,3} setmetatable(myTable,{ __index = function(t,k) return "Could not access "..k end, __metatable = "No access" -- does not effect debug.getmetatable }) print(myTable[4]) -- Could not access 4 local backup = {"backup1","backup2","backup3","backup4","backup5"} debug.getmetatable(myTable).__index = backup print(myTable[4]) --backup4 ``` ## debug.getproto This function was recently added to Synapse X and doesn't seem to be working properly at the moment. ## debug.getprotos This function was recently added to Synapse X and doesn't seem to be working properly at the moment. ## debug.getregistry Alias: `getreg` Documentation: ``` table debug.getregistry() ``` Returns the Lua registry table. This table contains all functions and threads created from any client-side scripts. Keep in mind that `getgc()` is actually better for most purposes. Example: ```lua for k,v in pairs(debug.getregistry()) do if type(v) == "function" then --you can do stuff like getting upvalues, constants, environment, etc. end end ``` ## debug.getstack Documentation: ``` table debug.getstack( lvl) ``` Returns a table containing the **local** variables at the stack level `lvl`. Unfortunately, this function does not allow you to get the arguments of a function (probably because of Luau?). Example: ```lua for k,v in pairs(debug.getstack(1)) do -- level 1 is current thread print(k,v) end ``` This script will display all of the local variables inside the current thread. ## debug.getupvalue Documentation: ``` any debug.getupvalue( f, idx) ``` Returns the upvalue inside the function or stack level`f` at index `idx`. If an upvalue at `idx` is not found, **nil** is returned. Example: ```lua local health = 100 function takeDamage() health = health - 10 end print(debug.getupvalue(takeDamage,1)) -- 100 ``` ## debug.getupvalues Documentation: ``` table debug.getupvalues( f) ``` Returns a table containing the upvalues of function or stack level `f`. If the function is from a script inside the game, you will get number indices instead of names because of Luau. Example: ```lua local health = 10 local ammo = 100 local state = "standing" function showStats() print("Health: "..health.."\nAmmo: "..ammo.."\nState: "..state) end for k,v in pairs(debug.getupvalues(showStats)) do print(k,v) end ``` ## debug.setconstant Documentation: ``` nil debug.setconstant( f, idx, value) ``` Sets the constant at index `idx` to `value` inside function or stack level `f`. It's recommended to use `debug.getconstants` to see all of the constants available in a function before using this function. Example: ```lua function test() local x = 10 print(x) end function hijack(...) print("HIJACKED: ",...) end debug.setconstant(test,2,"hijack") test() -- HIJACKED: 10 ``` ## debug.setlocal This will return an error due to a replacement function being made for Luau. `debug.setstack` should be used instead. ## debug.setmetatable Alias: `setrawmetatable` Documentation: ``` bool debug.setmetatable( o, mt) ``` Sets `o`'s metatable to `mt` even if the `__metatable` field exists in `o`'s metatable. Keep in mind that this function will return a boolean in Synapse X even though the function is supposed to return `o` according to the reference manual. Example: ```lua local t = {1,2,3} local mt = {__metatable = "The metatable is locked!"} setmetatable(t,mt) --[[ This block will return an error print(setmetatable(t,{ __index = {1,2,3,4,5,6} })) ]] print(debug.setmetatable(t,{ __index = {1,2,3,4,5,6} })) -- true ``` ## debug.setproto This function was recently added to Synapse X and doesn't seem to be working properly at the moment. ## debug.setstack Documentation: ``` nil debug.setstack( lvl, idx, value) ``` Sets the local at index `idx` to `value` at stack level `lvl`. Unfortunately, you cannot set the arguments of a function with this function (probably because of Luau?). Example: ```lua local x = 10 debug.setstack(1,1,100) print(x) -- 100 ``` ## debug.setupvalue Documentation: ``` nil debug.setupvalue( f, idx, value) ``` Sets the upvalue at index `idx` to `value` in function or stack level `f`. Example: ```lua local health = 100 function takeDamage() health = health - 10 end print(health) -- 100 debug.setupvalue(takeDamage,1,math.huge) print(health) -- inf ``` ## debug.setupvaluename Documentation: ``` nil debug.setupvaluename( f, idx, name) ``` Sets the name of the upvalue at `idx` to `name` in function or stack level `f`. This function is meant to be used when you are working with functions from a script inside the game that uses number indices instead of names. Example: ```lua local health = 100 function takeDamage() health = health - 10 end debug.setupvaluename(takeDamage,1,"hp") print(debug.getupvalue(takeDamage,"hp")) -- 100 ``` ## debug.traceback Documentation: ``` string debug.traceback([ message, lvl]) ``` Returns a full execution stack trace of stack level `lvl` and starting with message `message` before the logging. Both arguments are optional and the default value for `lvl` is 1 which is the current thread. Example: ```lua print(debug.traceback()) -- @SCRIPTNAME:1 ``` Example 2: ```lua print(debug.traceback("Traceback test")) -- Traceback test! -- @SCRIPTNAME:1 ```