Lua程序逆向之Luajit字节码与反汇编

作者:非虫

Luajit的字节码设计与指令的反汇编有很多值得学习的地方。Luajit除了将Lua原生40条左右的指令扩展到了93条(Luajit版本2.0.5)外,还更改了字节码中Opcode与操作数的排列方式,可以说,Luajit使用了一种完全全新的方式来编译与执行Lua程序。经过处理后的Luajit程序,字节码的编码实现更加简单,执行效率也比原生Luac指令更加高效。

指令格式分析

Luajit很多情况下需要与系统底层打交道,为了方便开发人员扩展与使用Luajit,在指令的设计细节上,Luajit官方提供了一份完整的指令参考文档。地址是:http://wiki.luajit.org/Bytecode-2.0。文档中详细说明了指令的编码格式与各条指令的含义。

首先是指令的编码,Luajit指令同样采用等长的32位,指令分为Opcode与操作数域两个部分,则每个域占用8字节,如下所示:

这样做的好处显而易见,在处理32位指令数据时,对于每次只能处理8位的处理器来说,这种对齐后的优化,会减少处理器取指令时的运算周期,提高了指令的执行效率。Luajit只支持ABC与AD两种指令编码形式,其中,A、B、C各占8位,D占用16位。在编写解码程序时,代码部分比起Luac会简单许多。

Luajit OpCode

根据定义规则,每条指令最多拥有3个操作数,最少拥有1个操作数。指令的定义可以在Luajit源码的lj_bc.h头文件中找到。指令的声明部分采用宏定义,片断如下:

所有的指令都使用BCOp表示,BCDEF(BCENUM)经过宏展开后,会声明每一条LuaJit指令。从声明中可以看出,指令由以下五部分组成:

  1. name。指令的名称,展开后指令名如BC_ISLT、BC_ADDVV。

  2. ma。指令第一个操作数域,展开后是一个BCMode类型常量。

  3. mb。指令第二个操作数域,展开后是一个BCMode类型常量。

  4. mc。指令第三个操作数域,展开后是一个BCMode类型常量。

  5. mt。指令的类型,展开后是一个一个MMS类型常量。

宏声明中的“___”展开后是BCM___,它被定义为BCMnone,即这个域为空,它是BCMode的一部分,稍后再讲。

指令列表中,有些指令有添加上一个或多个字符的后缀,来标识指令操作数的类型。它们的取值包括:

  • V variable slot。变量槽。

  • S string constant。字符串常量。

  • N number constant。数值常量。

  • P primitive type。原始类型。

  • B unsigned byte literal。无符号字节字面量。

  • M multiple arguments/results。多参数与返回值。

除了后缀外,部分指令还会有一些约定俗成的前缀,用来标识指令操作的目标数据的类型。例如:

  • T table。表。

  • F function。函数。

  • U UpValue。上值。

  • K constant。常量。

  • G global。全局。

例如,指令USETS是为一个UpValue设置字符串值;指令TGETV是获取一个表结构中指定索引的数据。

BCMode

ma、mb、mc展开后是一个BCMode类型常量。它们的定义如下:

当这3个标志的值都不为BCMnone时,表示当前指令使用三个操作数,例如ADDVV指令声明如下:

展开后,变成了:

即3个操作数都有用到,对于指令0xbbccaa1e,解析它可得知,最低8位0x1e表示为ADDVV指令,并且操作数A = 0xaa,B = 0xbb,C = 0xcc。

对于少于3个操作数的情况,即ma、mb、mc中有1个或2个被设置成BCMnone,这种情况即为AD模式,如果只有一个操作数,则取A部分即可,如果有两个操作数,则取指令高16位为CD作为指令的第二个操作数。如指令0x10047,0x47表示它为RET0指令,它的指令声明如下:

可见,其mb为BCMnone,,表示第二个操作数不占位,即第三个操作数可以与第二合并为CD。此时,第一个参数值A取值为0,第(二/三)个参数CD取值为1,即解析后的指令格式为“RET0 0 1”。

MMS

MMS为指令的类型,它在Luajit源码的lj_obj.h头文件中通过宏定义为如下:

展开后,定义如下:

它们的主要作用是将指令归类,辅助Luajit内部执行与调试时使用,对于指令的格式并没有影响,这里不再展开。

lj_bc_mode

Luajit将所有的指令模式BCMode与MMS组合,生成了一张表,它就是lj_bc_mode。这张表与Luac中的luaP_opmodes一样,主要用于辅助指令的解析工作。lj_bc_mode的定义是通过builddvm_lib.c中的emit_lib()函数执行宏展开的,当ctx->mode被定义为BUILD_bcdef时,会执行如下片断代码:

