高效反编译luac文件

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
函数计算FC,每月15万CU 3个月
简介: 高效反编译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文件。

相关文章
|
2月前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
67 2
|
自然语言处理
《编译与反编译技术实战 》一3.4 本章小结
词法分析是编译过程的第一个阶段,负责对源程序进行扫描,按照源程序的构词规则识别出一个个单词符号。编译过程中执行词法分析的程序称为词法分析器。本章介绍了构造词法分析器的两种方法:一种是基于状态转换图的词法分析器的手工实现,另一种是利用词法分析程序的自动生成工具LEX的词法分析器的自动实现。
1764 0
|
自然语言处理 安全 C语言
《编译与反编译技术实战 》一导读
“编译技术”是从事软件开发和信息安全相关工作的技术人员必须掌握的基础性技术,也是高等院校计算机科学与技术和软件专业的一门必修专业课,这是理论与实践结合非常强的领域,对提升开发人员的技术水平和大学生科学思维的养成、解决实际问题能力具有重要作用。
2015 0

热门文章

最新文章