Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Sandbox script v0.2 by LizardCar.
- So I followed the http://wiki.roblox.com/index.php/Sandboxing#Making_Instances_safe,
- Does it work? I don't know. I'm a noob scripter. I was just following
- http://wiki.roblox.com/index.php/Sandboxing#Making_Instances_safe due to interest.
- So the basic idea of this script is to prevent certain Methods, Roblox Instances,
- or Tables from executing code if a "Member" is from the "Blacklist". Example:
- If you type code such as
- Game.Workspace.BasePlate:Destroy()
- in the sandboxed lua code,
- it will sandbox Game, then Workspace, then BasePlate, then Destroy and make "Proxy Tables" that
- pretend to be the instances themselves, but the tables can check the "Blacklist" if a userdata,
- table, or function should not execute code. The tables can also "infect" other members
- so that the members can be "Proxy Tables" themselves. If Destroy is in the blacklist, then the
- sandbox will throw an error by saying something like "The member 'Destroy' has been
- sandboxed/locked." or something to prevent the BasePlate from being gone.
- At least, that's what I got out of http://wiki.roblox.com/index.php/Sandboxing#Making_Instances_safe.
- One sandbox-breaking bug so far (but now patched 10-29-13):
- When using Events and calling event listeners, the arguments that the Event passes
- to each listener would get the non-proxied values, meaning that "certain code can break out
- of the sandbox" :(. For Example:
- local DestroyThis=function(p)
- pcall(function()
- p.Parent.Parent.Parent.Players.Player1:Destroy();
- end);
- end
- Game.Workspace.BasePlate.Touched:connect(DestroyThis);
- Whenever a Player touches the BasePlate, the character called Player1 would be "kicked out" of
- the game (p.Parent.Parent.Parent.Players.Player1 is Game.Players.Player1 if you understand).
- The pcall prevents disabling the listener so that Player1 would be grieved :(. Fortunately,
- I patched the code so that the parameter p would be the Proxy instead of the Instance ("Right Leg",
- "Left Leg" as argument if you understand) instead of the real instance itself.
- This means that the proxy p parameter would have
- the metatable that searches for the branches of members and sandbox them if possible
- (Player1 then Workspace then Game then Players then Parent1 then Destroy). There is also
- a sandbox mechanism called "Blacklist" that prevents "Members" (userdata(Roblox Isntances), tables,
- and functions) from executing code such as Destroy (If Destroy is in the blacklist).
- Also, I did something like "Disable Event Connections" in the SandBox to prevent
- "global connections", or any connections.
- ]]
- local SandboxObject=(function()
- local DISAPPOINTING_MESSAGE="If you used getmetatable for this member or getfenv(), you can't. It has been sandboxed :(."
- local ProxyCache={};
- --Contains tables as Proxies for "Members" userdata/tables/functions being declared
- --in the Sandboxed Lua code.
- local UnproxyCache={};
- --Contains the raw unproxied userdata/tables/functions using the proxy value
- --as the key. I probably should have called it "Unsandboxed Members or something".
- local BlacklistCache={};
- local NeedsSandbox=function() end; --Forward declaration.
- local ReturnMemberOrProxy=function(Member,Proxy,function_name)
- if Proxy then
- print("Already sandboxed.");
- for _,T in pairs(BlacklistCache) do
- if Member==T then
- error(string.format("The member \"%s (%s)\" has been Sandboxed/Locked.",
- tostring(Member),function_name),0);
- end
- end
- return Proxy;
- elseif NeedsSandbox(Member,function_name) then
- for _,T in pairs(BlacklistCache) do
- if Member==T then
- error(string.format("The member \"%s (%s)\" has been Sandboxed/Locked.",
- tostring(Member),function_name),0);
- end
- end
- return ProxyCache[Member];
- else
- print("Cannot be sandboxed (yet).");
- return Member;
- end
- end;
- local ShouldDisableEvents=false;
- --[[local ListenToSignal=function(U)
- if string.match(tostring(U),"Signal %u[%l]+") then --Get signal names.
- local ListenerArguments={U:wait()}; --Pauses until event happens.
- print(string.format("A %s Event happened.",tostring(U)));
- for i,j in pairs(ListenerArguments) do
- NeedsSandbox(j);
- end
- end
- end]]
- local SandboxUserdata=function(U)
- print("Sandboxing Userdata: "..tostring(U));
- --coroutine.wrap(ListenToSignal)(U); --To check for any :connections.
- if ShouldDisableEvents and tostring(U)=="Connection" then
- print(pcall(function() U:disconnect() end)); --Disable Event connections immediately.
- error("All Event connections have been locked due to the Sandbox configurations.",0);
- end
- local ProxyTable=setmetatable({},{
- __index=function(_,member_string)
- if member_string=="__proxy_type" then
- return "userdata"; --For the function Sandboxed_type.
- else
- print(string.format(
- "Attempting to sandbox %s[\"%s\"].",tostring(U),
- member_string));
- local Member=U[member_string];
- return ReturnMemberOrProxy(Member,ProxyCache[Member],member_string);
- end
- end;
- __tostring=function(_)
- return tostring(U);
- end;
- __metatable=DISAPPOINTING_MESSAGE;
- }); --Userdata Proxy Table.
- ProxyCache[U]=ProxyTable;
- UnproxyCache[ProxyTable]=U;
- end
- local SandboxTable=function(t) --Mostly same code as function SandboxUserdata.
- print("Sandboxing Table: "..tostring(t));
- local ProxyTable=setmetatable({},{
- __index=function(_,member_string)
- if member_string=="__proxy_type" then
- return "table"; --For the function Sandboxed_type.
- else
- print(string.format(
- "Attempting to sandbox %s[\"%s\"].",tostring(t),
- member_string));
- local Member=t[member_string];
- return ReturnMemberOrProxy(Member,ProxyCache[Member],member_string);
- end
- end;
- __newindex=function(_,member_string,value)
- print("Unsandboxing "..tostring(value));
- if UnproxyCache[value] then
- t[member_string]=UnproxyCache[value];
- else
- t[member_string]=value;
- end --If value is a proxy table, get the unproxied data first.
- --Not sure if needed as much as the Function Proxy tables.
- end;
- --__tostring=function(_)
- -- return tostring(t);
- --end;
- __metatable=DISAPPOINTING_MESSAGE;
- }); --Table Proxy Table.
- ProxyCache[t]=ProxyTable;
- UnproxyCache[ProxyTable]=t;
- end
- local SandboxFunction=function(F,function_name)
- print(string.format("Sandboxing Function: \"%s\"",tostring(function_name)));
- local ProxyTable=setmetatable({},{
- __index=function(_,member_string)
- if member_string=="__proxy_type" then
- return "function"; --For the function Sandboxed_type.
- end
- end;
- __call=function(_,...)
- local args={...};
- for i,v in ipairs(args) do
- print(string.format(
- "Attempting to sandbox %s\'s argument \"%s\".",tostring(function_name),
- tostring(v)));
- args[i]=ReturnMemberOrProxy(v,ProxyCache[v],function_name);
- print(string.format(
- "Attempting to temporarily unsandbox %s\'s argument \"%s\".",tostring(function_name),
- tostring(v)));
- if UnproxyCache[v] then
- print("Done.");
- args[i]=UnproxyCache[v];
- else
- print("Unneeded.");
- args[i]=v;
- end
- if function_name=="connect" and i==2 then
- print("Sandboxing listener: "..tostring(v));
- args[i]=function(...)
- local listenerargs={...};
- for i,T in ipairs(listenerargs) do
- listenerargs[i]=ReturnMemberOrProxy(T,ProxyCache[T]);
- --Sandbox any instances before passing to listener.
- end
- v(unpack(listenerargs)); --Pass proxies if any to listener.
- end --I think this can prevent the sandbox-breaking bug.
- end
- end
- local return_values={F(unpack(args))}
- for i,v in ipairs(return_values) do
- print(string.format(
- "Attempting to sandbox %s\'s return value \"%s\".",tostring(function_name),
- tostring(v)));
- return_values[i]=ReturnMemberOrProxy(v,ProxyCache[v],function_name);
- end
- return unpack(return_values); --Return sandboxed values if any.
- end;
- __metatable=DISAPPOINTING_MESSAGE;
- }); --Function Proxy Table.
- ProxyCache[F]=ProxyTable;
- UnproxyCache[ProxyTable]=F;
- end
- NeedsSandbox=function(T,function_name)
- assert(not ProxyCache[T],
- tostring(ProxyCache[T]).." already sandboxed.");
- if type(T)=="userdata" then
- SandboxUserdata(T);
- elseif type(T)=="table" then
- SandboxTable(T);
- elseif type(T)=="function" then
- SandboxFunction(T,function_name);
- else
- print("Sandboxing failed for: "..tostring(T));
- return false;
- end
- return true;
- end
- local Sandboxed_type=function(v)
- local Success, EmulatedTypeString=pcall(function() return v.__proxy_type; end);
- if Success and v.__proxy_type~=nil then
- return EmulatedTypeString; --Proxies are actually a "table". Let's rewrite type
- --to make the proxies return the member's actual type
- --(Ex:print(type(Game)) should print "userdata" instead of "table").
- else
- return type(v); --Return actual type of non-proxies (numbers,strings...).
- end
- end
- local Run=function(f)
- local sandboxed_env=setmetatable({},{
- __index=function(_,first_member_string)
- if first_member_string=="type" then
- return Sandboxed_type;
- --elseif first_member_string=="__type" then
- -- return "table";
- else
- print("---------------------------------------------------");
- print(string.format(
- "Attempting to sandbox getfenv()[\"%s\"].",first_member_string));
- local Member=getfenv()[first_member_string];
- return ReturnMemberOrProxy(Member,ProxyCache[Member],first_member_string);
- --Return first or second.
- end
- end;
- __metatable=DISAPPOINTING_MESSAGE;
- }); --Captures the first variable/member from getfenv().
- --Ex: In Game.Workspace.BasePlate, first_member_string
- --would be "Game", and the Game
- --Instance is getfenv()[first_member_string].
- --Proxy is a metatable that "pretends to be the Instance Game"
- --as the sandbox mechanism. Otherwise, if it's safe code, return
- --the Member.
- setfenv(f,sandboxed_env);
- f();
- end;
- local ProtectMember=function(T)
- for i,t in ipairs(BlacklistCache) do
- if t==T then
- print("Member \""..tostring(T).."\" already added to Blacklist.")
- return;
- end
- end
- table.insert(BlacklistCache,1,T);
- print("Added member \""..tostring(T).."\" to Blacklist.")
- end
- local UnprotectMember=function(T)
- for i,t in ipairs(BlacklistCache) do
- if t==T then
- table.remove(BlacklistCache,i);
- print("Removed member \""..tostring(T).."\" from Blacklist.")
- break;
- end
- end
- end
- local DisableEvents=function(bool)
- ShouldDisableEvents=bool;
- print("Event connections in the sandbox have been "..(bool and "Disabled." or "Enabled."));
- end
- local ClearBlacklist=function()
- print("Clearing all members from blacklist.");
- BlacklistCache={};
- end
- local ClearCache=function()
- print("Clearing cache.");
- ProxyCache={};
- UnproxyCache={};
- end
- return {Run=Run;
- ProtectMember=ProtectMember;
- UnprotectMember=UnprotectMember;
- DisableEvents=DisableEvents;
- ClearBlacklist=ClearBlacklist;
- ClearCache=ClearCache;
- }; --"Public Methods" Table
- end)();
- SandboxObject.ProtectMember(Workspace);
- SandboxObject.ProtectMember(Workspace);
- SandboxObject.UnprotectMember(Workspace);
- SandboxObject.ProtectMember(Game);
- SandboxObject.ClearBlacklist();
- SandboxObject.ProtectMember(script.Destroy);
- --I think most/all userdata share this (Game.Destroy,Workspace.Destroy, Workspace.BasePlate.Destroy,
- --script.Parent.Destroy, Instance.new("Part",Workspace).Destroy...)
- --SandboxObject.DisableEvents(true);
- Game.Players.PlayerAdded:connect(function(player)
- player.CharacterAdded:wait();
- SandboxObject.ProtectMember(player);
- end);
- SandboxObject.Run(function()
- Game.Players.PlayerAdded:connect(function(player)
- player:Destroy();
- end);
- --[[Game.Workspace.BasePlate.Touched:connect(function(p)
- local player_string=tostring(p.Parent);
- if Game.Players:FindFirstChild(player_string) then
- Game.Players[player_string]:Destroy();
- end
- end);]]
- --[[Game.Workspace.BasePlate.Touched:connect(function(p)
- pcall(function()
- p.Parent:Destroy()
- end);
- end);]]
- local DestroyThis=function(p)
- pcall(function()
- p.Parent.Parent.Parent.Players.Player1:Destroy();
- end);
- end
- Game.Workspace.BasePlate.Touched:connect(DestroyThis);
- --[[local a=setmetatable({},{});
- setmetatable(a,{__index=function(t,k)
- if k==1 then
- return script
- end
- end});
- print(a[1]);]]
- print(type(Game.GetFullName));
- --[[local a=function(g)
- g:Destroy();
- end
- a(Game.Workspace.BasePlate);]]
- end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement