前言:推薦一個工具Luadec,此工具有個功能可以反編譯Lua代碼生成Lua字節碼,這篇博客使用了-dis 命令。
D:\luadec>luadec.exe luadec.exe: no input files given LuaDec by Hisham Muhammad Ongoing port to Lua 5.1 by Zsolt Sz. Sztupak (http://winmo.sztupy.hu) usage: luadec.exe [options] [filename]. Available options are: - process stdin -d output information for debugging the decompiler -dis don't decompile, just disassemble -f num decompile only num-th function (0=main block) -l LDS declare locals as defined by LDS -l2 LDS2 declare locals as defined by LDS2 -dg disable built-in local guessing -pg don't run just print out the LDS2 string used -a always declare all register as locals -- stop handling options
1.賦值語句
Lua有一個奇葩的賦值方式,先把值讀入寄存器,再賦值給變量,這使得 a,b=b,a;這種腳本可以交換a和b 的值。我們看下下面的代碼:
a=10; b=5; a,b=b,a; print(a); print(b); x,y= 0; print(x); print(y); --[[ D:\luadec>luadec.exe -dis test_exec.lua ; This file has been disassembled using luadec 2.0 standard by sztupy (http://l adec51.luaforge.net) ; Command line was: -dis test_exec.lua ; Name: ; Defined at line: 0 ; #Upvalues: 0 ; #Parameters: 0 ; Is_vararg: 2 ; Max Stack Size: 2 1 [-]: LOADK R0 K1 ; R0 := 10 2 [-]: SETGLOBAL R0 K0 ; a := R0 3 [-]: LOADK R0 K3 ; R0 := 5 4 [-]: SETGLOBAL R0 K2 ; b := R0 5 [-]: GETGLOBAL R0 K2 ; R0 := b 6 [-]: GETGLOBAL R1 K0 ; R1 := a 7 [-]: SETGLOBAL R1 K2 ; b := R1 8 [-]: SETGLOBAL R0 K0 ; a := R0 9 [-]: GETGLOBAL R0 K4 ; R0 := print 10 [-]: GETGLOBAL R1 K0 ; R1 := a 11 [-]: CALL R0 2 1 ; R0(R1) 12 [-]: GETGLOBAL R0 K4 ; R0 := print 13 [-]: GETGLOBAL R1 K2 ; R1 := b 14 [-]: CALL R0 2 1 ; R0(R1) 15 [-]: RETURN R0 1 ; return ]]--
注釋中是lua生成的字節碼,從字節碼的1~2行可以看出:給a賦值的操作,是通過一個R0的存儲區來操作的,可能是一個寄存器,也可能是其他的,5~8行字節碼可以看出,a和b的值,分別賦值給了R1和R0,然后R1和R0再分別賦值給b和a,以達到交換的目的。
2.Local變量
這個比較好理解,java代碼中有int a;這種定義語句,但是lua中不需要,這就導致一個問題,代碼塊中,如何申明一個臨時變量呢?lua是通過local這個關鍵字來做的。
i=20; x=10; if i < 20 then local x = 5; print(x) end if i < 20 then x = 5; print(x) end --[[ D:\luadec>luadec.exe -dis test_local.lua ; This file has been disassembled using luadec 2.0 standard by sztupy (http://lu adec51.luaforge.net) ; Command line was: -dis test_local.lua ; Name: ; Defined at line: 0 ; #Upvalues: 0 ; #Parameters: 0 ; Is_vararg: 2 ; Max Stack Size: 3 1 [-]: LOADK R0 K1 ; R0 := 20 2 [-]: SETGLOBAL R0 K0 ; i := R0 3 [-]: LOADK R0 K3 ; R0 := 10 4 [-]: SETGLOBAL R0 K2 ; x := R0 5 [-]: GETGLOBAL R0 K0 ; R0 := i 6 [-]: LT 0 R0 K1 ; if R0 >= 20 then PC := 12 7 [-]: JMP 12 ; PC := 12 8 [-]: LOADK R0 K4 ; R0 := 5 9 [-]: GETGLOBAL R1 K5 ; R1 := print 10 [-]: MOVE R2 R0 ; R2 := R0 11 [-]: CALL R1 2 1 ; R1(R2) 12 [-]: GETGLOBAL R0 K0 ; R0 := i 13 [-]: LT 0 R0 K1 ; if R0 >= 20 then PC := 20 14 [-]: JMP 20 ; PC := 20 15 [-]: LOADK R0 K4 ; R0 := 5 16 [-]: SETGLOBAL R0 K2 ; x := R0 17 [-]: GETGLOBAL R0 K5 ; R0 := print 18 [-]: GETGLOBAL R1 K2 ; R1 := x 19 [-]: CALL R0 2 1 ; R0(R1) 20 [-]: RETURN R0 1 ; return ]]--
可以在字節碼第8行和16行的區別看出,8行local語句使編譯器選擇了一個新的臨時變量,而在16行則使用了SETGLOBAL語句。不過這個和C語言系列的沒啥區別,不算是陷阱。