如何为 Lua 的 VM/编译器重新编译已编译的字节码列表?

Posted

技术标签:

【中文标题】如何为 Lua 的 VM/编译器重新编译已编译的字节码列表?【英文标题】:How to re-compile a compiled bytecode listing for Lua's VM/compiler? 【发布时间】:2019-07-01 03:37:48 【问题描述】:

我正在尝试修改一款名为 Fun Run 2 的旧手机游戏的代码,作为我大学软件工程课程的一个独特研究项目。

该应用是使用 Corona SDK 制作的,因此使用 Lua 进行编程。因此,一旦我拿到 APK 并解压缩它,我需要解压缩 resource.car (Corona ARchive) 以访问 Lua 文件。所以我这样做了,并且该存档中的所有文件都是预编译的 .lu 文件。

为了修改 Lua 代码,我尝试的第一件事是使用 unluac/luadec 将 .lu 文件反编译为可读、可修改、可编译的源代码。我能够成功地对它们进行反编译、修改和重新编译,但是当它在游戏中实际执行修改后的 .lu 文件时,它因为尝试索引一个 nil 值而崩溃。我发现造成这种情况的原因是有一个叫做 upvalue 的东西,当从编译的 Lua 文件中剥离调试信息时,它是无法检索的,因此尝试反编译和重新编译 .lu 文件是行不通的上班。

所以我的下一个方法是使用luac -l 方法,它为 Lua 的虚拟机创建一个已编译字节码的列表。

luac -l lua.gameLogic.powerUpChance.lu 的结果是这样的

...
main <?:0,0> (11 instructions, 44 bytes at 025D7D80)
0+ params, 3 slots, 0 upvalues, 0 locals, 4 constants, 2 functions
    1   [-] NEWTABLE    0 0 0
    2   [-] GETGLOBAL   1 -1    ; require
    3   [-] LOADK       2 -2    ; "composer"
    4   [-] CALL        1 2 2
    5   [-] CLOSURE     2 0 ; 025D8280
    6   [-] MOVE        0 1
    7   [-] SETTABLE    0 -3 2  ; "selectRandomPowerUp" -
...

鉴于此说明列表,我知道我需要修改什么以及在哪里修改它,并且我这样做了。这让我想到了我的问题 - 我如何获取修改后的字节码列表并将其编译回已编译的 .lu 文件?这甚至可能吗?这种修改已编译 Lua 文件的方法是在浪费我的时间吗?

【问题讨论】:

请上传您的字节码文件。 @EgorSkriptunoff 给你,这是未修改的版本 - pastebin.com/gZjFsKDL 我问的是字节码二进制文件,而不是它的列表。 @EgorSkriptunoff 我错了,你在这里 - ufile.io/hu26d 好的,通常是没有调试信息的 Lua 5.1 x86 字节码。这个文件应该被反编译和重新编译没有问题。所有的上值都在里面描述(除了它们的名字),不需要额外的信息。显示反编译结果(检查反编译是否正确)。 【参考方案1】:

没有工具(至少我不知道)可以将字节码的字符串表示形式编译回正确的 Lua 字节码;主要是因为这不是要编辑的,而是作为检查 Lua 代码编译并可能对其进行优化的一种方式。

更好的解决方案是使用将字节码反编译为 Lua 代码的工具。使用哪种工具在很大程度上取决于您使用的 Lua 版本,但对于从 5.1 到 5.3 的任何东西,我都没有亲自尝试过 LuaDec,但应该可以完成工作。反编译源代码后,进行所有必要的修改并使用 luac 再次编译它,就像使用任何 Lua 文件一样。

希望这会有所帮助:)

【讨论】:

不幸的是,我尝试了这种方法,没有骰子 :( 我能够以这种方式反编译、修改和重新编译 Lua 文件,但在运行时我收到有关尝试访问 nil 值的错误。我发现这是因为局部变量有时是“upvalues”,由于某种原因无法看到/使用。更具体地说,在反编译之前,有“GETUPVAL”指令,当我重新编译时,它们变成“GETGLOBAL”指令. 我知道有可能解决这个问题,因为其他人已经使用这种方法成功修改了这个游戏的代码,但我不知道如何。 嗯...我看到了那里的问题,但很容易避免这个问题。您只需要确定哪些变量是上值,然后将它们作为局部变量添加到函数之外,因此函数会将它们作为上值。然后你应该能够string.dump 函数及其上值。 我明白了...找出哪些变量是上值很容易,但在这种情况下,如果上值被引用和使用,我将如何找出将本地声明为函数外部的内容所以? pastebin.com/bZAkELZa 在这种情况下,只需在函数前写入local _UPVALUE0_ 就足够了,并且应该将其编译为上值。但请记住只切换方法本身的字节码,而不是实际创建闭包的部分:) @xn 我建议寻找现有问题或为此创建一个新问题,因为它与您的原始问题越来越远。确保在使用时添加更多上下文,例如您如何准确地提取字节码、它是如何执行的、反编译代码的 /[ph]astebins/ 等。如果您不能将其归结为单个*** 的问题,你也可以在 lua subreddit 上问,它对问题的简洁和重点没有 SO 严格。【参考方案2】:

您使用的反编译器似乎无法正确反编译 for 循环。 以下片段:

if A1_3 == #A3_5 and A2_4 > 2 then
   for _FORV_9_ = 1, #A3_5 do
   end
   if _FOR_ then
      L5_7 = 11
   end
end

必须是这样的:

if A1_3 == # A3_5 and A2_4 > 2 then
   local R6 = true
   for k = 1, #A3_5 do
      if not A3_5[k].mainPlayer then
         if A3_5[k].x < A0_2.x + 1200 then
            R6 = false
         end
      end
   end
   if R6 then
      L5_7 = 11
   end
end

【讨论】:

以上是关于如何为 Lua 的 VM/编译器重新编译已编译的字节码列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何为自定义 CPU 创建 C 编译器?

如何为 MXE 交叉编译器编译 Berkeley DB 5.3.28?

Automake:如何为编译器命令添加前缀

如何为树莓派编译内核模块?

编译lua动态库

cmake - 如何为英特尔编译器设置不同的变量