作者:非虫
预估稿费:800RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
传送门
简介
在了解完了Luac字节码文件的整体结构后,让我们把目光聚焦,放到更具体的指令格式上。Luac字节码指令是整个Luac最精华、也是最具有学习意义的一部分,了解它的格式与OpCode相关的知识后,对于逆向分析Luac,会有事半功倍的效果,同时,也为自己开发一款虚拟机执行模板与引擎打下良好的理论基础。
指令格式分析
Luac指令在Lua中使用Instruction来表示,是一个32位大小的数值。在Luac.bt中,我们将其定义了为Inst结构体,回顾一下它的定义与读取函数:
typedef struct(int pc) {
local int pc_ = pc;
local uchar inst_sz = get_inst_sz();
if (inst_sz == 4) {
uint32 inst;
} else {
Warning("Error size_Instruction.");
}
} Inst ;
定义的每一条指令为uint32,这与ARM处理器等长的32位指令一样,但不同的是,Lua 5.2使用的指令只有40条,也就是说,要为其Luac编写反汇编引擎,比起ARM指令集,在工作量上要少出很多。
Luac指令完整由:OpCode、OpMode操作模式,以及不同模式下使用的不同的操作数组成。
官方5.2版本的Lua使用的指令有四种格式,使用OpMode表示,它的定义如下:
enum OpMode {iABC, iABx, iAsBx, iAx};
其中,i表示6位的OpCode;A表示一个8位的数据;B表示一个9位的数据,C表示一个9位的无符号数据;后面跟的x表示数据组合,如Bx表示B与C组合成18位的无符号数据,Ax表示A与B和C共同组成26位的无符号数据。sBx前的s表示是有符号数,即sBx是一个18位的有符号数。
ABC这些字节大小与起始位置的定义如下:
#define SIZE_C9
#define SIZE_B9
#define SIZE_Bx(SIZE_C + SIZE_B)
#define SIZE_A8
#define SIZE_Ax(SIZE_C + SIZE_B + SIZE_A)
#define SIZE_OP6
#define POS_OP0
#define POS_A(POS_OP + SIZE_OP)
#define POS_C(POS_A + SIZE_A)
#define POS_B(POS_C + SIZE_C)
#define POS_BxPOS_C
#define POS_AxPOS_A
从定义中可以看来,从位0开始,ABC的排列为A->C->B。
以小端序为例,完整的指令格式定义如下表所示:
先来看最低6位的OpCode,在Lua中,它使用枚举表示,5.2版本的Lua支持40条指令,它们的定义如下所示:
typedef enum {
/*----------------------------------------------------------------------
nameargsdescription
------------------------------------------------------------------------*/
OP_MOVE,/*A BR(A) := R(B)*/
OP_LOADK,/*A BxR(A) := Kst(Bx)*/
OP_LOADBOOL,/*A B CR(A) := (Bool)B; if (C) pc++*/
OP_LOADNIL,/*A BR(A) := ... := R(B) := nil*/
OP_GETUPVAL,/*A BR(A) := UpValue[B]*/
OP_GETGLOBAL,/*A BxR(A) := Gbl[Kst(Bx)]*/
OP_GETTABLE,/*A B CR(A) := R(B)[RK(C)]*/
OP_SETGLOBAL,/*A BxGbl[Kst(Bx)] := R(A)*/
OP_SETUPVAL,/*A BUpValue[B] := R(A)*/
OP_SETTABLE,/*A B CR(A)[RK(B)] := RK(C)*/
......