网上搜索好久,没有找到现成的luajit字节码的反编译工具。
最好的的是NightNord的ljd项目(https://github.com/NightNord/ljd)。但是最近一次提交是1年前。
下载下来之后尝试了下,报了IO Error:
I/O error while reading dump: Unexpected EOF while trying to read 13550132507160
1275869946770648659091 bytes
虽然luajit自身也有显示字节码的选项(luajit.exe -bl bytecodefile),但是显示结果相当难懂:
0001 GGET 0 0 ; "ti"
0002 LEN 0 0
0003 ADDVN 0 0 0 ; 1
0004 GGET 1 0 ; "ti"
0005 TNEW 2 0
0006 TSETV 2 1 0
0007 KSHORT 1 0
0008 ADDVN 1 1 0 ; 1
0009 GGET 2 0 ; "ti"
0010 TGETV 2 2 0
0011 TNEW 3 0
0012 TSETV 3 2 1
0013 GGET 2 0 ; "ti"
0014 TGETV 2 2 0
0015 TGETV 2 2 1
0016 KSTR 3 1 ; "鐔勭伅鍚庢湁濡瑰瓙鍦ㄧ敺瀵濇ゼ涓嬪ぇ鍠婂"~
经过努力,终于用python写出了解析luajit字节码dump文件的工具,解析结果为字典树。代码如下:
## according to Bytecode dump format: src/lj_bcdump.h, src/lj_bcwrite.c and other header files
import sys
import struct
import bytecode
ERR_SYNTAX = 'command syntax error'
ERR_EMPTY_FILE = 'empty file error'
ERR_FILE_FORMAT = 'file format incorrect or corrupted file'
ERR_SIGNATURE = 'file signature error'
ERR_UNSUPPORTED_VERSION = 'unsupported version of bytecode dump'
BCDUMP_F_BE = 0x01
BCDUMP_F_STRIP = 0x02
BCDUMP_F_FFI = 0x04
BCDUMP_KGC_CHILD = 0
BCDUMP_KGC_TAB = 1
BCDUMP_KGC_I64 = 2
BCDUMP_KGC_U64 = 3
BCDUMP_KGC_COMPLEX = 4
BCDUMP_KGC_STR = 5
BCDUMP_KTAB_NIL = 0
BCDUMP_KTAB_FALSE = 1
BCDUMP_KTAB_TRUE = 2
BCDUMP_KTAB_INT = 3
BCDUMP_KTAB_NUM = 4
BCDUMP_KTAB_STR = 5
PROTO_CHILD = 0x01 # Has child prototypes
PROTO_VARARG = 0x02 # Vararg function
PROTO_FFI = 0x04 # Uses BC_KCDATA for FFI datatypes.
class ContextStream:
def checkedSlice(self, offset, length):
self.buffer[offset]
if length > 0:
self.buffer[offset + length - 1]
return self.buffer[offset:offset + length]
def __init__(self, buffer, root):
self.buffer = buffer
self.offset = 0
self.root = root
def readULEB128(self):
result = 0
length = 1 # at least 1 byte
while self.buffer[self.offset + length - 1] >= 128:
length = length + 1
tmp = self.readChunk(length)
tmp = tmp[::-1]
for v in tmp:
result = result * 128 + (v & 0b1111111)
return result
def readByte(self):
oldOffset = self.offset
self.offset = self.offset + 1
return self.buffer[oldOffset]
def readChunk(self, length):
result = self.checkedSlice(self.offset, length)
self.skip(length)
return result
def peek(self):
return self.buffer[self.offset]
def skip(self, length):
self.offset = self.offset + length
def isBigEndian(self):
return (self.root['header']['flags'] & BCDUMP_F_BE) != 0
def isStripped(self):
return (self.root['header']['flags'] & BCDUMP_F_STRIP) != 0
def buildFloat64(self, highU32, lowU32):
if self.isBigEndian():
buffer = struct.pack('>II', highU32, lowU32)
result, = struct.unpack('>d', buffer)
return result
else:
buffer = struct.pack('<II', lowU32, highU32)
result, = struct.unpack('<d', buffer)
return result
def debug(obj):
print(repr(obj))
def construct_dump(dump, stream):
dump['_offset'] = stream.offset
header = {}
header['_parent'] = dump
dump['header'] = header
construct_header(header, stream)
protos = []
dump['protos'] = protos
while stream.peek() != 0:
proto = {}
protos.append(proto)
proto['_parent'] = dump
construct_proto(proto, stream)
dump['zero'] = stream.readByte()
if dump['zero'] != 0:
raise Exception(ERR_FILE_FORMAT)
dump['_length'] = stream.offset - dump['_offset']
#dump['_bytes'] = stream.checkedSlice(dump['_offset'], dump['_length']
def construct_header(header, stream):
header['_offset'] = stream.offset
header['ESC'] = stream.readByte()
header['L'] = stream.readByte()
header['J'] = stream.readByte()
signatureOK = header['ESC'] == 0x1b and header['L'] == 0x4c and header['J'] == 0x4a
if not signatureOK:
raise Exception(ERR_SIGNATURE)
header['version'] = stream.readByte()
if header['version'] != 1:
raise Exception(ERR_UNSUPPORTED_VERSION)
header['flags'] = stream.readULEB128()
if not stream.isStripped():
namelen = stream.readULEB128()
header['namelen'] = namelen
name = stream.readChunk(namelen)
header['name'] = name
header['_length'] = stream.offset - header['_offset']
#header['_bytes'] = stream.checkedSlice(header['_offset'], header['_length'])
def construct_proto(proto, stream):
proto['_offset'] = stream.offset
proto['length'] = stream.readULEB128()
pdata = {}
pdata['_parent'] = proto
proto['pdata'] = pdata
construct_pdata(pdata, stream)
proto['_length'] = stream.offset - proto['_offset']
#proto['_bytes'] = stream.checkedSlice(proto['_offset'], proto['_length'])
def construct_pdata(pdata, stream):
pdata['_offset'] = stream.offset
phead = {}
phead['_parent'] = pdata
pdata['phead'] = phead
construct_phead(phead, stream)
pdata['bcins'] = stream.readChunk(phead['numbc'] * 4)
pdata['uvdata'] = stream.readChunk(phead['numuv'] * 2)
kgcs = []
pdata['kgcs'] = kgcs
for i in range(phead['numkgc']):
kgc = {}
kgcs.append(kgc)
kgc['_parent'] = pdata
construct_kgc(kgc, stream)
knums = []
pdata['knums'] = knums
for i in range(phead['numkn']):
knum = {}
knums.append(knum)
knum['_parent'] = pdata
construct_knum(knum, stream)
dump = pdata['_parent']['_parent']
if not stream.isStripped():
pdata['debug'] = stream.readChunk(phead['debuglen'])
pdata['_length'] = stream.offset - pdata['_offset']
#pdata['_bytes'] = stream.checkedSlice(pdata['_offset'], pdata['_length'])
def construct_kgc(kgc, stream):
kgc['_offset'] = stream.offset
kgctype = stream.readULEB128()
kgc['kgctype'] = kgctype
if kgctype == BCDUMP_KGC_CHILD:
pass
elif kgctype == BCDUMP_KGC_TAB:
ktab = {}
ktab['_parent'] = kgc
kgc['ktab'] = ktab
construct_ktab(ktab, stream)
elif kgctype == BCDUMP_KGC_I64:
i64 = {}
i64['_parent'] = kgc
kgc['i64'] = i64
lo = stream.readULEB128()
i64['lo'] = lo
hi = stream.readULEB128()
i64['hi'] = hi
i64['value'] = lo + hi * (2 ** 32)
if i64['value'] >= (2 ** 63):
i64['value'] = i64['value'] - (2 ** 64)
elif kgctype == BCDUMP_KGC_U64:
u64 = {}
u64['_parent'] = kgc
kgc['u64'] = u64
lo = stream.readULEB128()
u64['lo'] = lo
hi = stream.readULEB128()
u64['hi'] = hi
u64['value'] = lo + hi * (2 ** 32)
elif kgctype == BCDUMP_KGC_COMPLEX:
complex = {}
complex['_parent'] = kgc
kgc['complex'] = complex
rlo = stream.readULEB128()
complex['rlo'] = rlo
rhi = stream.readULEB128()
complex['rhi'] = rhi
complex['real'] = stream.buildFloat64(rhi, rlo)
ilo = stream.readULEB128()
complex['ilo'] = ilo
ihi = stream.readULEB128()
complex['ihi'] = ihi
complex['image'] = stream.buildFloat64(ihi, ilo)
elif kgctype >= BCDUMP_KGC_STR:
strlen = kgctype - BCDUMP_KGC_STR
kgc['str'] = stream.readChunk(strlen)
kgc['_length'] = stream.offset - kgc['_offset']
kgc['_bytes'] = stream.checkedSlice(kgc['_offset'], kgc['_length'])
def construct_ktab(ktab, stream):
ktab['_offset'] = stream.offset
ktab['narray'] = stream.readULEB128()
ktab['nhash'] = stream.readULEB128()
karrays = []
ktab['karrays'] = karrays
for i in range(ktab['narray']):
karray = {}
karray['_parent'] = ktab
karrays.append(karray)
construct_karray(karray, stream)
khashes = []
ktab['khashes'] = khashes
for i in range(ktab['nhash']):
khash = {}
khash['_parent'] = ktab
khashes.append(khash)
construct_khash(khash, stream)
ktab['_length'] = stream.offset - ktab['_offset']
#ktab['_bytes'] = stream.checkedSlice(ktab['_offset'], ktab['_length'])
def construct_karray(karray, stream):
karray['_offset'] = stream.offset
ktabk = {}
ktabk['_parent'] = karray
karray['value'] = ktabk
construct_ktabk(ktabk, stream)
karray['_length'] = stream.offset - karray['_offset']
#karray['_bytes'] = stream.checkedSlice(karray['_offset'], karray['_length'])
def construct_khash(khash, stream):
khash['_offset'] = stream.offset
key = {}
key['_parent'] = khash
khash['key'] = key
construct_ktabk(key, stream)
value = {}
value['_parent'] = khash
khash['value'] = value
construct_ktabk(value, stream)
khash['_length'] = stream.offset - khash['_offset']
khash['_bytes'] = stream.checkedSlice(khash['_offset'], khash['_length'])
def construct_ktabk(ktabk, stream):
ktabk['_offset'] = stream.offset
ktabtype = stream.readULEB128()
ktabk['ktabtype'] = ktabtype
if ktabtype == BCDUMP_KTAB_NIL:
ktabk['value'] = None
elif ktabtype == BCDUMP_KTAB_FALSE:
ktabk['value'] = False
elif ktabtype == BCDUMP_KTAB_TRUE:
ktabk['value'] = True
elif ktabtype == BCDUMP_KTAB_NUM:
lo = stream.readULEB128()
ktabk['lo'] = lo
hi = stream.readULEB128()
ktabk['hi'] = hi
ktabk['value'] = stream.buildFloat64(hi, lo)
elif ktabtype == BCDUMP_KTAB_INT:
int = stream.readULEB128()
ktabk['int'] = int
if int >= (2 ** 31):
ktabk['value'] = int - (2 ** 32)
else:
ktabk['value'] = int
elif ktabtype >= BCDUMP_KTAB_STR:
strlen = ktabtype - BCDUMP_KTAB_STR
ktabk['str'] = stream.readChunk(strlen)
ktabk['_length'] = stream.offset - ktabk['_offset']
ktabk['_bytes'] = stream.checkedSlice(ktabk['_offset'], ktabk['_length'])
def construct_knum(knum, stream):
knum['_offset'] = stream.offset
isnum = (stream.peek() & 1) != 0
lo= stream.readULEB128()
lo = lo >> 1 # get top 32 bit
if isnum:
knum['lo'] = lo
hi = stream.readULEB128()
knum['hi'] = hi
knum['value'] = stream.buildFloat64(hi, lo)
else:
knum['int'] = lo
if lo >= (2 ** 31):
knum['value'] = lo - (2 ** 32)
else:
knum['value'] = lo
knum['_length'] = stream.offset - knum['_offset']
knum['_bytes'] = stream.checkedSlice(knum['_offset'], knum['_length'])
def construct_phead(phead, stream):
phead['_offset'] = stream.offset
phead['flags'] = stream.readByte()
phead['numparams'] = stream.readByte()
phead['framesize'] = stream.readByte()
phead['numuv'] = stream.readByte()
phead['numkgc'] = stream.readULEB128()
phead['numkn'] = stream.readULEB128()
phead['numbc'] = stream.readULEB128()
if not stream.isStripped():
phead['debuglen'] = stream.readULEB128()
if phead['debuglen'] != 0:
phead['firstline'] = stream.readULEB128()
phead['numline'] = stream.readULEB128()
phead['_length'] = stream.offset - phead['_offset']
phead['_bytes'] = stream.checkedSlice(phead['_offset'], phead['_length'])
def decode(filename):
buffer = None
with open(filename, 'rb') as f:
buffer = f.read()
if buffer is None:
raise Exception(ERR_EMPTY_FILE)
try:
dump = {}
stream = ContextStream(buffer, dump)
dump['_parent'] = None
construct_dump(dump, stream)
return dump,stream
except IndexError:
raise Exception(ERR_FILE_FORMAT)
if __name__ == '__main__':
if len(sys.argv) != 2:
print('usage:\ndecode.py file')
raise Exception(ERR_SYNTAX)
else:
dump,context = decode(sys.argv[1])
# debug(dump)
i = 0
for proto in dump['protos']:
print('-- proto[{0}] --'.format(i))
i = i + 1
bytecode.printbytecode(context, proto)
其中的键值命名基本上是对照着LuaJIT 2.0 Bytecode Dump Format ( http://wiki.luajit.org/Bytecode-2.0#LuaJIT-2.0-Bytecode-Dump-Format )来的。
针对由大量字符串常量资源构成的字节码dump文件,处理起来基本上没问题。
打印修饰过后的字节码指令代码如下:
import struct
import decode
string_encoding = 'utf-8'
GGET = 52
LEN = 19
ADDVN = 20
TNEW = 50
TSETV = 57
KSHORT = 39
TGETV = 54
KSTR = 37
TSETB = 59
TGETB = 56
RET0 = 71
RET1 = 72
RET = 70
FNEW = 49
GSET = 53
MOV = 16
CALL = 62
SUBVV = 31
table = {}
op = {}
op['name'] = 'SUBVV'
op['format'] = 'abc'
op['A'] = 'dst'
op['B'] = 'var'
op['C'] = 'var'
op['description'] = '{A} = {B} - {C}'
table[SUBVV] = op
op = {}
op['name'] = 'RET1'
op['format'] = 'ad'
op['A'] = 'rbase'
op['D'] = 'lit'
op['description'] = 'return local[{A}]'
table[RET1] = op
op = {}
op['name'] = 'CALL'
op['format'] = 'abc'
op['A'] = 'base'
op['B'] = 'lit'
op['C'] = 'lit'
op['description'] = 'local[{A}, ..., {A}+{B}-2] = local[{A}]( local[{A}+1, ..., {A}+{C}-1] )'
table[CALL] = op
op = {}
op['name'] = 'MOV'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'var'
op['description'] = '{A} = {D}'
table[MOV] = op
op = {}
op['name'] = 'GSET'
op['format'] = 'ad'
op['A'] = 'var'
op['D'] = 'str'
op['description'] = '_G[{D}] = {A}'
table[GSET] = op
op = {}
op['name'] = 'FNEW'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'func'
op['description'] = '{A} = function[{D}]'
table[FNEW] = op
op = {}
op['name'] = 'RET'
op['format'] = 'ad'
op['A'] = 'rbase'
op['D'] = 'lit'
op['description'] = 'return slot[{A}, ..., {A}+{D}-2]'
table[RET] = op
op = {}
op['name'] = 'RET0'
op['format'] = 'ad'
op['A'] = 'rbase'
op['D'] = 'lit'
op['description'] = 'return'
table[RET0] = op
op = {}
op['name'] = 'TGETB'
op['format'] = 'abc'
op['A'] = 'dst'
op['B'] = 'var'
op['C'] = 'lit'
op['description'] = '{A} = {B}[{C}]'
table[TGETB] = op
op = {}
op['name'] = 'TSETB'
op['format'] = 'abc'
op['A'] = 'var'
op['B'] = 'var'
op['C'] = 'lit'
op['description'] = '{B}[{C}] = {A}'
table[TSETB] = op
op = {}
op['name'] = 'KSTR'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'str'
op['description'] = '{A} = {D}'
table[KSTR] = op
op = {}
op['name'] = 'TGETV'
op['format'] = 'abc'
op['A'] = 'dst'
op['B'] = 'var'
op['C'] = 'var'
op['description'] = '{A} = {B}[{C}]'
table[TGETV] = op
op = {}
op['name'] = 'KSHORT'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'lits'
op['description'] = '{A} = {D}'
table[KSHORT] = op
op = {}
op['name'] = 'TSETV'
op['format'] = 'abc'
op['A'] = 'var'
op['B'] = 'var'
op['C'] = 'var'
op['description'] = '{B}[{C}] = {A}'
table[TSETV] = op
op = {}
op['name'] = 'GGET'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'str'
op['description'] = '{A} = _G[{D}]'
table[GGET] = op
op = {}
op['name'] = 'LEN'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'var'
op['description'] = '{A} = len({D})'
table[LEN] = op
op = {}
op['name'] = 'ADDVN'
op['format'] = 'abc'
op['A'] = 'dst'
op['B'] = 'var'
op['C'] = 'num'
op['description'] = '{A} = {B} + {C}'
table[ADDVN] = op
op = {}
op['name'] = 'TNEW'
op['format'] = 'ad'
op['A'] = 'dst'
op['D'] = 'lit'
op['description'] = '{A} = {{}}'
table[TNEW] = op
def toU8(buffer):
return buffer[0]
def toI16(buffer, context):
if context.isBigEndian():
result, = struct.unpack('>h', buffer)
else:
result, = struct.unpack('<h', buffer)
return result
def toU16(buffer, context):
if context.isBigEndian():
result, = struct.unpack('>H', buffer)
else:
result, = struct.unpack('<H', buffer)
return result
def format(fmt, value, context, proto):
kgcs = proto['pdata']['kgcs']
knums = proto['pdata']['knums']
phead = proto['pdata']['phead']
if fmt == 'dst':
return 'slot[' + str(value) + ']'
elif fmt == 'var':
return 'slot[' + str(value) + ']'
elif fmt == 'lit':
return str(value)
elif fmt == 'lits':
return str(value)
elif fmt == 'base':
return str(value)
elif fmt == 'rbase':
return str(value)
elif fmt == 'func':
# wtf the negated index of constants?
return str(value//2) # hack
elif fmt == 'str':
index = phead['numkgc'] - value - 1
strLiteral = kgcs[index]['str']
return repr(strLiteral.decode(string_encoding))
elif fmt == 'num':
index = value
numLiteral = knums[index]['value']
return repr(numLiteral)
else:
raise Exception('unknow operand format')
def formatArgumentsAndReturns(op, s):
if op in [RET, RET1]:
startIndex = s.index('[')
endIndex = s.index(']', startIndex + 1)
splits = s[startIndex: endIndex + 1]
lst = eval(splits)
if len(lst) == 3:
return 'return local' + repr(list(range(lst[0], lst[2] + 1)))
else:
return s
elif op in [CALL]:
result = ''
startIndex = 0
while True:
prev = startIndex
startIndex = s.find('local[', startIndex)
if startIndex == -1:
result = result + s[prev:]
break
startIndex = startIndex + len('local')
result = result + s[prev: startIndex]
endIndex = s.find(']', startIndex)
splits = s[startIndex: endIndex + 1]
lst = eval(splits)
if '...' in splits and len(lst) == 3:
lst = list(range(lst[0], lst[2] + 1))
result = result + repr(lst)
else:
result = result + splits
startIndex = endIndex + 1
if result.startswith('local[] = '):
result = result[len('local[] = '):]
return result
else:
return s
def formatbytecode(op, A, B, C, D, context, proto):
op = op[0]
opinfo = table[op]
A = format(opinfo['A'], toU8(A), context, proto)
if opinfo['format'] == 'ad':
D = format(opinfo['D'], toU16(D, context), context, proto)
else:
B = format(opinfo['B'], toU8(B), context, proto)
C = format(opinfo['C'], toU8(C), context, proto)
result = opinfo['description'].format(A=A, B=B, C=C, D=D)
result = formatArgumentsAndReturns(op, result)
return result
def printbytecode(context, proto):
pdata = proto['pdata']
phead = pdata['phead']
bcins = pdata['bcins']
numbc = phead['numbc']
for i in range(numbc):
seq = i + 1
ins = bcins[i*4:i*4+4]
if context.isBigEndian():
op = ins[3:4]
A = ins[2:3]
B = ins[0:1]
C = ins[1:2]
D = ins[0:2]
else:
op = ins[0:1]
A = ins[1:2]
B = ins[3:4]
C = ins[2:3]
D = ins[2:4]
insSeq = str(seq).zfill(4)
print(insSeq, ' ', end='')
print(formatbytecode(op, A, B, C, D, context, proto), end='')
print()
输出效果如下:
C:\Users\敏\Desktop>decode q12.bc
-- proto[0] --
0001 slot[0] = _G['ti']
0002 slot[0] = len(slot[0])
0003 slot[0] = slot[0] + 1
0004 slot[1] = _G['ti']
0005 slot[2] = {}
0006 slot[1][slot[0]] = slot[2]
0007 slot[1] = 0
0008 slot[1] = slot[1] + 1
0009 slot[2] = _G['ti']
0010 slot[2] = slot[2][slot[0]]
0011 slot[3] = {}
0012 slot[2][slot[1]] = slot[3]
0013 slot[2] = _G['ti']
0014 slot[2] = slot[2][slot[0]]
0015 slot[2] = slot[2][slot[1]]
0016 slot[3] = '大街上,有美女走过来主动吻你,你该怎么办'
0017 slot[2][1] = slot[3]
0018 slot[2] = _G['ti']
0019 slot[2] = slot[2][slot[0]]
0020 slot[2] = slot[2][slot[1]]
0021 slot[3] = {}
0022 slot[2][2] = slot[3]
0023 slot[2] = _G['ti']
0024 slot[2] = slot[2][slot[0]]
0025 slot[2] = slot[2][slot[1]]
0026 slot[2] = slot[2][2]
0027 slot[3] = '问问是否吻错人'
0028 slot[2][1] = slot[3]
0029 slot[2] = _G['ti']
0030 slot[2] = slot[2][slot[0]]
0031 slot[2] = slot[2][slot[1]]
0032 slot[2] = slot[2][2]
0033 slot[3] = 1
0034 slot[2][2] = slot[3]
0035 slot[2] = _G['ti']
0036 slot[2] = slot[2][slot[0]]
0037 slot[2] = slot[2][slot[1]]
0038 slot[3] = {}
0039 slot[2][3] = slot[3]
0040 slot[2] = _G['ti']
0041 slot[2] = slot[2][slot[0]]
0042 slot[2] = slot[2][slot[1]]
0043 slot[2] = slot[2][3]
0044 slot[3] = '反正是她主动的'
0045 slot[2][1] = slot[3]
0046 slot[2] = _G['ti']
0047 slot[2] = slot[2][slot[0]]
0048 slot[2] = slot[2][slot[1]]
0049 slot[2] = slot[2][3]
0050 slot[3] = 0
0051 slot[2][2] = slot[3]
0052 slot[2] = _G['ti']
0053 slot[2] = slot[2][slot[0]]
0054 slot[2] = slot[2][slot[1]]
0055 slot[3] = {}
0056 slot[2][4] = slot[3]
0057 slot[2] = _G['ti']
0058 slot[2] = slot[2][slot[0]]
0059 slot[2] = slot[2][slot[1]]
0060 slot[2] = slot[2][4]
0061 slot[3] = '美女遇到麻烦事了'
0062 slot[2][1] = slot[3]
0063 slot[2] = _G['ti']
0064 slot[2] = slot[2][slot[0]]
0065 slot[2] = slot[2][slot[1]]
0066 slot[2] = slot[2][4]
0067 slot[3] = 0
0068 slot[2][2] = slot[3]
0069 slot[2] = _G['ti']
0070 slot[2] = slot[2][slot[0]]
0071 slot[2] = slot[2][slot[1]]
0072 slot[3] = {}
0073 slot[2][5] = slot[3]
0074 slot[2] = _G['ti']
0075 slot[2] = slot[2][slot[0]]
0076 slot[2] = slot[2][slot[1]]
0077 slot[2] = slot[2][5]
0078 slot[3] = '吻更狠,直接舌吻'
0079 slot[2][1] = slot[3]
0080 slot[2] = _G['ti']
0081 slot[2] = slot[2][slot[0]]
0082 slot[2] = slot[2][slot[1]]
0083 slot[2] = slot[2][5]
0084 slot[3] = 0
0085 slot[2][2] = slot[3]
0086 return
而其lua源代码为:
local i = #ti + 1
ti[i] = {}
local j = 0
--题目
j = j + 1
ti[i][j] = {}
ti[i][j][1] = "大街上,有美女走过来主动吻你,你该怎么办"
ti[i][j][2] = {}
ti[i][j][2][1] = "问问是否吻错人"
ti[i][j][2][2] = 1
ti[i][j][3] = {}
ti[i][j][3][1] = "反正是她主动的"
ti[i][j][3][2] = 0
ti[i][j][4] = {}
ti[i][j][4][1] = "美女遇到麻烦事了"
ti[i][j][4][2] = 0
ti[i][j][5] = {}
ti[i][j][5][1] = "吻更狠,直接舌吻"
ti[i][j][5][2] = 0
如果资源配置文件里含有更复杂的指令那就麻烦了。
但是既然是资源配置文件,那么就不可能很复杂。解析下字符串常量、整数常量、浮点数常量等应该是足够了。