Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Last active January 2, 2024 06:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MCJack123/4cf6fc941a2d412b4195caafb9636363 to your computer and use it in GitHub Desktop.
Save MCJack123/4cf6fc941a2d412b4195caafb9636363 to your computer and use it in GitHub Desktop.
dbprotect.lua - Protect your functions from the debug library
-- dbprotect.lua - Protect your functions from the debug library
-- By JackMacWindows
-- Licensed under CC0, though I'd appreciate it if this notice was left in place.
-- Simply run this file in some fashion, then call `debug.protect` to protect a function.
-- It takes the function as the first argument, as well as a list of functions
-- that are still allowed to access the function's properties.
-- Once protected, access to the function's environment, locals, and upvalues is
-- blocked from all Lua functions. A function *can not* be unprotected without
-- restarting the Lua state.
-- The debug library itself is protected too, so it's not possible to remove the
-- protection layer after being installed.
-- It's also not possible to add functions to the whitelist after protecting, so
-- make sure everything that needs to access the function's properties are added.
local protectedObjects
local n_getfenv, n_setfenv, d_getfenv, getlocal, getupvalue, d_setfenv, setlocal, setupvalue, upvaluejoin =
getfenv, setfenv, debug.getfenv, debug.getlocal, debug.getupvalue, debug.setfenv, debug.setlocal, debug.setupvalue, debug.upvaluejoin
local error, getinfo, running, select, setmetatable, type, tonumber = error, debug.getinfo, coroutine.running, select, setmetatable, type, tonumber
local superprotected
local function keys(t, v, ...)
if v then t[v] = true end
if select("#", ...) > 0 then return keys(t, ...)
else return t end
end
local function superprotect(v, ...)
if select("#", ...) > 0 then return superprotected[v or ""] or v, superprotect(...)
else return superprotected[v or ""] or v end
end
local function checkint32(n)
n = bit32.band(tonumber(n), 0xFFFFFFFF)
if bit32.btest(n, 0x80000000) then n = n - 0x100000000 end
return n
end
function debug.getinfo(thread, func, what)
if type(thread) ~= "thread" then what, func, thread = func, thread, running() end
local retval
if tonumber(func) then retval = getinfo(thread, func+1, what)
else retval = getinfo(thread, func, what) end
if retval and retval.func then retval.func = superprotected[retval.func] or retval.func end
return retval
end
function debug.getlocal(thread, level, loc)
if loc == nil then loc, level, thread = level, thread, running() end
local k, v
if type(level) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[level] and not (caller and protectedObjects[level][caller.func]) then return nil end
k, v = superprotect(getlocal(level, loc))
elseif tonumber(level) then
local info = getinfo(thread, level + 1, "f")
local caller = getinfo(2, "f")
if info and protectedObjects[info.func] and not (caller and protectedObjects[info.func][caller.func]) then return nil end
k, v = superprotect(getlocal(thread, level + 1, loc))
else k, v = superprotect(getlocal(thread, level, loc)) end
return k, v
end
function debug.getupvalue(func, up)
if type(func) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[func] and not (caller and protectedObjects[func][caller.func]) then return nil end
end
local k, v = superprotect(getupvalue(func, up))
return k, v
end
function debug.setlocal(thread, level, loc, value)
if loc == nil then loc, level, thread = level, thread, running() end
if tonumber(level) then
local info = getinfo(thread, level + 1, "f")
local caller = getinfo(2, "f")
if info and protectedObjects[info.func] and not (caller and protectedObjects[info.func][caller.func]) then error("attempt to set local of protected function", 2) end
setlocal(thread, level + 1, loc, value)
else setlocal(thread, level, loc, value) end
end
function debug.setupvalue(func, up, value)
if type(func) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[func] and not (caller and protectedObjects[func][caller.func]) then error("attempt to set upvalue of protected function", 2) end
end
setupvalue(func, up, value)
end
function _G.getfenv(f)
local v
if f == nil then v = n_getfenv(2)
elseif tonumber(f) and checkint32(f) > 0 then
local info = getinfo(f + 1, "f")
local caller = getinfo(2, "f")
if info and protectedObjects[info.func] and not (caller and protectedObjects[info.func][caller.func]) then return nil end
v = n_getfenv(f+1)
elseif type(f) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[f] and not (caller and protectedObjects[f][caller.func]) then return nil end
v = n_getfenv(f)
else v = n_getfenv(f) end
return v
end
function _G.setfenv(f, tab)
if tonumber(f) and checkint32(f) > 0 then
local info = getinfo(f + 1, "f")
local caller = getinfo(2, "f")
if info and protectedObjects[info.func] and not (caller and protectedObjects[info.func][caller.func]) then error("attempt to set environment of protected function", 2) end
n_setfenv(f+1, tab)
elseif type(f) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[f] and not (caller and protectedObjects[f][caller.func]) then error("attempt to set environment of protected function", 2) end
end
n_setfenv(f, tab)
end
if d_getfenv then
function debug.getfenv(o)
if type(o) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[o] and not (caller and protectedObjects[o][caller.func]) then return nil end
end
local v = d_getfenv(o)
return v
end
function debug.setfenv(o, tab)
if type(o) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[o] and not (caller and protectedObjects[o][caller.func]) then error("attempt to set environment of protected function", 2) end
end
d_setfenv(o, tab)
end
end
if upvaluejoin then
function debug.upvaluejoin(f1, n1, f2, n2)
if type(f1) == "function" and type(f2) == "function" then
local caller = getinfo(2, "f")
if protectedObjects[f1] and not (caller and protectedObjects[f1][caller.func]) then error("attempt to get upvalue of protected function", 2) end
if protectedObjects[f2] and not (caller and protectedObjects[f2][caller.func]) then error("attempt to set upvalue of protected function", 2) end
end
upvaluejoin(f1, n1, f2, n2)
end
end
function debug.protect(func, ...)
if type(func) ~= "function" then error("bad argument #1 (expected function, got " .. type(func) .. ")", 2) end
if protectedObjects[func] then error("attempt to protect a protected function", 2) end
protectedObjects[func] = keys(setmetatable({}, {__mode = "k"}), ...)
end
superprotected = {
[n_getfenv] = _G.getfenv,
[n_setfenv] = _G.setfenv,
[d_getfenv] = debug.getfenv,
[d_setfenv] = debug.setfenv,
[getlocal] = debug.getlocal,
[setlocal] = debug.setlocal,
[getupvalue] = debug.getupvalue,
[setupvalue] = debug.setupvalue,
[upvaluejoin] = debug.upvaluejoin,
[getinfo] = debug.getinfo,
[superprotect] = function() end,
}
protectedObjects = keys(setmetatable({}, {__mode = "k"}),
getfenv,
setfenv,
debug.getfenv,
debug.setfenv,
debug.getlocal,
debug.setlocal,
debug.getupvalue,
debug.setupvalue,
debug.upvaluejoin,
debug.getinfo,
superprotect,
debug.protect
)
for k,v in pairs(protectedObjects) do protectedObjects[k] = {} end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment