高效反编译luac文件

本文涉及的产品
简介: 高效反编译luac文件

       对于游戏开发人员,有时候希望从一些游戏apk中反编译出源代码,进行学习,但是如果你触碰到法律边缘,那么你要非常小心。

      这篇文章,我针对一些用lua写客户端或者服务器的编译过的luac文件进行反编译,获取其源代码的过程。

      这里我不赘述如何反编译解压apk包的过程了,只说重点,在解压获取luac文件后,你应该是可以看到类似于这样的目录:

image.gif编辑      

这些文件是经过lua或者luajit编译后生成的字节码文件(后边我们会分析下它到底是经过lua还是luajit处理的)

我们需要知道的是lua手游有三种文件:lua,luac,luajit。

lua是明文代码,直接用记事本就能打开。

luac是lua编译后的字节码。

luajit是用的另一种对lua加密。

Luac文件格式

一个luac文件包含两部分:文件头和函数体

文件头格式

typedef struct {
    char signature[4];   //".lua"
    uchar version;
    uchar format;
    uchar endian;
    uchar size_int;
    uchar size_size_t;
    uchar size_Instruction;
    uchar size_lua_Number;
    uchar lua_num_valid;
    uchar luac_tail[0x6];
} GlobalHeader;

image.gif

第一个字段**signature**在lua.h头文件中有定义,它是LUA_SIGNATURE,取值为“\033Lua",其中,\033表示按键。LUA_SIGNATURE作为Luac文件开头的4字节,它是Luac的Magic Number,用来标识它为Luac字节码文件。Magic Number在各种二进制文件格式中比较常见,通过是特定文件的前几个字节,用来表示一种特定的文件格式。

version 字段表示Luac文件的格式版本,它的值对应于Lua编译的版本,对于5.2版本的Lua生成的Luac文件,它的值为0x52。

**format**字段是文件的格式标识,取值0代表official,表示它是官方定义的文件格式。这个字段的值不为0,表示这是一份经过修改的Luac文件格式,可能无法被官方的Lua虚拟机正常加载。

**endian**表示Luac使用的字节序。现在主流的计算机的字节序主要有小端序LittleEndian与大端序BigEndian。这个字段的取值为1的话表示为LittleEndian,为0则表示使用BigEndian。

**size_int**字段表示int类型所占的字节大小。size_size_t字段表示size_t类型所占的字节大小。这两个字段的存在,是为了兼容各种PC机与移动设备的处理器,以及它们的32位与64位版本,因为在特定的处理器上,这两个数据类型所占的字节大小是不同的。

**size_Instruction**字段表示Luac字节码的代码块中,一条指令的大小。目前,指令Instruction所占用的大小为固定的4字节,也就表示Luac使用等长的指令格式,这显然为存储与反编译Luac指令带来了便利。

**size_lua_Number**字段标识lua_Number类型的数据大小。lua_Number表示Lua中的Number类型,它可以存放整型与浮点型。在Lua代码中,它使用LUA_NUMBER表示,它的大小取值大小取决于Lua中使用的浮点数据类型与大小,对于单精度浮点来说,LUA_NUMBER被定义为float,即32位大小,对于双精度浮点来说,它被定义为double,表示64位长度。目前,在macOS系统上编译的Lua,它的大小为64位长度。

**lua_num_valid**字段通常为0,用来确定lua_Number类型能否正常的工作。

**luac_tail**字段用来捕捉转换错误的数据。在Lua中它使用LUAC_TAIL表示,这是一段固定的字符串内容:"\x19\x93\r\n\x1a\n"。

在文件头后面,紧接着的是函数体部分。一个Luac文件中,位于最上面的是一个顶层的函数体,函数体中可以包含多个子函数,子函数可以是嵌套函数、也可以是闭包,它们由常量、代码指令、Upvalue、行号、局部变量等信息组成。

HxD查看lua是否经过加密编译

luac和luajit同样是.luac后缀,但是文件头不同,对其所使用的反编译方法也不同,所以需要特别注意。

luac文件头为:0x1B 0x4C 0x75 0x61 0x51

luacjit文件头为:文件头是0x1B 0x4C 0x4A

打开so文件(一般是文件大小最大的那个(不做更改的话是libcocos2dlua.so),在so文件里搜字符串有无lua就知道是不是)

比如我们使用objdump反汇编出来重定向到文件1中: objdump -S libcocos2dlua.so >1

然后搜索你应该是能看到luaopen_jit字符,说明是用luajit编译的

image.gif编辑

我们选取其中的一个main.luac.32,使用工具HxD打开,

image.gif编辑

注意看,你的头部信息是这种0x1B 0x4C 0x4A开头的,所以我们推断他是luajit处理的文件。

接下来,我们可以直接使用python工具来导出对应的源码的

使用工具ljd

准备python3的环境,下载GitHub - zzwlpx/ljd: LuaJIT raw-bytecode decompiler

下载本地后,直接按照手册说明运行, 比如我想反编译main.luac.32

image.gif编辑 同样的其他文件也是类似这样的方式来反编译。

最终反编译所有的源码,便可以运行了。

有些文件在反编译过程中会报错如下:

$ python main.py  code/layer/GuideLayer.luac.32

Traceback (most recent call last):

 File "main.py", line 126, in <module>

   retval = main()

 File "main.py", line 105, in main

   ljd.ast.unwarper.unwarp(ast)

 File "C:\Users\PC\Downloads\ljd-master\ljd-master\ljd\ast\unwarper.py", line 36, in unwarp

   _run_step(_unwarp_ifs, node)

 File "C:\Users\PC\Downloads\ljd-master\ljd-master\ljd\ast\unwarper.py", line 43, in _run_step

   statements.contents = step(statements.contents, **kargs)

 File "C:\Users\PC\Downloads\ljd-master\ljd-master\ljd\ast\unwarper.py", line 161, in _unwarp_ifs

   _unwarp_if_statement(start, body, end, end)

 File "C:\Users\PC\Downloads\ljd-master\ljd-master\ljd\ast\unwarper.py", line 978, in _unwarp_if_statement

   assert isinstance(warp_out, nodes.UnconditionalWarp)

AssertionError

这个是由于debug模式下断言报错,我们可以加上-O参数来禁止debug模式:

$ python -O  main.py  code/layer/GuideLayer.luac.32

绝大多数文件都可以被成功转换,但部分文件仍然会收到如下报错:

Traceback (most recent call last):

 File "main.py", line 123, in <module>

   retval = main()

 File "main.py", line 77, in main

   header, prototype = ljd.rawdump.parser.parse(file_in)

 File "C:\Users\PC\Downloads\ljd-master\ljd\ljd\rawdump\parser.py", line 34, in parse

   r = r and _read_prototypes(parser, parser.prototypes)

 File "C:\Users\PC\Downloads\ljd-master\ljd\ljd\rawdump\parser.py", line 72, in _read_prototypes

   if not ljd.rawdump.prototype.read(state, prototype):

 File "C:\Users\PC\Downloads\ljd-master\ljd\ljd\rawdump\prototype.py", line 51, in read

   r = r and _read_instructions(parser, prototype)

 File "C:\Users\PC\Downloads\ljd-master\ljd\ljd\rawdump\prototype.py", line 127, in _read_instructions

   instruction = ljd.rawdump.code.read(parser)

 File "C:\Users\PC\Downloads\ljd-master\ljd\ljd\rawdump\code.py", line 179, in read

   instruction_class = instructions.UNKN  # @UndefinedVariable

AttributeError: module 'ljd.bytecode.instructions' has no attribute 'UNKN'

 

使用luajit-decompiler

https://download.csdn.net/download/pbymw8iwm/88222584

该工具的使用方法和ljd相差无几,基本上所有的luac文件都可以被还原lua文件。

相关文章
|
1月前
|
安全 Java 图形学
Unity3D 导出的apk进行混淆加固、保护与优化原理(防止反编译)
Unity3D 导出的apk进行混淆加固、保护与优化原理(防止反编译)
44 0
|
自然语言处理
《编译与反编译技术实战 》一3.4 本章小结
词法分析是编译过程的第一个阶段,负责对源程序进行扫描,按照源程序的构词规则识别出一个个单词符号。编译过程中执行词法分析的程序称为词法分析器。本章介绍了构造词法分析器的两种方法:一种是基于状态转换图的词法分析器的手工实现,另一种是利用词法分析程序的自动生成工具LEX的词法分析器的自动实现。
1740 0
《编译与反编译技术实战 》一1.9 动态分析工具TEMU
TEMU是动态分析工具BitBlaze的一个组件,是一个基于系统仿真器QEMU开发的动态二进制分析工具,以QEMU为基础运行一个完整的系统(包括操作系统和应用程序),并对二进制代码的执行进行跟踪和分析。
1966 0

热门文章

最新文章