Jump to content

loadstring and wrappers


Bonsai

Recommended Posts

  • Replies 57
  • Created
  • Last Reply

Top Posters In This Topic

So its just for security purposes but does not affect if its working?

Sounds important, but in my opinion maps with compiled scripts shouldn't be uploaded anyway.

There are things possible much worse then changing element data or triggering events and it doesn't even have to be in acl.

Link to comment

Using

setmetatable(env, {__index = _G}) 

means if a variable/function doesn't exist in the environment, then search in the global environment (of the resource), which means if you are trying to disallow triggerServerEvent, then a map can simply have script that does this:

triggerServerEvent = nil 

and the map will have access to the real triggerServerEvent function.

To fix this problem, you should copy variables instead:

function copyTable(tbl) 
   local t = {} 
   for k, v in pairs(tbl) do 
       if(type(v) ~= "table")then 
           t[k] = v 
        else 
            t[k] = copyTable(v) 
        end 
    end 
    return t 
end 
  
-- Define env as an empty table before this. 
-- Make sure to use pairs, not ipairs. 
for k, v in pairs(_G) do 
    if(v ~= _G)then 
        if(type(v) ~= "table")then 
             env[k] = v 
        else 
            env[k] = copyTable(v) 
        end 
    end 
end 
env._G = env 
  

The only problem left is that source, resourceRoot, localPlayer, resource etc are global variables and are only set in the default environment (resource global environment), so you need to do to so that if you are trying to get/set the value of any of those variables, then you should access the global environment (of the resource) instead, but at the same time, your environment should not have access to that environment.

This can be done by taking advantage of 'variable capturing', which means refrences to variables are copied into functions that are defined in a scope (for example if a function is defined in another function, then that function will have access to variables that the function that created it, has access to too), for example:

  
function f(v) 
    local d = 23 
    local f2 =  
        function() 
            outputChatBox(d) -- will output 23 
            outputChatBox(v) -- will output the value of 'v' from the function v 
        end 
end 
  

We can take advantage of this method to give metamethod __index and __newindex of the new environment access to global environment without giving the actual environment access to it.

An example of how the metamethod looks like (this should be called in the global environment before copying variables/functions):

  
setmetatable(env, 
{ 
    __index =  
        function(t, key) 
            local r = rawget(_G, "_GET_GLOBAL")(key) 
            if(r)then 
                return r 
            end 
            return rawget(t, key) 
        end, 
    __newindex =  
        function(t, key, value) 
            local r = rawget(_G, "_SET_GLOBAL")(key, value) 
            if(not r)then 
                rawset(t, key, value) 
            end 
        end 
}) 
  

_GET_GLOBAL and _SET_GLOBAL are functions defined in the global environment, and what they simply do is check if the key you called it with is "source", "resource", "resourceRoot" etc, then return the value, or set the value and return true; otherwise get the value from the new environment or set it in the new environment.

The reason you need __newindex as well is because __index is only called when trying to access a variable that doesn't exist (we dont want source, resourceRoot etc to exist in the new environment), and if a script in a map for example has:

source = aValue 

and you dont have __newindex set, then source will be saved as a new variable in the environment, and __index will not be called when trying to access source, and source will always have the same value until you manually override it again.

The order you should do everything in is:

Create a empty table (env in this case).

Set the metamethods for __index and __newindex for it, to get/set values from global environment instead.

Loop the global environment and copy variables.

Set the environments _G table to itself (env._G = env in this case).

(Set the environments debug table to nil, in this case env.debug = nil, to prevent a user from doing stuff that they should not able to do. Do the same for loadstring and load).

Load your code with loadstring and use setfenv on the returned function

Use pcall to call the function returned from loadstring.

Edited by Guest
Link to comment

Oh wow, thats one perfect answer :P

I did it a bit different, but its pretty much the same. And so far everything is working good.

Sometimes I get errors in debuglog caused by map scripts, but I guess that are general problems in those scripts,

not related to the loading progress. At least I didn't find a map yet thats impossible to finish because of missing marker, objects etc.

Thanks for your detailed answer.

Link to comment
Oh wow, thats one perfect answer :P

I did it a bit different, but its pretty much the same. And so far everything is working good.

Sometimes I get errors in debuglog caused by map scripts, but I guess that are general problems in those scripts,

not related to the loading progress. At least I didn't find a map yet thats impossible to finish because of missing marker, objects etc.

Thanks for your detailed answer.

Finally, its solved. :D

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...