1. 程式人生 > >luajit 2.0 bytecode dump檔案解析

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


如果資源配置檔案裡含有更復雜的指令那就麻煩了。

但是既然是資源配置檔案,那麼就不可能很複雜。解析下字串常量、整數常量、浮點數常量等應該是足夠了。

相關推薦

luajit 2.0 bytecode dump檔案解析

網上搜索好久,沒有找到現成的luajit位元組碼的反編譯工具。 最好的的是NightNord的ljd專案(https://github.com/NightNord/ljd)。但是最近一次提交是1年前。 下載下來之後嘗試了下,報了IO Error: I/O error wh

dump檔案解析之探索.Net的記憶體

前言: 對於需要長時間執行的.net程式,有時需要我們檢視記憶體的使用有沒有記憶體洩露問題。 我們可以從dump檔案中找到答案。 Dump的看點 用dump檔案來分析記憶體,到底我們需要關心哪些點呢? 記憶體的使用情況 HeapSize/object的數量 也就是託管堆使用大小以及託管堆內有

ARKit學習之2.0基礎及案例解析(後續再更新)

 為了方便AR開發交流,博主建立了一個群 :891555732,歡迎一起討論 一.多人共享資料  官方案例原始碼 : https://github.com/Unity-Technologies/SharedSpheres ①.獲取資料及儲

UCloud釋出“資料方舟2.0” 強力防範檔案誤刪與勒索病毒攻擊

2017年1月14日某網際網路公司因發生故障導致資料損壞而重啟伺服器,並且由於相關備份資料庫也同時故障所以經多次嘗試最終仍舊無法修復成功。 2017年2月1日凌晨,著名的程式碼資源託管網站 Gitlab.com 的一位工程師在維護資料時不慎刪除約 300GB 的資料。當他意識到並按下 Ctrl +

Django 2.0 專案配置檔案介紹:

Django專案配置檔案為setting,如下: 其各部分內容含義如下: BASE_DIR:專案檔案路徑,專案建立之後自動設定,不需要改動。 SECRET_KEY:金鑰,專案建立之後自動設定,不需要改動。 DEBUG:供開發人員用,專案結束後,需要將其設定為False。

spring boot 2.0的POM檔案中不用使用pluginManagement標籤,會使打包配置檔案失效

參考如下,如果spring boot 2.0中的採用下面模式 <pluginManagement> <plugins>             <plugin>     &nb

java使用Extract API 2.0生成.hyper檔案

突然接到一個任務,需要用java使用Extract API 2.0實現抽取資料庫的數並生成.hyper檔案。沒辦法,只能一邊看文件一邊寫個demo先試試看能不能行得通。 文件:https://onlinehelp.tableau.com/current/api/extract_api/e

微服務 SpringBoot 2.0(二):配置檔案解析

properties我用了好多年,你卻讓我用yml,這是什麼鬼 —— Java面試必修 引言 上一篇介紹了Spring Boot的輕鬆入門專案構建,對Spring Boot的特性有了初步瞭解。但如果要想玩得很熟練的話就請看接下來的文章,這樣有助於後續我們快速

Ansible API 2.0解析

ansibleimport json from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.vars import VariableManager from ansib

關於MBI-MFC理財平臺2.0最新制度解析

風控 模式 必須 賬戶 現在 粉絲 100% 什麽是 官方 MFC 2.0版本解讀由於現在平臺的數據過於龐大平臺決定將龐大的數據進行壓縮 如何壓縮?平臺將會以粉絲戶的配送次數進行分配壓縮 【壓縮分配】配送?次 - 80%分配到資產包;20%分配到GRC1錢包配送?次 - 7

微服務 SpringBoot 2.0(二):配置文件解析

@override 書寫 string 接下來 code java interface sse als properties我用了好多年,你卻讓我用yml,這是什麽鬼 —— Java面試必修 引言 上一篇介紹了Spring Boot的輕松入門項目構建,對Spring B

2.0 解析系列 | OceanBase 2.0 之 Flashback功能

OB君:本文是 “OceanBase 2.0 技術解析系列” 的第六篇文章。今天我們來聊聊資料的持續可用,說說2.0中大家都很關心的“Flashback”閃回功能。更多精彩歡迎關注OceanBase公眾號持續訂閱本系列內容! 前言 資料庫產品作為資訊系統的重要組成部分,除了要高效的處理使用者請求

asp.net 2.0 分析器錯誤訊息: 檔案.aspx.cs”不存在錯誤

布webapplication時後老是報告分析器錯誤訊息: 檔案.aspx.cs”不存在錯誤,差點抓狂,後來在網上搜到原因是: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="{0}" Inherits="{1}" %&g

2.0解析系列 | OceanBase 2.0 之 Flashback 閃回功能

OB君:本文是 “ OceanBase 2.0 技術解析系列” 的第六篇文章。今天我們來聊聊資料的持續可用,說說2.0中大家都很關心的“Flashback”閃回功能。更多精彩歡迎關注OceanBase公眾號持續訂閱本系列內容! 前言 資料庫產品作為資訊

2.0解析系列 | OceanBase 2.0 之 索引實時生效

OB君:本文是 “ OceanBase 2.0 技術解析系列” 的第七篇文章。今天我們來聊聊資料的持續可用,說說2.0的索引實時生效功能。更多精彩歡迎關注OceanBase公眾號持續訂閱本系列內容! 引言 隨著業務的快速發展,其對資料庫的資料訪問規則是不斷變化的,在資料庫中新建索引來加速業務查

2.0解析系列 | OceanBase 2.0——第一款支援“儲存過程”的原生分散式資料庫

OB君:本文是 “ OceanBase 2.0 技術解析系列” 的第八篇文章,今天我們來說說2.0版本最標誌性、最不得不提的新特性——儲存過程。在為數不多的原生分散式資料庫中,OceanBase 2.0是第一款支援儲存過程功能的產品。本文將為你深入剖析2.0中儲存過程的功能特性和實現機制。更多精彩歡迎關

未能載入檔案或程式集 System Web Helpers Version 2 0 0 0

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

2.0 解析系列 | OceanBase的重要基礎設施——DBReplay

OB君:9月21日,OceanBase 2.0 在雲棲大會上重磅釋出。我們將在接下來的時間裡為大家持續推出 “ OceanBase 2.0 技術解析系列” 文章,分別從 可運維性、分散式架構、資料可用性、價效比及相容性 五個方面對OceanBase 2.0的產品新特性及其背後的技術原理進行深入的解析。

JavaEE-SSM:005 Mybatis的配置檔案解析2

setting配置不常用,給出配置說明:     本文給出Setting配置的全量程式碼:   <settings> <setting name="cacheEnabled" value="true"

.net core 2.0 虛擬目錄下載 Android Apk 等檔案

當Android 檔案 Apk 放在Asp.net core wwwroot 虛擬目錄下面、訪問是 404,設定Content-Type型別 app.UseStaticFiles(); //設定實際目錄與虛擬目錄