Пример #1
0
def decompile(func, funcNum):
    global localVars, funcTypes, allLocalVarTypes
    f = c_ast.FuncDef(funcTypes[funcNum], func.name, c_ast.DeclList(),
                      c_ast.Statements())
    #try:
    # If non-empty function that doesn't start with 0x2
    if len(func.cmds) != 0 and func.cmds[0].command != 0x2:
        raise DecompilerError("Script {} doesn't start with a begin".format(
            func.name))
    beginCommand = func.cmds[0]
    argc = beginCommand.parameters[0]
    varc = beginCommand.parameters[1]
    localVarTypes = getLocalVarTypes(func, varc)
    allLocalVarTypes.append(localVarTypes)
    localVars = []
    localVarDecls = []
    for i in range(argc):
        f.args.append(c_ast.Decl(localVarTypes[i], "arg{}".format(i)))
        localVars.append(c_ast.ID("arg{}".format(i)))
    for i in range(varc - argc):
        localVarDecls.append(
            c_ast.Decl(localVarTypes[i + argc], "var{}".format(i + argc)))
        localVars.append(c_ast.ID("var{}".format(i + argc)))

    s = f.statements
    decompileFunc(func, s)

    # Insert local var declarations at the beginning of the function, in order
    for i, decl in enumerate(localVarDecls):
        s.insert(i, decl)
    #except Exception as e:
    #    f.statements = c_ast.Statements([c_ast.Comment("Error occurred while decompiling:\n{}".format(str(e)))])
    return f
Пример #2
0
def main(args):
    global globalVars, globalVarDecls, funcTypes, funcNames, allLocalVarTypes, xmlInfo

    # Use path passed by argument if it exists,
    # else use the path found from getXmlInfoPath()
    # if no XmlInfo file is found, xmlPath will be None
    # MscXmlInfo(None) (aka filename=None) will be an empty MscXmlInfo object
    xmlPath = args.xmlPath if args.xmlPath != None else getXmlInfoPath()
    xmlInfo = MscXmlInfo(xmlPath)

    print("Analyzing...")
    mscFile = mscsb_disasm(args.file)
    print("Decompiling...")

    globalVarDecls = getGlobalVars(mscFile)
    if args.assumeCharStd:
        for g in xmlInfo.globals:
            globalVarDecls[g.id].name = g.name

    globalVars = [c_ast.ID(decl.name) for decl in globalVarDecls]
    funcs = []
    funcTypes = [None for _ in range(len(mscFile))]

    # Rename entrypoint function to "main"
    mscFile.getScriptAtLocation(mscFile.entryPoint).name = 'main'
    funcNames = []
    for script in mscFile:
        funcNames.append(script.name)
    if args.assumeCharStd:
        for f in xmlInfo.functions:
            funcNames[f.id] = f.name

    allLocalVarTypes = []
    for i, script in enumerate(mscFile):
        funcs.append(decompile(script, i))
    funcTypes = getFuncTypes(mscFile)
    for i, func in enumerate(funcs):
        func.type = funcTypes[i]

    if args.split:
        stdlibFuncs = []
        while funcs[0].name != "main":
            stdlibFuncs.append(funcs.pop(0))
        with open("stdlib.c", "w") as f:
            printC(globalVarDecls, stdlibFuncs, f)
        with open(
                args.filename if args.filename != None else
            (os.path.basename(os.path.splitext(args.file)[0]) + '.c'),
                "w") as f:
            print('#include "stdlib.c"', file=f)
            printC([], funcs, f)
    else:
        with open(
                args.filename if args.filename != None else
            (os.path.basename(os.path.splitext(args.file)[0]) + '.c'),
                "w") as f:
            printC(globalVarDecls, funcs, f)
