没有简单的答案(经过这么多年,仍然如此)。我将详细说明替代方案。
1 考古学教授的回答(还有一点拆解):
这个古老问题的答案在于原始问题。即,luadec。可能是那时它处于停机状态,但是,至少到目前为止,有一个更新版本,它可以处理 lua 5.1-.3。
此外,它string.dump
提供的不是完整的胡言乱语,它以机器指令的原始字节提供程序代码,您可以看到带有luac -l -p <filename>
. vm 代码没有很好地记录,但人们确实在这里整理了一些东西。Luajit 在自己的指令集上有更好的文档。
从 vm 指令重建代码是 luadec 所做的。理论上,您也可以将自己的指令拼接到转储字符串中。
然而,无论你对字节码做了什么技巧,它都会遇到不同解释器之间的不兼容,包括 lua 本身的不同版本。
2.实际做X
将函数转换为字符串是一种非常特殊的愿望(除非您正在生成代码,在这种情况下,您首先已经有了字符串)。
“记录并注入代码”确实是相当一般的 X,这可能保证 Y 得到解决。但是单一的案例可以由单一的措施覆盖。Lua 是一种非常灵活的语言,例如,您可以x
通过将其设为对象来跟踪示例中值的流动:
local to2number = tonumber
tonumber= function(o)
local r= to2number(o)
if not r then
local m= getmetatable(o)
if m and m.__tonumber then
r=m.__tonumber(o)
end
end
return r
end
local number
number={
new=function(n)
return setmetatable({n},number)
end,
__add=function(me,other)
print("I'm "..tostring(me).." and I'm being added to "..tostring(other))
local o=tonumber(other)
return number.new(me[1]+o)
end,
__tonumber=function(me) return me[1] end,
__tostring=function(me) return tostring(me[1]) end,
}
test(number.new(4), number.new(10))
如上例所示,您可以通过更改函数的环境来注入行为。也就是说,我已经重新定义了 global function tonumber
。您可能希望在不同的环境中完全打包函数:
local test = function() print"hello" end
local newenv={print=function(s) print(s..'world') end}
setfenv(test,newenv)--this is lua 5.1, luajit, good luck with upvalues
local test = load(string.dump(test),nil,nil,newenv)--this is lua 5.2-5.3, good luck with upvalues
test()
对于旧版本,您必须处理可能引用您尝试重新定义的全局函数的上值。对于较新的版本,您必须处理在转储加载过程中丢失的上值。
3.读取文件
最后,正如其他人所说,如果您可以访问源代码,您可以尝试从中找到函数定义。除非它是单个函数定义或单个返回文件,否则该任务最终可能等同于重新实现 lua 解析器。其中不止一个,但他们并没有考虑到这样的功能,因此可能需要一些工作来重新利用他们的代码。
如果所有的函数都是自己定义的,而且你愿意稍微克制一下,你可以再次使用lua metatables,在编码阶段解决问题:
local def=function(code,env)
env=env or _ENV
local compiled,q=load("return "..code,nil,nil,env)
if not compiled then error(q) end
local f=compiled()
return setmetatable({code=code},{__call=function(me,...) return f(...) end})
end
local test=def[[function(a,b)
return a+b
end]]
print(test(2,3))
然而,定义上值将是棘手的。