Advertisement
ZeeDerp

Sandbox

Oct 15th, 2014
1,577
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.54 KB | None | 0 0
  1. --[[
  2. Sandbox script v0.2 by LizardCar.
  3. So I followed the http://wiki.roblox.com/index.php/Sandboxing#Making_Instances_safe,
  4. Does it work? I don't know. I'm a noob scripter. I was just following
  5. http://wiki.roblox.com/index.php/Sandboxing#Making_Instances_safe due to interest.
  6. So the basic idea of this script is to prevent certain Methods, Roblox Instances,
  7. or Tables from executing code if a "Member" is from the "Blacklist". Example:
  8. If you type code such as
  9. Game.Workspace.BasePlate:Destroy()
  10. in the sandboxed lua code,
  11. it will sandbox Game, then Workspace, then BasePlate, then Destroy and make "Proxy Tables" that
  12. pretend to be the instances themselves, but the tables can check the "Blacklist" if a userdata,
  13. table, or function should not execute code. The tables can also "infect" other members
  14. so that the members can be "Proxy Tables" themselves. If Destroy is in the blacklist, then the
  15. sandbox will throw an error by saying something like "The member 'Destroy' has been
  16. sandboxed/locked." or something to prevent the BasePlate from being gone.
  17.  
  18. At least, that's what I got out of http://wiki.roblox.com/index.php/Sandboxing#Making_Instances_safe.
  19.  
  20. One sandbox-breaking bug so far (but now patched 10-29-13):
  21. When using Events and calling event listeners, the arguments that the Event passes
  22. to each listener would get the non-proxied values, meaning that "certain code can break out
  23. of the sandbox" :(. For Example:
  24. local DestroyThis=function(p)
  25. pcall(function()
  26. p.Parent.Parent.Parent.Players.Player1:Destroy();
  27. end);
  28. end
  29. Game.Workspace.BasePlate.Touched:connect(DestroyThis);
  30. Whenever a Player touches the BasePlate, the character called Player1 would be "kicked out" of
  31. the game (p.Parent.Parent.Parent.Players.Player1 is Game.Players.Player1 if you understand).
  32. The pcall prevents disabling the listener so that Player1 would be grieved :(. Fortunately,
  33. I patched the code so that the parameter p would be the Proxy instead of the Instance ("Right Leg",
  34. "Left Leg" as argument if you understand) instead of the real instance itself.
  35. This means that the proxy p parameter would have
  36. the metatable that searches for the branches of members and sandbox them if possible
  37. (Player1 then Workspace then Game then Players then Parent1 then Destroy). There is also
  38. a sandbox mechanism called "Blacklist" that prevents "Members" (userdata(Roblox Isntances), tables,
  39. and functions) from executing code such as Destroy (If Destroy is in the blacklist).
  40. Also, I did something like "Disable Event Connections" in the SandBox to prevent
  41. "global connections", or any connections.
  42. ]]
  43. local SandboxObject=(function()
  44. local DISAPPOINTING_MESSAGE="If you used getmetatable for this member or getfenv(), you can't. It has been sandboxed :(."
  45. local ProxyCache={};
  46. --Contains tables as Proxies for "Members" userdata/tables/functions being declared
  47. --in the Sandboxed Lua code.
  48. local UnproxyCache={};
  49. --Contains the raw unproxied userdata/tables/functions using the proxy value
  50. --as the key. I probably should have called it "Unsandboxed Members or something".
  51. local BlacklistCache={};
  52. local NeedsSandbox=function() end; --Forward declaration.
  53. local ReturnMemberOrProxy=function(Member,Proxy,function_name)
  54. if Proxy then
  55. print("Already sandboxed.");
  56. for _,T in pairs(BlacklistCache) do
  57. if Member==T then
  58. error(string.format("The member \"%s (%s)\" has been Sandboxed/Locked.",
  59. tostring(Member),function_name),0);
  60. end
  61. end
  62. return Proxy;
  63. elseif NeedsSandbox(Member,function_name) then
  64. for _,T in pairs(BlacklistCache) do
  65. if Member==T then
  66. error(string.format("The member \"%s (%s)\" has been Sandboxed/Locked.",
  67. tostring(Member),function_name),0);
  68. end
  69. end
  70. return ProxyCache[Member];
  71. else
  72. print("Cannot be sandboxed (yet).");
  73. return Member;
  74. end
  75. end;
  76. local ShouldDisableEvents=false;
  77. --[[local ListenToSignal=function(U)
  78. if string.match(tostring(U),"Signal %u[%l]+") then --Get signal names.
  79. local ListenerArguments={U:wait()}; --Pauses until event happens.
  80. print(string.format("A %s Event happened.",tostring(U)));
  81. for i,j in pairs(ListenerArguments) do
  82. NeedsSandbox(j);
  83. end
  84. end
  85. end]]
  86. local SandboxUserdata=function(U)
  87. print("Sandboxing Userdata: "..tostring(U));
  88. --coroutine.wrap(ListenToSignal)(U); --To check for any :connections.
  89. if ShouldDisableEvents and tostring(U)=="Connection" then
  90. print(pcall(function() U:disconnect() end)); --Disable Event connections immediately.
  91. error("All Event connections have been locked due to the Sandbox configurations.",0);
  92. end
  93. local ProxyTable=setmetatable({},{
  94. __index=function(_,member_string)
  95. if member_string=="__proxy_type" then
  96. return "userdata"; --For the function Sandboxed_type.
  97. else
  98. print(string.format(
  99. "Attempting to sandbox %s[\"%s\"].",tostring(U),
  100. member_string));
  101. local Member=U[member_string];
  102. return ReturnMemberOrProxy(Member,ProxyCache[Member],member_string);
  103. end
  104. end;
  105. __tostring=function(_)
  106. return tostring(U);
  107. end;
  108. __metatable=DISAPPOINTING_MESSAGE;
  109. }); --Userdata Proxy Table.
  110. ProxyCache[U]=ProxyTable;
  111. UnproxyCache[ProxyTable]=U;
  112. end
  113. local SandboxTable=function(t) --Mostly same code as function SandboxUserdata.
  114. print("Sandboxing Table: "..tostring(t));
  115. local ProxyTable=setmetatable({},{
  116. __index=function(_,member_string)
  117. if member_string=="__proxy_type" then
  118. return "table"; --For the function Sandboxed_type.
  119. else
  120. print(string.format(
  121. "Attempting to sandbox %s[\"%s\"].",tostring(t),
  122. member_string));
  123. local Member=t[member_string];
  124. return ReturnMemberOrProxy(Member,ProxyCache[Member],member_string);
  125. end
  126. end;
  127. __newindex=function(_,member_string,value)
  128. print("Unsandboxing "..tostring(value));
  129. if UnproxyCache[value] then
  130. t[member_string]=UnproxyCache[value];
  131. else
  132. t[member_string]=value;
  133. end --If value is a proxy table, get the unproxied data first.
  134. --Not sure if needed as much as the Function Proxy tables.
  135. end;
  136. --__tostring=function(_)
  137. -- return tostring(t);
  138. --end;
  139. __metatable=DISAPPOINTING_MESSAGE;
  140. }); --Table Proxy Table.
  141. ProxyCache[t]=ProxyTable;
  142. UnproxyCache[ProxyTable]=t;
  143. end
  144. local SandboxFunction=function(F,function_name)
  145. print(string.format("Sandboxing Function: \"%s\"",tostring(function_name)));
  146. local ProxyTable=setmetatable({},{
  147. __index=function(_,member_string)
  148. if member_string=="__proxy_type" then
  149. return "function"; --For the function Sandboxed_type.
  150. end
  151. end;
  152. __call=function(_,...)
  153. local args={...};
  154. for i,v in ipairs(args) do
  155. print(string.format(
  156. "Attempting to sandbox %s\'s argument \"%s\".",tostring(function_name),
  157. tostring(v)));
  158. args[i]=ReturnMemberOrProxy(v,ProxyCache[v],function_name);
  159. print(string.format(
  160. "Attempting to temporarily unsandbox %s\'s argument \"%s\".",tostring(function_name),
  161. tostring(v)));
  162. if UnproxyCache[v] then
  163. print("Done.");
  164. args[i]=UnproxyCache[v];
  165. else
  166. print("Unneeded.");
  167. args[i]=v;
  168. end
  169. if function_name=="connect" and i==2 then
  170. print("Sandboxing listener: "..tostring(v));
  171. args[i]=function(...)
  172. local listenerargs={...};
  173. for i,T in ipairs(listenerargs) do
  174. listenerargs[i]=ReturnMemberOrProxy(T,ProxyCache[T]);
  175. --Sandbox any instances before passing to listener.
  176. end
  177. v(unpack(listenerargs)); --Pass proxies if any to listener.
  178. end --I think this can prevent the sandbox-breaking bug.
  179. end
  180. end
  181. local return_values={F(unpack(args))}
  182. for i,v in ipairs(return_values) do
  183. print(string.format(
  184. "Attempting to sandbox %s\'s return value \"%s\".",tostring(function_name),
  185. tostring(v)));
  186. return_values[i]=ReturnMemberOrProxy(v,ProxyCache[v],function_name);
  187. end
  188. return unpack(return_values); --Return sandboxed values if any.
  189. end;
  190. __metatable=DISAPPOINTING_MESSAGE;
  191. }); --Function Proxy Table.
  192. ProxyCache[F]=ProxyTable;
  193. UnproxyCache[ProxyTable]=F;
  194. end
  195. NeedsSandbox=function(T,function_name)
  196. assert(not ProxyCache[T],
  197. tostring(ProxyCache[T]).." already sandboxed.");
  198. if type(T)=="userdata" then
  199. SandboxUserdata(T);
  200. elseif type(T)=="table" then
  201. SandboxTable(T);
  202. elseif type(T)=="function" then
  203. SandboxFunction(T,function_name);
  204. else
  205. print("Sandboxing failed for: "..tostring(T));
  206. return false;
  207. end
  208. return true;
  209. end
  210. local Sandboxed_type=function(v)
  211. local Success, EmulatedTypeString=pcall(function() return v.__proxy_type; end);
  212. if Success and v.__proxy_type~=nil then
  213. return EmulatedTypeString; --Proxies are actually a "table". Let's rewrite type
  214. --to make the proxies return the member's actual type
  215. --(Ex:print(type(Game)) should print "userdata" instead of "table").
  216. else
  217. return type(v); --Return actual type of non-proxies (numbers,strings...).
  218. end
  219. end
  220. local Run=function(f)
  221. local sandboxed_env=setmetatable({},{
  222. __index=function(_,first_member_string)
  223. if first_member_string=="type" then
  224. return Sandboxed_type;
  225. --elseif first_member_string=="__type" then
  226. -- return "table";
  227. else
  228. print("---------------------------------------------------");
  229. print(string.format(
  230. "Attempting to sandbox getfenv()[\"%s\"].",first_member_string));
  231. local Member=getfenv()[first_member_string];
  232. return ReturnMemberOrProxy(Member,ProxyCache[Member],first_member_string);
  233. --Return first or second.
  234. end
  235. end;
  236. __metatable=DISAPPOINTING_MESSAGE;
  237. }); --Captures the first variable/member from getfenv().
  238. --Ex: In Game.Workspace.BasePlate, first_member_string
  239. --would be "Game", and the Game
  240. --Instance is getfenv()[first_member_string].
  241. --Proxy is a metatable that "pretends to be the Instance Game"
  242. --as the sandbox mechanism. Otherwise, if it's safe code, return
  243. --the Member.
  244. setfenv(f,sandboxed_env);
  245. f();
  246. end;
  247. local ProtectMember=function(T)
  248. for i,t in ipairs(BlacklistCache) do
  249. if t==T then
  250. print("Member \""..tostring(T).."\" already added to Blacklist.")
  251. return;
  252. end
  253. end
  254. table.insert(BlacklistCache,1,T);
  255. print("Added member \""..tostring(T).."\" to Blacklist.")
  256. end
  257. local UnprotectMember=function(T)
  258. for i,t in ipairs(BlacklistCache) do
  259. if t==T then
  260. table.remove(BlacklistCache,i);
  261. print("Removed member \""..tostring(T).."\" from Blacklist.")
  262. break;
  263. end
  264. end
  265. end
  266. local DisableEvents=function(bool)
  267. ShouldDisableEvents=bool;
  268. print("Event connections in the sandbox have been "..(bool and "Disabled." or "Enabled."));
  269. end
  270. local ClearBlacklist=function()
  271. print("Clearing all members from blacklist.");
  272. BlacklistCache={};
  273. end
  274. local ClearCache=function()
  275. print("Clearing cache.");
  276. ProxyCache={};
  277. UnproxyCache={};
  278. end
  279. return {Run=Run;
  280. ProtectMember=ProtectMember;
  281. UnprotectMember=UnprotectMember;
  282. DisableEvents=DisableEvents;
  283. ClearBlacklist=ClearBlacklist;
  284. ClearCache=ClearCache;
  285. }; --"Public Methods" Table
  286. end)();
  287. SandboxObject.ProtectMember(Workspace);
  288. SandboxObject.ProtectMember(Workspace);
  289. SandboxObject.UnprotectMember(Workspace);
  290. SandboxObject.ProtectMember(Game);
  291. SandboxObject.ClearBlacklist();
  292. SandboxObject.ProtectMember(script.Destroy);
  293. --I think most/all userdata share this (Game.Destroy,Workspace.Destroy, Workspace.BasePlate.Destroy,
  294. --script.Parent.Destroy, Instance.new("Part",Workspace).Destroy...)
  295. --SandboxObject.DisableEvents(true);
  296. Game.Players.PlayerAdded:connect(function(player)
  297. player.CharacterAdded:wait();
  298. SandboxObject.ProtectMember(player);
  299. end);
  300. SandboxObject.Run(function()
  301. Game.Players.PlayerAdded:connect(function(player)
  302. player:Destroy();
  303. end);
  304. --[[Game.Workspace.BasePlate.Touched:connect(function(p)
  305. local player_string=tostring(p.Parent);
  306. if Game.Players:FindFirstChild(player_string) then
  307. Game.Players[player_string]:Destroy();
  308. end
  309. end);]]
  310. --[[Game.Workspace.BasePlate.Touched:connect(function(p)
  311. pcall(function()
  312. p.Parent:Destroy()
  313. end);
  314. end);]]
  315.  
  316. local DestroyThis=function(p)
  317. pcall(function()
  318. p.Parent.Parent.Parent.Players.Player1:Destroy();
  319. end);
  320. end
  321. Game.Workspace.BasePlate.Touched:connect(DestroyThis);
  322.  
  323. --[[local a=setmetatable({},{});
  324. setmetatable(a,{__index=function(t,k)
  325. if k==1 then
  326. return script
  327. end
  328. end});
  329. print(a[1]);]]
  330. print(type(Game.GetFullName));
  331. --[[local a=function(g)
  332. g:Destroy();
  333. end
  334. a(Game.Workspace.BasePlate);]]
  335.  
  336.  
  337. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement