luajit 2.0 bytecode dump文件解析

网上搜索好久,没有找到现成的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


如果资源配置文件里含有更复杂的指令那就麻烦了。

但是既然是资源配置文件,那么就不可能很复杂。解析下字符串常量、整数常量、浮点数常量等应该是足够了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值