Пример #3
0
def decompileCmd(cmd):
    global currentFunc, index, localVars, globalVars, funcNames, xmlInfo

    funcHolder = currentFunc

    if type(cmd) == Label:
        return None
    if type(cmd) == Command:
        c = cmd.command
        if c in [0x0, 0x1, 0x2, 0x3]:  # Useless garbage
            return None
        elif c in [0x4, 0x5]:  # Jumps
            pass
        elif c in [0x6, 0x8]:  # Return item
            other, args = getArgs(1)
            return other + [c_ast.Return(args[0])]
        elif c in [0x7, 0x9]:  # Return nothing
            return c_ast.Return()
        elif c in [0xA, 0xD]:  # Push constant
            if type(cmd.parameters[0]) == ScriptRef:
                return c_ast.ID(str(cmd.parameters[0]))
            if type(cmd.parameters[0]) == float:
                # Check if float is equal to a math constant
                for m_const in math_constants:
                    if math.isclose(cmd.parameters[0], m_const,
                                    abs_tol=0.0001):
                        return c_ast.ID(math_constants[m_const])
            return c_ast.Constant(cmd.parameters[0])
        elif c == 0xB:  # Push variable
            if cmd.parameters[0] == 0:
                return localVars[cmd.parameters[1]]
            else:
                return globalVars[cmd.parameters[1]]
        elif c in [
                0xe, 0xf, 0x10, 0x11, 0x12, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x25,
                0x26, 0x27, 0x28, 0x29, 0x2a, 0x3a, 0x3b, 0x3c, 0x3d, 0x46,
                0x47, 0x48, 0x49, 0x4a, 0x4b
        ]:
            other, args = getArgs(2)
            return other + [
                c_ast.BinaryOp(BINARY_OPERATIONS[c], args[1], args[0])
            ]
        elif c in [
                0x13, 0x18, 0x2b, 0x3e
        ]:  # Negation, bit not, logic not, etc. (Unary Op not applied to variable)
            other, args = getArgs(1)
            return other + [c_ast.UnaryOp(UNARY_OPERATIONS[c], args[0])]
        elif c in [0x14, 0x15, 0x3f,
                   0x40]:  # ++, --, etc. (Unary Op applied to variable)
            if cmd.parameters[0] == 0:
                return c_ast.UnaryOp(UNARY_OPERATIONS[c],
                                     localVars[cmd.parameters[1]])
            else:
                return c_ast.UnaryOp(UNARY_OPERATIONS[c],
                                     globalVars[cmd.parameters[1]])
        elif c in [
                0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x41,
                0x42, 0x43, 0x44, 0x45
        ]:  # varset, floatvarset, etc.
            other, args = getArgs(1)
            if cmd.parameters[0] == 0:
                variable = localVars[cmd.parameters[1]]
            else:
                variable = globalVars[cmd.parameters[1]]
            return other + [
                c_ast.Assignment(ASSIGNMENT_OPERATIONS[c], variable, args[0])
            ]
        elif c == 0x2c:  # printf
            other, args = getArgs(cmd.parameters[0])
            return other + [
                c_ast.FuncCall("printf", c_ast.DeclList(args[::-1]))
            ]
        elif c == 0x2d:  # syscall
            other, args = getArgs(cmd.parameters[0])
            syscallInfo = xmlInfo.getSyscall(cmd.parameters[1])
            if syscallInfo != None:
                if RepresentsInt(str(args[-1])):
                    methodInfo = syscallInfo.getMethod(int(str(args[-1]), 0))
                    if methodInfo != None:
                        return other + [
                            c_ast.FuncCall(
                                c_ast.StructRef(syscallInfo.name,
                                                methodInfo.name),
                                c_ast.DeclList(args[-2::-1]))
                        ]
                return other + [
                    c_ast.FuncCall(syscallInfo.name, c_ast.DeclList(
                        args[::-1]))
                ]
            return other + [
                c_ast.FuncCall("sys_%X" % cmd.parameters[1],
                               c_ast.DeclList(args[::-1]))
            ]
        elif c == 0x30:  # set_main
            other, args = getArgs(cmd.parameters[0] + 1)
            if type(args[0]) == c_ast.Constant and type(args[0].value) == str:
                args[0] = c_ast.ID(args[0].value)
            return other + [
                c_ast.FuncCall("set_main",
                               c_ast.DeclList(args[0:1] + args[:0:-1]))
            ]  #args[0:1] + args[:0:-1] is the first arg then the rest are in opposite order
        elif c == 0x31:  # callFunc3
            other, args = getArgs(cmd.parameters[0] + 1)
            if type(args[0]) == c_ast.Constant and type(args[0].value) == str:
                args[0] = c_ast.ID(args[0].value)
            return other + [
                c_ast.FuncCall("callFunc3",
                               c_ast.DeclList(args[0:1] + args[:0:-1]))
            ]
    elif type(cmd) == FunctionCallGroup:
        oldFunc = currentFunc
        oldIndex = index

        currentFunc = cmd
        index = len(
            currentFunc) - 2  # (ignore the label that will be at the end)
        if currentFunc[index].command != 0x2f:
            raise DecompilerError("Function improperly formatted")
        cmd = currentFunc[index]
        other, args = getArgs(cmd.parameters[0] + 1)

        while index > 0:
            d = decompileCmd(currentFunc[index])
            if type(d) == list:
                other = d + other
            else:
                other.insert(0, d)
            index -= 1

        if type(args[0]) == c_ast.ID and not args[0].name in funcNames:
            args[0] = c_ast.UnaryOp("*", args[0])

        if type(args[0]) == c_ast.Constant and type(args[0].value) == str:
            args[0] = c_ast.ID(args[0].value)

        currentFunc = oldFunc
        index = oldIndex

        return other + [c_ast.FuncCall(args[0], c_ast.DeclList(args[:0:-1]))]
    elif type(cmd) == Cast:
        other, args = getArgs(1)
        return other + [c_ast.Cast(cmd.type, args[0])]
    elif type(cmd) == IfElseIntermediate:
        beforeIf, args = getArgs(1)
        if len(args) == 0:
            return beforeIf
        ifCondition = args[0]
        oldFunc = currentFunc
        oldIndex = index
        trueStatements = c_ast.Statements()
        currentFunc = cmd.ifCommands
        index = len(currentFunc) - 1
        while index >= 0:
            d = decompileCmd(currentFunc[index])
            if type(d) == list:
                for i in d[::-1]:
                    if i != None:
                        trueStatements.insert(0, i)
            elif d != None:
                trueStatements.insert(0, d)
            index -= 1
        if cmd.elseCommands != None:
            currentFunc = cmd.elseCommands
            index = len(currentFunc) - 1
            falseStatements = c_ast.Statements()
            while index >= 0:
                d = decompileCmd(currentFunc[index])
                if type(d) == list:
                    for i in d[::-1]:
                        if i != None:
                            falseStatements.insert(0, i)
                elif d != None:
                    falseStatements.insert(0, d)
                index -= 1
        else:
            falseStatements = None
        currentFunc = oldFunc
        index = oldIndex
        if cmd.isNot:
            ifCondition = c_ast.UnaryOp("!", ifCondition)
        return beforeIf + [
            c_ast.If(ifCondition, trueStatements, falseStatements)
        ]
    elif type(cmd) == WhileIntermediate:
        oldFunc = currentFunc
        oldIndex = index
        loopStatements = c_ast.Statements()
        currentFunc = cmd.commands
        index = len(currentFunc)
        other, condition = getArgs(1)
        index -= 1
        condition = condition[0]
        for i in other[::-1]:
            loopStatements.insert(0, i)
        while index >= 0:
            d = decompileCmd(currentFunc[index])
            if type(d) == list:
                for i in d[::-1]:
                    if i != None:
                        loopStatements.insert(0, i)
            elif d != None:
                loopStatements.insert(0, d)
            index -= 1
        currentFunc = oldFunc
        index = oldIndex
        if not cmd.isIfNot:
            condition = c_ast.UnaryOp("!", condition)
        if cmd.isDoWhile:
            return c_ast.DoWhile(condition, loopStatements)
        else:
            return c_ast.While(condition, loopStatements)
    elif type(cmd) == c_ast.Break:
        return cmd