目前网上的luajit反编译工具主要存在两款:
- Luajit-decomp: GitHub - bobsayshilol/luajit-decomp: LuaJIT decompiler
- Ljd: https://github.com/NightNord/ljd
总的来说,luajit-decomp比较稳定,但是反编译出来的代码可读性差,之所以稳定,是因为它先使用 luajit -bl 命令将字节码转换成汇编代码,再通过线性扫描汇编代码,转换成lua脚本代码。
Ljd是使用Python写的反编译工具,定义了luajit2.0完整的指令信息,并实现了所有的luajit字节码解析函数,解析出来的lua脚本可读性高
luajit-decomp
- 使用方式
1)下载后,将luajit字节码对应版本的luajit.exe、lua库的dll文件以及jit文件夹拷贝到luajit-decmp目录。
2)将待反编译的luajit字节码拷贝到当前目录,命名为test.lua
3)运行decoder_new.exe,生成test.asm、out.lua、out2.lua
4)test.asm是luajit使用-bl命令生成的汇编代码,out.lua是生成的反编译代码,out2.lua是优化后的反编译代码
- 实际测试
测试的lua代码如下,这是table.remove函数
function remove(t, pos)
CHECK_tab(t)
local len = #t
if pos == nil then
if len ~= 0 then
local old = t[len]
t[len] = nil
return old
end
else
CHECK_int(pos)
if pos >= 1 and pos <= len then
local old = t[pos]
for i=pos+1,len do
t[i-1]= t[i]
end
t[len] = nil
return old
end
end
end
2、反编译效果
1)生成的test.asm文件
– BYTECODE – test.lua:0-0
0001 GGET 2 0 ; "CHECK_tab"
0002 MOV 3 0
0003 CALL 2 1 2
0004 LEN 2 0
0005 ISNEP 1 0
0006 JMP 3 => 0014
0007 ISEQN 2 0 ; 0
0008 JMP 3 => 0034
0009 TGETV 3 0 2
0010 KPRI 4 0
0011 TSETV 4 0 2
0012 RET1 3 2
0013 JMP 3 => 0034
0014 => GGET 3 1 ; "CHECK_int"
0015 MOV 4 1
0016 CALL 3 1 2
0017 KSHORT 3 1
0018 ISGT 3 1
0019 JMP 3 => 0034
0020 ISGT 1 2
0021 JMP 3 => 0034
0022 TGETV 3 0 1
0023 ADDVN 4 1 1 ; 1
0024 MOV 5 2
0025 KSHORT 6 1
0026 FORI 4 => 0031
0027 => SUBVN 8 7 1 ; 1
0028 TGETV 9 0 7
0029 TSETV 9 0 8
0030 FORL 4 => 0027
0031 => KPRI 4 0
0032 TSETV 4 0 2
0033 RET1 3 2
0034 => RET0 0 1
2)生成的out.lua文件
function someFunc0()
var_0_3 = INPUT_VAR_0_
CHECK_tab(var_0_3)
LEN unhandled at 0004
if INPUT_VAR_1_ == nil then
for var_0_3, var_0_4 in (INPUT_VAR_0_ calling function) do --INPUT_VAR_0_ FORTEST-FORTEST
--jump to 0014 (if previous if statement is false) --0014 JMP-JMP
if CHECK_tab ~= 0 then
for var_0_3, var_0_4 in (INPUT_VAR_0_ calling function) do --INPUT_VAR_0_ FORTEST-FORTEST
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
var_0_4 = nil --var_0_4 PRIMITIVE-PRIMITIVE
INPUT_VAR_0_[CHECK_tab] = var_0_4
return unknown0
for var_0_3, var_0_4 in (INPUT_VAR_0_ calling function) do --INPUT_VAR_0_ FORTEST-FORTEST
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
--location 0014--0014 LOCATION-LOCATION
var_0_4 = INPUT_VAR_1_
CHECK_int(var_0_4)
var_0_3 = 1 --var_0_3 NUMBER-NUMBER
if var_0_3 <= INPUT_VAR_1_ then
for var_0_3, var_0_4 in (INPUT_VAR_0_ calling function) do --INPUT_VAR_0_ FORTEST-FORTEST
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
if INPUT_VAR_1_ <= CHECK_tab then
for var_0_3, var_0_4 in (INPUT_VAR_0_ calling function) do --INPUT_VAR_0_ FORTEST-FORTEST
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
var_0_4 = INPUT_VAR_1_ + 1 --var_0_4 NUMBER-NUMBER
var_0_5 = CHECK_tab
var_0_6 = 1 --var_0_6 NUMBER-NUMBER
for var_0_7 = var_0_4,var_0_5,var_0_6 do --location 0026, loop ends at 0031-1
var_0_8 = var_0_7 - 1 --var_0_8 NUMBER-NUMBER
INPUT_VAR_0_[var_0_8] = unknown2
end --location 0030, loops back to 0027-1
var_0_4 = nil --var_0_4 PRIMITIVE-PRIMITIVE
INPUT_VAR_0_[CHECK_tab] = var_0_4
return unknown1
--location 0034--0034 LOCATION-LOCATION
return
end
3)生成的out2.lua文件
function randomFunctiona (INPUT_VAR_0_,INPUT_VAR_1_)
var_0_3 = INPUT_VAR_0_
CHECK_tab(var_0_3)
LEN unhandled at 0004
if INPUT_VAR_1_ == nil then
--jump to 0014 (if previous if statement is false) --0014 JMP-JMP
if CHECK_tab ~= 0 then
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
var_0_4 = nil --var_0_4 PRIMITIVE-PRIMITIVE
INPUT_VAR_0_[CHECK_tab] = var_0_4
return unknown0
end
else
--location 0014--0014 LOCATION-LOCATION_
var_0_4 = INPUT_VAR_1_
CHECK_int(var_0_4)
var_0_3 = 1 --var_0_3 NUMBER-NUMBER
if var_0_3 <= INPUT_VAR_1_ then
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
if INPUT_VAR_1_ <= CHECK_tab then
--jump to 0034 (if previous if statement is false) --0034 JMP-JMP
var_0_4 = INPUT_VAR_1_ + 1 --var_0_4 NUMBER-NUMBER
var_0_5 = CHECK_tab
var_0_6 = 1 --var_0_6 NUMBER-NUMBER
for var_0_7 = var_0_4,var_0_5,var_0_6 do --location 0026, loop ends at 0031-1
var_0_8 = var_0_7 - 1 --var_0_8 NUMBER-NUMBER
INPUT_VAR_0_[var_0_8] = unknown2
end --location 0030, loops back to 0027-1
var_0_4 = nil --var_0_4 PRIMITIVE-PRIMITIVE
INPUT_VAR_0_[CHECK_tab] = var_0_4
return unknown1
end
end
end
return
end
可以看到luajit-decomp不能还原函数名,并且代码的可读性极差,并且实质上luajit-decomp只是将汇编代码转换成lua代码,所以依赖luajit解释器,并且解释器与字节码的版本要对应,否则反汇编的结果会不正确
Ljd
- 使用方法
下载后执行 Python main.py 字节码文件
2、反编译效果
function remove(slot0, slot1)
CHECK_tab(slot0)
slot2 = #slot0
if slot1 == nil then
if slot2 ~= 0 then
slot0[slot2] = nil
return slot0[slot2]
end
else
CHECK_int(slot1)
if 1 <= slot1 and slot1 <= slot2 then
slot3 = slot0[slot1]
for slot7 = slot1 + 1, slot2, 1 do
slot0[slot7 - 1] = slot0[slot7]
end
slot0[slot2] = nil
return slot3
end
end
return
end
可以看到,ljd可以恢复字节码文件中的函数名,库函数等符号信息,可读性高