整个核心的开展由BCDEF(BCMODE)完成。展开后的代码片断如下:

这是一个被定义为每项大小为uint16_t类型,个数为93的数组。可以手工的计算它们生成的值。以ADDVV为例,计算如下:

当然,也可以使用代码将它们的值打印出来,如下所示:

输出如下

可以看到,与它们的格式相关,输出的效果与Luac中的luaP_opmodes一样,会有很多的项的值是相同的。

反汇编引擎实现

Luajit的安装目录下的share/luajit-2.0.5/jit目录中的bc.lua文件为Luajit提供的反汇编模块,可以使用它来完成Luajit字节码文件的反汇编工作。执行如下命令,可以查看hello.lua的指令信息:

当然,也可以使用它直接反汇编Lua代码生成指令信息,如下所示:

如果要查看已经生成的hello.luajit的指令信息,可以使用Luajit的-bl参数,执行如下命令,与上面luajit -jbc的输出是一样的:

bc.lua中提供了bcline()反汇编引擎来实现指令的反汇编,它基于lj_bc_mode返回的指令BCMode来生成ma、mb与mc,但没有经过移位处理,如果没Luajit的指令格式不太熟悉,可能不能马上理解它的含义。当然,编写指令解析时,也可以直接本地定义一份指令Opcode与模式之前的表,解析时不需要用到lj_bc_mode,并且解析速度更快,反汇编工具ljd就是这么干的。下面,我们为010 Editor编写反汇编引擎时,由于模板语法的限制,最终选择了结合它们两种的处理方法。

编写基本的Luajit.bt功能之前已经实现,这里主要集中在反汇编引擎InstructionRead()的实现上,由于指令中需要用到当前指令地址pc以及指令中访问同级常量表中的信息,因此,对Luajit.bt进行了之前Luac.bt一样的重构,将当前Proto中所有的指令Instruction封装成Instructions,然后内联声明到Proto中,如下所示:

这样做之后,可以通过parentof(parentof(inst))访问到指令所在的Proto信息,进行获取Proto中其他字段的信息。

反汇编引擎的实现分为以下几步:

  1. 获取指令BCOp,解析不同的指令。

  2. 解析与处理指令的参数,这里通过BCMode来完成。

  3. 字符串与跳转处理。达到更好的反汇编输出效果。

  4. 输出指令BCOp与操作数,完成指令反汇编引擎。

指令BCOp解析

解析指令的BCOp很简单,只需要取指令的最低8位即可,获取指令BCOp只需要如下一行代码:

这里的INSTRUCTION_OPCODES为声先声明好的指令枚举类型。获取指令BCOp后,需要处理指令的参数。010 Editor模板不支持定义的本地数组结构直接赋值,因此,只能声明一个数组后,一行行的赋值,比较尴尬,代码片断如下:

init_modes()需要在模板最外层,Luajit lj;声明前调用一次。然后在代码中就可以访问每一条指令对应的Mode了,编写代码如下:

参数处理

参数的处理不难,根据前面分析的规则,通过ma、mb、mc的值即可完成。首先,需要判断参数的个数是否为3个,然后,通过它来确定是ABC还是AD模式,代码如下:

获取参数个数后,就可以设置A、B、CD的值了。代码片断如下:

字符串与跳转处理

获取了A、B、CD的值后,并不能直接输出反汇编,因为,针对不同类型的指令操作数,它的取值可能需要进行处理。例如对于BCMstr、BCMtab、BCMfunc、BCMcdata类型的操作数,它表示的是一个ComplexConstant的索引值,需要到指令所在的Proto的ComplexConstant中取数据,而且取数据的索引值与需要从ComplexConstant表相反的方向进行获取,即如下的代码所示:

还有,针对BCMjump类型的操作数,它跳转的地址计算方法是当前操作数的值加上当前指令pc减去0xFFFF。

终上所述,可以写出指令操作数处理函数process_operand(),代码如下:

get_data_from_constants()的代码如下:

complex_constants_count与inst一起作为参数传递,而不是在process_operand()中计算获取,是因为该方法会被多次调用,这样做可以提高代码执行效率。

完成指令反汇编引擎

最终,完成指令的反汇编引擎代码如下:

使用010 Editor打开hello.luajit,并加载编写好的模板,效果如图所示: 

luajit_dis.jpg

完整的luajit.bt文件可以在这里找到:https://github.com/feicong/lua_re

原文:https://github.com/feicong/lua_re/blob/master/lua/lua_re4.md


发表评论

(必填)

(必填)

(以便回访)