Advertisement
einsteinK

"Simple" killable (easy-to-breakout) sandbox

Jan 12th, 2018
242
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.03 KB | None | 0 0
  1. local fakeToReal = setmetatable({},{__mode="k"})
  2. local realToFake = setmetatable({},{__mode="v"})
  3. local connections = setmetatable({},{__mode="k"})
  4.  
  5.  
  6. local Metafields = {"index","newindex","call","metatable","tostring","len","unm","add","sub","mul","div","mod","pow","concat","eq","lt","le"}
  7.  
  8. local API,dead = {}
  9.  
  10. local DeadTrap,Kill = newproxy(true)
  11.  
  12. local metacount = 0
  13.  
  14. local function safeprint(...)
  15.     local e = getfenv(0)
  16.     setfenv(0,getfenv())
  17.     print(...) setfenv(0,e)
  18. end
  19.  
  20. local kill = Instance.new("Folder")
  21. local function killf() kill:Destroy() end
  22. function Kill()
  23.     -- safeprint("metacount:", metacount)
  24.     if metacount > 0 then return DeadTrap end
  25.     spawn(killf) kill.Changed:Wait()
  26. end
  27.  
  28. do
  29.     local deadMeta = getmetatable(DeadTrap)
  30.     local function deadBringer() return DeadTrap end
  31.     for k,v in pairs(Metafields) do
  32.         deadMeta["__"..v] = deadBringer
  33.     end deadMeta.__call = Kill
  34. end
  35.  
  36. local sandbox,unsandbox
  37.  
  38. local function tuple(...)
  39.     return {n=select("#",...),...}
  40. end
  41. local function customConnect(e,f)
  42.     local con = e:Connect(function(...)
  43.         metacount = 0 f(...)
  44.     end)
  45.     connections[con] = true
  46.     return con
  47. end
  48. local function customWait(e)
  49.     local t = tuple(e:Wait())
  50.     metacount = 0
  51.     if dead then Kill() end
  52.     return unpack(t,1,t.n)
  53. end
  54.  
  55. local instanceMeta = {}
  56. instanceMeta.__metatable = getmetatable(game)
  57. function instanceMeta:__index(key)
  58.     if dead then return DeadTrap end
  59.     local real = fakeToReal[self]
  60.     if key == "connect" or key == "Connect" and typeof(real) == "RBXScriptSignal" then
  61.         return sandbox(customConnect)
  62.     elseif key == "wait" or key == "Wait" and typeof(real) == "RBXScriptSignal" then
  63.         return sandbox(customWait)
  64.     end
  65.     return sandbox(real[key])
  66. end
  67. function instanceMeta:__newindex(key,val)
  68.     if dead then return DeadTrap end
  69.     unsandbox(self)[key] = unsandbox(val)
  70. end
  71. function instanceMeta:__call(...)
  72.     if ... == 123 then return true end -- Kind of an assert for "is this sandboxed?"
  73.     if dead then return Kill() end
  74.     return API.SandboxTuple(unsandbox(self)(API.UnsandboxTuple(...)))
  75. end
  76. function instanceMeta:__tostring()
  77.     if dead then return DeadTrap end
  78.     return "~"..tostring(unsandbox(self)) -- Easier to debug
  79. end
  80.  
  81. function API.Kill()
  82.     warn("RIP")
  83.     dead = true
  84.     for k in pairs(connections) do
  85.         k:Disconnect()
  86.     end
  87. end
  88.  
  89. function API.Sandbox(real)
  90.     local fake = realToFake[real]
  91.     if fake then return fake end
  92.    
  93.     local t = typeof(real)
  94.     if t == "Instance" or t == "RBXScriptSignal" then
  95.         fake = newproxy(true)
  96.         fakeToReal[fake] = real
  97.         realToFake[real] = fake
  98.         local meta = getmetatable(fake)
  99.         for k,v in pairs(instanceMeta) do
  100.             meta[k] = v
  101.         end return fake
  102.     elseif t == "function" then
  103.         local f = function(...)
  104.             if dead then return Kill() end
  105.             return API.SandboxTuple(real(API.UnsandboxTuple(...)))
  106.         end
  107.         fakeToReal[f] = real
  108.         realToFake[real] = f
  109.     elseif t == "table" then
  110.         fake = {}
  111.         for k,v in pairs(real) do
  112.             fake[sandbox(k)] = sandbox(v)
  113.         end return fake
  114.     end
  115.    
  116.     return real
  117. end sandbox = API.Sandbox
  118. function API.Unsandbox(fake)
  119.     local real = fakeToReal[fake]
  120.     if real then return real end
  121.    
  122.     local t = typeof(fake)
  123.     if t == "function" then
  124.         local f = function(...)
  125.             if dead then return Kill() end
  126.             return API.UnsandboxTuple(fake(API.SandboxTuple(...)))
  127.         end
  128.         fakeToReal[fake] = f
  129.         realToFake[f] = fake
  130.     elseif t == "table" then
  131.         real = {}
  132.         for k,v in pairs(fake) do
  133.             real[unsandbox(k)] = unsandbox(v)
  134.         end return real
  135.     end
  136.    
  137.     return fake
  138. end unsandbox = API.Unsandbox
  139.  
  140. function API.SandboxTuple(...)
  141.     local n,res = select("#",...),{...}
  142.     for i=1,n do
  143.         res[i] = sandbox(res[i])
  144.     end return unpack(res,1,n)
  145. end
  146. function API.UnsandboxTuple(...)
  147.     local n,res = select("#",...),{...}
  148.     for i=1,n do
  149.         res[i] = unsandbox(res[i])
  150.     end return unpack(res,1,n)
  151. end
  152.  
  153. local Globals,GlobalEnv = {
  154.     tostring = tostring;
  155.     error = error;
  156.     getfenv = getfenv;
  157.     setfenv = setfenv;
  158.     pairs = pairs;
  159.     ipairs = ipairs;
  160.     next = next;
  161.     setmetatable = setmetatable;
  162.     getmetatable = getmetatable;
  163. },getfenv()
  164.  
  165. function Globals.spawn(f)
  166.     spawn(function(...)
  167.         metacount = 0 f(...)
  168.     end)
  169. end Globals.Spawn = Globals.spawn
  170. function Globals.delay(d,f)
  171.     delay(d,function(...)
  172.         metacount = 0 f(...)
  173.     end)
  174. end Globals.Delay = Globals.delay
  175. function Globals.wait(...)
  176.     if dead then
  177.         metacount = 0
  178.         return Kill()
  179.     end return wait(...)
  180. end Globals.Wait = Globals.wait
  181. function Globals.print(...)
  182.     if dead then return Kill() end
  183.     print(...)
  184. end
  185. function Globals.warn(...)
  186.     if dead then return Kill() end
  187.     warn(...)
  188. end
  189.  
  190. local metas = setmetatable({},{__mode="k"})
  191. local function proxyMeta(meta)
  192.     local res = {}
  193.     for k,v in pairs(Metafields) do
  194.         v = "__"..v
  195.         res[v] = function(...)
  196.             local m = meta[v]
  197.             if not m then
  198.                 error("No metamethod "..v,0)
  199.             end metacount = metacount + 1
  200.             local t = tuple(m(...))
  201.             metacount = metacount - 1
  202.             return unpack(t,1,t.n)
  203.         end
  204.     end
  205.     function res:__index(key)
  206.         local m,t = meta.__index,{}
  207.         metacount = metacount + 1
  208.         if typeof(m) == "function" then
  209.             t = tuple(m(self,key))
  210.         else
  211.             t = {n=1,m[key]}
  212.         end
  213.         metacount = metacount - 1
  214.         return unpack(t,1,t.n)
  215.     end
  216.     function res:__newindex(key,val)
  217.         local m,t = meta.__newindex,{}
  218.         metacount = metacount + 1
  219.         if typeof(m) == "function" then
  220.             m(self,key,val)
  221.         else
  222.             m[key] = val
  223.         end metacount = metacount - 1
  224.     end
  225.     local function e(a) return a end
  226.     function res:__call(...)
  227.         if dead then return Kill() end
  228.         return e(meta.__call)(...)
  229.     end return res
  230. end
  231. function Globals.getmetatable(val)
  232.     local m = metas[val]
  233.     if m and m.__metatable ~= nil then
  234.         return m
  235.     end return m or getmetatable(val)
  236. end
  237. function Globals.setmetatable(val,meta)
  238.     local m = metas[val]
  239.     if m and m.__metatable ~= nil then
  240.         error("cannot change a protected metatable",2)
  241.     end
  242.     setmetatable(val,proxyMeta(meta))
  243.     metas[val] = meta return val
  244. end
  245.  
  246. function API.Create()
  247.     return setmetatable({},{__index=function(s,k)
  248.         return Globals[k] or dead and DeadTrap or sandbox(GlobalEnv[k])
  249.     end})
  250. end
  251.  
  252. local unboxedBE = Instance.new("BindableEvent",workspace)
  253. unboxedBE.Name = "SandboxEvent"
  254. spawn(function()
  255.     while wait(0.1) do
  256.         unboxedBE:Fire(game)
  257.     end
  258. end)
  259.  
  260. spawn(function()
  261.     local env = API.Create()
  262.     delay(0.5,API.Kill)
  263.     setfenv(0,env)
  264.     setfenv(1,env)
  265.     game(123)
  266.    
  267.     local BE = Instance.new("BindableEvent")
  268.     BE.Event:Connect(warn)
  269.     BE:Fire("hi")
  270.    
  271.     local eh = setmetatable({},{__index=function(s,k)
  272.         safeprint("eh.__index",k)
  273.         print("Did __index with key",k)
  274.     end})
  275.     local function test(k)
  276.         print(eh[k])
  277.     end
  278.     test()
  279.     spawn(test)
  280.     delay(1,test)
  281.    
  282.     spawn(function()
  283.         while wait() do
  284.             print("Still running")
  285.         end
  286.     end)
  287.    
  288.     local BE = workspace.SandboxEvent
  289.     BE.Parent = nil -- otherwise every test leaves one behind
  290.     BE.Event:Connect(function(g) print("fired",g,g(123)) end)
  291.     spawn(function()
  292.         local g = BE.Event:Wait()
  293.         print("waited",g,g(123))
  294.     end)
  295.     delay(1,function()
  296.         local g = BE.Event:Wait()
  297.         print("waited",g,g(123))
  298.     end)
  299.     wait(0.5)
  300.     warn("for realz")
  301. end)
  302.  
  303. return API
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement