Jump to content

Deal with loadstring & enviroments


Overkillz

Recommended Posts

Hello dear community. Some time ago I started to work with loadstring and load functions. Nowadays I've realized that I got some issue with load so I decided to chose loadstring to reach my purpose. I have read some @IIYAMA topics and other people which had a similar problem some months/years ago, but sadly. I couldn't get the solution.

I have currently 2 resource. One is to create the maps, gets the map files, scripts, store them in the cache ...etc, lets call it (MapCreator) and the other one is to execute the map scripts, lets call it (scriptRunner). Well, I have done a little version of them to make it more understable.

I have found some problems with the mapCreator. to be clear, the whileIsEOF part. If I comment this part, the script runs correctly, but If I leave them uncommented, it doesn't read the script file.

--##MapCreator
local cachePath = "tempCache"
local scriptList = {"client.lua","water.lua","shaders.lua"}

function scriptRead()
	for i,mapScripts in ipairs(scriptList) do
		local scriptData
		local file = fileOpen(cachePath.."/"..mapScripts)
		if not file then
			error("Not file found")
			return
		end
		while not fileIsEOF(file) do
			scriptData = fileRead(file, fileGetSize(file))
		end
		fileClose(file)
		if i == #scriptList then
			exports.scriptLoaderResource:runScript(scriptData, true)
			return
		end
		exports.scriptLoaderResource:runScript(scriptData, false)
	end
end

Well, on the other hand, at (scriptRunner). I have read that enviroments bring you more security to prevent that other people stole or execute your scripts.

Im not sure if Im following the right way to run map scripts (Obyously no. Because sometimes in a X map. the song is executed like 3 times at the sime time) and Im not sure if the enviroments must be created like in my code.

local enviroment = {}

local blockedFunctions = {
	["enviroment"] = true,
	["newEnviroment"] = true,
}

function newEnviroment()
	enviroment = {}
	for name, valor in pairs(_G) do
		if not blockedFunctions[name] then
			enviroment[name] = valor
		end
	end
end

function runScript(scriptData, executeIt)
	newEnviroment()
	
	local loaded = loadstring(script)
	local ex = pcall(loaded)
	if ex then 
		if executeIt then
			loaded()
			triggerEvent("onClientResourceStart", resourceRoot, getResourceFromName("scriptLoaderResource"))
			outputDebugString("Scripts Executed")
		end
	else
		outputDebugString("Not Executed")
	end
end

I hope you can help me to fix the issue that if the map has a script that play a song, it doesn't get execute more than 3 times (And not, on the script is only executed one time. It is a problem of my code.).And, can you tell me if the enviroments must be created like this ? I have to clean them or something when I change the map.

And, how can I stop the runned scripts? I know that just setting the loaded variable as nil it should work but.  How to deal properly with it.

PS: the scripts that are inside the array scriptList are correctly coded.

Thanks for reading. Best regards.

Edited by Overkillz
Link to comment
  • Moderators

@Overkillz

For each script you run, you are destroying the previous environment by overwriting the 'enviroment' variable. That does not go well.

 

To be honest I haven't worked much with environments.

 

But you will need to use setfenv function for cutting off the environment.

Here a tutorial about the way to go, on the very BOTTOM of this page:

http://lua-users.org/wiki/EnvironmentsTutorial

 

A recommendation, based on just things that might be handy.

Merge all the strings/scripts together and wrap each of them with:

do
  --script
end

 

This will make sure that all local variables stay in their scope.

 

An IIFE will do as well: (in case of an overflow of 200+ local variables in total)

(function () 
    -- script
end)()

 

Link to comment
  • Moderators
21 minutes ago, Overkillz said:

ow can I merge all of them with

More or less the same way

 

scriptDataString .. "\n do \n" .. loopData .. "\n end \n"

 

 

Edited by IIYAMA
  • Like 1
Link to comment

Well, I have just came at home to try it and Im still having the same issue with for example, playSound.

The sound is executed more than one time, even if the scripts data are merged and I cant figure out why does it happen.

local cachePath = "tempCache"
local scriptList = {"client.lua","water.lua","shaders.lua"}

function scriptRead()
	local scriptData = ""
	for i,mapScripts in ipairs(scriptList) do
		local file = fileOpen(cachePath.."/"..mapScripts)
		if not file then
			error("Not file found")
			return
		end
		while not fileIsEOF(file) do
			scriptData = scriptData .. "\n do \n" .. fileRead(file, fileGetSize(file)) .. "\n end \n"
		end
		fileClose(file)
		if i == #scriptList then
			exports.scriptLoaderResource:runScript(scriptData, true)
		end
	end
end
function runScript(scriptData, executeIt)
	local loaded = loadstring(script)
	local ex = pcall(loaded)
	if ex then 
		if executeIt then
			loaded()
			triggerEvent("onClientResourceStart", resourceRoot, getResourceFromName("scriptLoaderResource"))
		end
	end
end

Probably the issue is on the triggerEvent, but I don't use it, the scripts are not executed.

Weird no ? Regards.

Link to comment
  • Moderators
7 hours ago, Overkillz said:

Well, I have just came at home to try it and Im still having the same issue with for example, playSound.

The sound is executed more than one time, even if the scripts data are merged and I cant figure out why does it happen.

local cachePath = "tempCache"
local scriptList = {"client.lua","water.lua","shaders.lua"}

function scriptRead()
	local scriptData = ""
	for i,mapScripts in ipairs(scriptList) do
		local file = fileOpen(cachePath.."/"..mapScripts)
		if not file then
			error("Not file found")
			return
		end
		while not fileIsEOF(file) do
			scriptData = scriptData .. " do " .. fileRead(file, fileGetSize(file)) .. " end "
		end
		fileClose(file)
		if i == #scriptList then
			exports.scriptLoaderResource:runScript(scriptData, true)
		end
	end
end
function runScript(scriptData, executeIt)
	local loaded = loadstring(script)
	local ex = pcall(loaded)
	if ex then 
		if executeIt then
			loaded()
			triggerEvent("onClientResourceStart", resourceRoot, getResourceFromName("scriptLoaderResource"))
		end
	end
end

Probably the issue is on the triggerEvent, but I don't use it, the scripts are not executed.

Weird no ? Regards.

Not really, pcall is already running the code. After that you are calling it without pcall again.

pcall(loaded) -- call 1

loaded() -- call 2

 

 

Note: pcall executions do hide most of the debug information. So even if you did debug it, you wouldn't have noticed it.

 

It is recommended to develop without pcall and when you are finish use it.

 

I also saw you use triggerEvent on the resourceRoot. It is better if you do NOT use that at first. It is very important to understand that you can't just use the same element for all the resources. It will go bad...

If you really want to use that.

Top:

local resourceRoot = createElement("resourceRootFaked")

 

Bottom:

triggerEvent("onClientResourceStart", resourceRoot, getResourceFromName("scriptLoaderResource"))

 

 

And try to use the setfenv function as I have recommended before. In your current code all the global variables are exposed.

Edited by IIYAMA
  • Like 1
Link to comment

@IIYAMA

Well, as far as I read pcall executes the string and if it detecs an error it just ignores it.

So, the code should be like this

local resourceRoot = createElement("resourceRootFaked")
local enviroment = {}

local blockedFunctions = {
	["enviroment"] = true,
	["newEnviroment"] = true,
}

function newEnviroment()
	enviroment = {}
	for name, valor in pairs(_G) do
		if not blockedFunctions[name] then
			enviroment[name] = valor
		end
	end
end

function runScript(scriptData, executeIt)
	newEnviroment()

	local loaded = loadstring(script)
	local ex = pcall(loaded)
	setfenv(ex, enviroment)
	if ex then 
		if executeIt then
			triggerEvent("onClientResourceStart", resourceRoot, getResourceFromName("scriptLoaderResource"))
		end
	end
end

 

  • Like 1
Link to comment
  • Scripting Moderators
newEnv = {}
setmetatable(newEnv,{__index=_G})
local fnc = loadstring(code)
if fnc then
  setfenv(fnc,newEnv)
  fnc()
end
   

Maybe this can be better for new enviroment

  • Like 1
Link to comment

Well, I have done some test and looks that it is getting a better look.

Btw, which one should be the way to test if my code is vulnerable to be read by those codes ?

Tried with fileOpen, fileread ...etc but not success. (Obyously, the enviroment is removed during this test.)

  • Like 1
Link to comment
  • Moderators
1 hour ago, Overkillz said:

Well, I have done some test and looks that it is getting a better look.

Btw, which one should be the way to test if my code is vulnerable to be read by those codes ?

Tried with fileOpen, fileread ...etc but not success. (Obyously, the enviroment is removed during this test.)

You mean protection from stealers?

Or access to files from different resources?

  • Like 1
Link to comment
On 17/04/2019 at 20:22, Overkillz said:

Hello dear community. Some time ago I started to work with loadstring and load functions. Nowadays I've realized that I got some issue with load so I decided to chose loadstring to reach my purpose. I have read some @IIYAMA topics and other people which had a similar problem some months/years ago, but sadly. I couldn't get the solution.

I have currently 2 resource. One is to create the maps, gets the map files, scripts, store them in the cache ...etc, lets call it (MapCreator) and the other one is to execute the map scripts, lets call it (scriptRunner). Well, I have done a little version of them to make it more understable.

I have found some problems with the mapCreator. to be clear, the whileIsEOF part. If I comment this part, the script runs correctly, but If I leave them uncommented, it doesn't read the script file.

--##MapCreator
local cachePath = "tempCache"
local scriptList = {"client.lua","water.lua","shaders.lua"}

function scriptRead()
	for i,mapScripts in ipairs(scriptList) do
		local scriptData
		local file = fileOpen(cachePath.."/"..mapScripts)
		if not file then
			error("Not file found")
			return
		end
		while not fileIsEOF(file) do
			scriptData = fileRead(file, fileGetSize(file))
		end
		fileClose(file)
		if i == #scriptList then
			exports.scriptLoaderResource:runScript(scriptData, true)
			return
		end
		exports.scriptLoaderResource:runScript(scriptData, false)
	end
end

Well, on the other hand, at (scriptRunner). I have read that enviroments bring you more security to prevent that other people stole or execute your scripts.

Im not sure if Im following the right way to run map scripts (Obyously no. Because sometimes in a X map. the song is executed like 3 times at the sime time) and Im not sure if the enviroments must be created like in my code.

local enviroment = {}

local blockedFunctions = {
	["enviroment"] = true,
	["newEnviroment"] = true,
}

function newEnviroment()
	enviroment = {}
	for name, valor in pairs(_G) do
		if not blockedFunctions[name] then
			enviroment[name] = valor
		end
	end
end

function runScript(scriptData, executeIt)
	newEnviroment()
	
	local loaded = loadstring(script)
	local ex = pcall(loaded)
	if ex then 
		if executeIt then
			loaded()
			triggerEvent("onClientResourceStart", resourceRoot, getResourceFromName("scriptLoaderResource"))
			outputDebugString("Scripts Executed")
		end
	else
		outputDebugString("Not Executed")
	end
end

I hope you can help me to fix the issue that if the map has a script that play a song, it doesn't get execute more than 3 times (And not, on the script is only executed one time. It is a problem of my code.).And, can you tell me if the enviroments must be created like this ? I have to clean them or something when I change the map.

And, how can I stop the runned scripts? I know that just setting the loaded variable as nil it should work but.  How to deal properly with it.

PS: the scripts that are inside the array scriptList are correctly coded.

Thanks for reading. Best regards.

https://github.com/Bonsai11/Multigamemode 

this resource might help you as it deals with loading map script files for specific player ( only client sided but might give you an idea on how to do it).

  • CCS_wrapper

This resources handles the loading of map scripts. Only clientside scripts are supported.

CSS_wrapper is what you are looking for !

  • Like 1
Link to comment
  • Moderators
13 minutes ago, Overkillz said:

@IIYAMA

I mean protection from the loaded string. Which one should be the way/ways that people can try to steal something (I don't care about dff, txd ...etc | Only scripts) 

There is a way without reading/writing files and it is not that hard to master. But the downside of that method is that it will be loaded already in your ram as a string, even if you do not use it.

 

 

-- the script should be between those [[ ]], which is a multiline string.
local file = [[

local variable = 1 
function example ()
	iprint(variable)
end

example()

]]

function getFile (securityKey)
	if securityKey == "4GDT^*#46345" then -- < An important layer of security. Compiled scripts might be not be viewed, but that doesn't mean they can't be loaded.
		return file
	end
end

 

<export function="getFile" type="client"/>

 

call ( getResourceFromName ( "resourceName" ), "getFile", "4GDT^*#46345" )

 

 

 

 

Link to comment
On 18/04/2019 at 19:34, Overkillz said:

but what about if I want to block some variables/arrays ?

about variables/arrays, you can check the string with patterns before performing the loadstring() on it, check http://lua-users.org/wiki/PatternsTutorial for more information about it.

for functions and events regulating, there is the wrapping way, which you can find in Bonsai's MGM, and the other way which i prefer to use, by adding debug hooks. it's extremely effective and gives you some extra abilities, check https://wiki.multitheftauto.com/wiki/addDebugHook

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...