Ejemplo n.º 1
0
def find_synced_units(source_units, addr_space):
    """
    :returns: units which are synced in type, size, and starting address. This only applies to Function or Data. All else is treated as Unknown
              it returns a list of synced function addresses, synced data addresses, and synced unknown addresses
    """
    synced_functions = []
    synced_data = []
    synced_unk = []
    for i, ea in enumerate(addr_space):
        unit = source_unit.get_physical_unit(source_units, ea)
        unit_size = source_unit.compute_unit_size_from_index(addr_space, i)

        if unit['unit']['id'] is AsmFile.UNIT_IDS.FUNCTION:
            if Function.isFunction(ea):
                func = Function.Function(ea)
                if func.func_ea == ea and func.getSize(withPool=True) == unit_size:
                    synced_functions.append(ea)
        else:
            data = Data.Data(ea)
            if data.ea == ea:
                if data.getSize() == unit_size:
                    if unit['unit']['id'] == AsmFile.UNIT_IDS.DATA:
                        synced_data.append(ea)
                    else:
                        synced_unk.append(ea)
                else:
                    # get size until another label is defined
                    till_label_size = _next.name(ea, ui=False, hexOut=False) - ea
                    if till_label_size == unit_size:
                        if unit['unit']['id'] == AsmFile.UNIT_IDS.DATA:
                            synced_data.append(ea)
                        else:
                            synced_unk.append(ea)

    return synced_functions, synced_data, synced_unk
Ejemplo n.º 2
0
 def testBasic(self):
     # type: () -> None
     """
     Tests that InvalidFunctionException is raised if instantiated with invalid EA.
     And tests that valid functions give valid behavior
     """
     try:
         f = Function.Function(0x00)
         Test.fail("InvalidFunctionException not raised")
     except (Function.FunctionException):
         pass
     for td in self.testData:
         f = Function.Function(td['ea'])
         Test.assertEquals(f.func_ea, td['ea'],
                           "Function EA mistmatch: 0x%08X" % f.func_ea)
         # getName()
         Test.assertEquals(f.getName(), td['name'],
                           "Function name mismatch")
         # setName()
         f.setName(td['name'] + "0")
         Test.assertEquals(f.getName(), td['name'] + "0",
                           "setName() not working")
         f.setName(td['name'])
         Test.assertEquals(f.getName(), td['name'],
                           "could not set name back to normal")
         # getSize()
         Test.assertEquals(f.getSize(withPool=True), td['size_pool'],
                           "invalid pool size")
         Test.assertEquals(f.getSize(), td['size_nopool'],
                           "invalid no pool size")
    def read(self, accesses_path):
        # type: (str) -> None
        """
        Reads in the memory accesses defined by the MemAccessScanner protocol
        :param accesses_path: path to a file containing the accesses
        :return:
        """
        f = open(accesses_path, "r")
        s = f.read()
        items = s.split(' ')
        self.accesses = []
        for i in range(len(items)):
            if '::' in items[i]:
                access_ea = int(items[i][items[i].index('::') + 2:], 16)
                access = items[i + 1][:-1]
                self.accesses.append((access_ea, access))
        self.accesses.sort(key=lambda tup: tup[0])

        for access_ea, access in self.accesses:
            if Function.isFunction(access_ea):
                func_ea = Function.Function(access_ea).func_ea
                if func_ea not in self.funcs:
                    self.funcs.append(func_ea)
            else:
                data_ea = Data.Data(access_ea).ea
                if data_ea not in self.data:
                    self.data.append(data_ea)
        self.funcs.sort()
        self.data.sort()

        f.close()
Ejemplo n.º 4
0
    def decomp(self, decompPath, gameFiles=None):
        if not gameFiles:
            gameFiles = self.gameFiles
        for file in sorted(gameFiles.keys(), key=gameFiles.__getitem__):
            if file.endswith('.s'):
                filename = file[:file.rindex('.')]
                decomp = ''
                # decompile all functions within file
                print("> Decompiling %s... " % (self._getBaseFilename(file)[:-2] + '.c'))
                ea = gameFiles[file][0]
                while ea < gameFiles[file][1]:
                    if Function.isFunction(ea):
                        func = Function.Function(ea)
                        # include address for ease of search in case of label name changes
                        decomp += '// 0x%07x\n' % func.func_ea
                        # try to decompile, or put the asm
                        try:
                            decomp += str(idaapi.decompile(func.func_ea)) + '\n\n'
                        except ida_hexrays.DecompilationFailure as e:
                            print('\tFailed to decompile %07X <%s>' % (func.func_ea, func.getName()))
                            decomp += 'int %s() { // could not decompile\n' % func.getName()
                            decomp += '\tasm("%s"\n\t);\n}\n\n' % func.getFormattedDisasm().replace('\n', '\\\n\t').rstrip()
                        ea = func.func_ea + func.getSize(withPool=True)
                    else:
                        ea += idaapi.get_item_size(ea)
                # write decomp to file.c
                if decomp:
                    decomp = decomp.replace('  ', '    ')
                    decomp = decomp.replace('\t', '    ')
                    print(self.projPath, decompPath)
                    cFile = open(self.projPath[0] + decompPath + self._getBaseFilename(file)[:-2] + '.c', 'w')
                    cFile.write(decomp)
                    cFile.close()

        print("Decompilation complete!")
Ejemplo n.º 5
0
    def __init__(self):
        test = Test.Test("IDAItems.GameFile")

        # dictionary of test data dictionaries
        self.testData = dict()

        # test data 1 -- LocalNamed File
        d1 = dict()
        d1['ea'] = 0x080E5964
        d1['name'] = "t"
        d1['size'] = 0x080E5A08 - d1['ea']
        d1['items'] = [Function.Function(0x080E5964), Data.Data(0x080E5972), Data.Data(0x080E5974),
                       Data.Data(0x080E5978), Function.Function(0x080E5988), Function.Function(0x080E59C6),
                       Function.Function(0x080E59E8), Data.Data(0x080E59F6), Data.Data(0x080E59F8),
                       Data.Data(0x080E59FC)]
        d1['mode'] = GameFile.Mode.NAME
        d1['content'] = self.readROM(d1['ea'], d1['size'])
        d1['xrefsTo'] = ([],[d1['poolFuncEA']+0x02])
        d1['xrefsFrom'] = ([], [])

        # add data to manually computed test data
        self.testData['pointer'] = d1

        test.add(Test.Test("testBasic()", self.testBasic))
        self.test = test
Ejemplo n.º 6
0
    def rng(start_ea, end_ea, debug=False):
        # type: (int, int) -> str
        """
        disassembles all data elements within a range
        if a function is detected within the range, the function itself is disassembled
        as a whole item. (meaning it could surpass end_ea, but it would be the last item)
        :param start_ea: the start ea of the range
        :param end_ea: the end ea, not included
        :return: the disassembly of the range, in optimal format
        """
        ea = start_ea
        disasm = ''

        # disassemble the range
        ea = start_ea
        while ea < end_ea:
            if  Function.isFunction(ea):
                f = Function.Function(ea)
                if debug: print("%07X: disass function %s @ %07X" % (ea, f.getName(), f.func_ea))
                disasm += f.getFormattedDisasm(start_ea, end_ea) + "\n\n"
                ea = ea + f.getSize(withPool=True)
            else:
                d = Data.Data(ea)
                if debug: print("%07X: disass data %s @ %07X" % (ea, d.getName(), d.ea))
                disasm += d.getFormattedDisasm(start_ea, end_ea) + "\n"
                ea = ea + d.getSize()

        # add comment for debugging purposes
        # disasm += "/*For debugging purposes, connect comment at any range!*/\n"

        return disasm
Ejemplo n.º 7
0
    def rngExterns(start_ea, end_ea, toStr=True):
        """
        creates .equs for all external symbols used in the range
        :param start_ea: start ea of the range, inclusive
        :param end_ea: end ea of the range, exclusive
        :return: a string containing all the external symbol .equs, or just the refs if not disp
        """
        ea = start_ea
        xrefs = []

        # if there's a function at end_ea, include all of its refs
        if Function.isFunction(end_ea):
            f = Function.Function(end_ea)
            end_ea = f.func_ea + f.getSize(withPool=True)

        # obtain xrefs of every data item, filtering out internal ones and duplicates
        while ea < end_ea:
            d = Data.Data(ea)
            # append crefs ands xrefs

            for xref in d.getXRefsFrom()[0]:
                # all code refs shouldn't have a +1 in them. The thumb switch isn't involved with the symbol itself
                if (idc.isCode(idc.GetFlags(xref)) or idc.isCode(
                        idc.GetFlags(xref - 1))) and xref & 1 == 1:
                    xref = xref - 1

                if ((xref < start_ea or xref >= end_ea
                     )  # filter internal (not external; within range)
                        and xref not in xrefs):  # filter duplicate
                    xrefs.append(xref)
            for xref in d.getXRefsFrom()[1]:
                # all code refs shouldn't have a +1 in them. The thumb switch isn't involved with the symbol itself
                if (idc.isCode(idc.GetFlags(xref)) or idc.isCode(
                        idc.GetFlags(xref - 1))) and xref & 1 == 1:
                    xref = xref - 1

                if ((xref < start_ea or xref >= end_ea
                     )  # filter internal (not external; within range)
                        and xref not in xrefs  # filter duplicate
                        and d.isPointer(xref)
                    ):  # filter non-pointer symbols, like byte_50
                    xrefs.append(xref)
            # advance to next item
            ea = ea + d.getSize()

        xrefs.sort()

        if not toStr:
            return xrefs

        output = ''
        # output file formats to include symbols into linking process
        for xref in xrefs:
            d = Data.Data(xref)
            name = d.getName()
            xref = d.ea
            output += '.equ %s, 0x%07X\n' % (name, xref)

        return output
Ejemplo n.º 8
0
def markStructOffsets():
    ea = here()
    while ea < 0x8800000:
        if Function.isFunction(ea):
            f = Function.Function(ea)
            print(f.getName())
            markToolkit(f.func_ea)
            markStructFromToolkit(f.func_ea, 0x3C, 'oGameState')
            ea += f.getSize(withPool=True)
        else:
            ea += Data.Data(ea).getSize()
Ejemplo n.º 9
0
def sync_identified_units_names(source_units, addr_space):
    def test_matching_name(unit, ea, verbose=False):
        if idc.get_name(ea) != unit['name']:
            err_msg = '0x{:X}: found differing unit name: unit {} != ida {}'.format(ea, unit['name'], idc.get_name(ea))
            if verbose:
                return err_msg
            return False
        return True

    print(os.getcwd())
    log_file = open('../a.log.ign', 'w')

    synced_function_addrs, synced_data_addrs, synced_unk_addrs = cache_find_synced_units(source_units, addr_space,
                                                                                                 recache=True)

    for ea in synced_function_addrs:
        unit = source_unit.get_physical_unit(source_units, ea)

        func = Function.Function(ea)
        out = test_matching_name(unit, func.func_ea, verbose=True)
        if type(out) is str:
            print(out)
            log_file.write(out + '\n')

    for ea in synced_data_addrs:
        unit = source_unit.get_physical_unit(source_units, ea)

        data = Data.Data(ea)
        out = test_matching_name(unit, data.ea, verbose=True)
        if type(out) is str:
            print(out)
            log_file.write(out + '\n')

    log_file.close()
    def find_function(self, func_ea):
        """
        This searches the binary for an occurrance of the hex data of this function. The hex data to be searched for
        is found in the Analysis ROM.
        :param func_ea:  long: The effective address of the function to search
        :return:  Address of occurrance of function in binary if found, or None.
                 (Error Cases)
                 - If func_ea is not found to be within a function, INVALID_FUNCTION is returned. (See constants)
                 - if func_ea < the ROM's starting addr, RANGE_OUT_OF_ROM is returned. (See constants)
        """

        # All functions to be found have to actually be within the ROM region
        if func_ea < self.ROM_start_addr: return RANGE_OUT_OF_ROM
        try:
            func = Function.Function(func_ea)
            start_ea, end_ea = func.getBoundaries()
            funcData = self._get_func_data(func)
            # Search Binary for the data
            try:
                output = self.binData.index(funcData)
            except ValueError:
                output = None
            return output
        except Function.FunctionException:
            return INVALID_FUNCTION
Ejemplo n.º 11
0
def markStructFromToolkit(func_ea, structOff, structName):
    f = Function.Function(func_ea)
    toolkitMovs = FuncAnalyzer.traceRegVar(f.func_ea, f.func_ea + f.getSize(withPool=False), 10, 0)
    for trace_ea in toolkitMovs:
        # lock into the register in question
        insn = Instruction.Insn(trace_ea)
        if insn.isComputationalInsn():
            regWrites = FuncAnalyzer.traceRegWrites(f.func_ea, f.func_ea + f.getSize(withPool=False),
                                                    insn.ops[0].reg)
            # TODO: traceRegWrites messes up insn
            insn = Instruction.Insn(trace_ea)
            # find the relevent register variable to trace
            regVarIdx = FuncAnalyzer.getRegWriteIndex(trace_ea, regWrites)
            insn = Instruction.Insn(trace_ea)
            # trace all reads to this register variable in particular
            # print('args', f.func_ea, f.func_ea + f.getSize(withPool=False),
            #                                           insn.ops[0].reg, regVarIdx)
            toolkitAccesses = FuncAnalyzer.traceRegVar(f.func_ea, f.func_ea + f.getSize(withPool=False),
                                                      insn.ops[0].reg, regVarIdx)
            # those now will represent all registers R10 moved to, and thus, all of those accesses
            # are accesses to the R10 struct
            for access in toolkitAccesses:
                # now mark with Toolkit enum
                accessInsn = Instruction.Insn(access)
                if accessInsn.ops[1].type == ida_ua.o_displ:
                    if accessInsn.ops[1].addr == structOff:
                        # print(hex(access), idc.GetDisasm(access))

                        structAcccesses = FuncAnalyzer.traceRegVar(f.func_ea, f.func_ea + f.getSize(withPool=False),
                                                                   accessInsn.ops[0].reg, regVarIdx+1)
                        for structAccess in structAcccesses:
                            # print(hex(structAccess), idc.GetDisasm(structAccess))
                            idc.op_enum(structAccess, 1, idc.get_enum(structName), 0)
    return True
def guessFuncSig(func_ea):
    # type: (int) -> (list[str], list[str])
    """
    Guesses the signature of the current function based on the input registers it uses (R0-R3) and
    based on the output registers it may return. (most likely R0, but sometimes multiple registers are returned)
    It also checks for whether the zero flag have been written to by the function without being used, then it would
    return the zero flag.
    :param func_ea: the linear address of the function to analyze
    :return: the list of types for the input parameters, and for the returns.
    'zf' can be included, in the return types list.
    """
    # input register parameters -- r0 through r3. If they are identified as an input, this will store
    # a string of their type. (States -- None: No param found yet. '': Parameter. But Unknown Type.
    paramTypes = [None, None, None, None]
    retType = None
    zf = False
    # flags
    updatedRegs = 0b0000  # if the register is updated, its flag is set
    # make sure to recognize push/pop like patterns. A register can be saved to be used later.
    savedRegs = 0b0000
    # This flag is cleared whenever R0 is updated.
    # It is set whenever R0 is used. This indicated whether the return is used or not
    usedRet = False
    # the returns of calls are remembered since their type can match with the current function
    callRets = None
    func = Function.Function(func_ea)

    ea = func.func_ea
    while ea < func.func_ea + func.getSize():
        insn = Instruction.Insn(ea)
        if insn.size != 0:
            # parse destination and source registers, if any
            if insn.ops[0].type == idaapi.o_reg:
                destReg = insn.ops[0].reg
                # now parse all source registers
                sourceRegisters = insn.getSourceRegisters()
                # if a destReg is R0~R3, set its flag
                raise (NotImplemented())
                # update return status to know whether the register is used after being set at the end of the function

            # traverse function calls if parameters weren't identified yet
            if insn.ops[0].type in [idaapi.o_far, idaapi.o_near
                                    ] and None in paramTypes:
                callParams, callRets = guessFuncSig(insn.ops[0].addr)
                # deduce input parameters for this function from input parameters for the called function
                for i in range(len(callParams)):
                    if not updatedRegs & i:
                        # register is passed in as input to callee! Register it as an input of this function too!
                        raise (NotImplemented())

            # handle the push/pop register saving pattern
            if insn.itype == idaapi.NN_push:
                raise (NotImplemented())
            if insn.itype == idaapi.NN_pop:
                raise (NotImplemented())

            ea += insn.size
        else:
            ea += idc.get_item_size(ea)
Ejemplo n.º 13
0
def findMostUsedFunctions(count, notModified=False, disp=True):
    # type: (int, bool, bool) -> list[int]
    """
    Returns the functions with the highest count of xrefsTo. if notModified, only those that are in the format
    *_xxxxxxx are returned. if disp, the output is formatted and printed as well
    :param count: the number of the most used functions to find
    :param notModified: only functions with names ending in func_ea, or all functions
    :param disp: print the output
    :return: list of function linear addresses to the most used functions
    """
    funcXrefs = []

    if count <= 0: count = 1
    for i in range(count):
        funcXrefs.append((0, 0))

    for seg_ea in idautils.Segments():
        for func_ea in idautils.Functions(seg_ea, idc_bc695.SegEnd(seg_ea)):
            if notModified:
                name = Function.Function(func_ea).getName()
                if not ('_' in name
                        and name[name.rindex('_'):] == ('_%X' % func_ea)):
                    continue

            xrefs = Function.Function(func_ea).getXRefsTo()
            numXrefs = len(xrefs[0]) + len(xrefs[1])

            # add if more than least in the list and sort
            if numXrefs > funcXrefs[0][1]:
                funcXrefs[0] = (func_ea, numXrefs)
                funcXrefs = sorted(funcXrefs, key=lambda tup: tup[1])

    # reverse to display most common first
    funcXrefs = sorted(funcXrefs, key=lambda tup: tup[1], reverse=True)

    if disp:
        for func_ea, xrefCount in funcXrefs:
            print('%07x <%s::%s>: %d' %
                  (func_ea, mtcomm.ea2gf(func_ea),
                   Function.Function(func_ea).getName(), xrefCount))

    output = []
    for func_ea, xrefCount in funcXrefs:
        output.append(func_ea)
    return output
def fcmp(cmd, *args, **kwargs):
    """
    This is to be executed through the run module
    :param cmd: Not used. There because python. Thank you!
    :param args: (binaryPath)
            binaryPath: (str) the path to the binary being function compared with
    :param kwargs: (wr=True, q=False, wrPath=PYTOOLS_PATH + 'FE8.txt')
            wr: (bool) whether also to write the analysis to a file, or only return the value. Default to write.
            q: (bool) supresses info messages, False if not specified.
            wrPath: (str) path to file to output the results in. If not specified, analysis ouput is returned
    :return: Files detected within the module
    """
    binaryPath = args[0]
    wr = kwargs['wr'] if 'wr' in kwargs else True
    wrPath = (
        'wrPath' in kwargs and
        wr) and kwargs['wrPath'] or wr and IDATOOLS_PATH + 'FE8.txt' or None
    wrFile = open(wrPath, 'w') if wrPath else None
    suppressed = 'q' in kwargs and kwargs['q'] or False

    if not suppressed: print("Starting Binary Search Analysis...")

    searcher = BinarySearcher(binaryPath)

    # Perform and time Analysis
    if not suppressed:
        stopwatch = time.time()
    matchedFunctions = searcher.scan_for_known_functions()
    if not suppressed:
        stopwatch = time.time() - stopwatch
        if wrFile: wrFile.write("Analysis took %d s\n" % int(stopwatch))
        else: print("Analysis took %d s\n" % int(stopwatch))

    # Simply output all entries
    matchedFunctions_filtered = []
    for x in matchedFunctions:
        filters = [
            # x["ROM_Addr"] == ROM_seg + x["Bin_Addr"], # Detect only functions identical in content and location
            Function.Function(x["ROM_Addr"]).getSize() >=
            2 * INSTRUCTION_WIDTH,  # Filter out empty/weird small funcs
            'nullsub' not in
            x["Name"],  # filter out all nullsubs, they don't give a lot of information.
            'jumpout' not in
            x["Name"],  # jumpout functions are very small, and just support various usages of a func
            'bx_R' not in
            x["Name"],  # Those weird 'functions' are so small and introduce noise to the analysis
        ]
        filtered = True
        for f in filters:
            filtered = filtered and f
        if filtered:
            if wrFile:
                wrFile.write(str(x["Name"]) + ": " + hex(x["Bin_Addr"]) + '\n')
            matchedFunctions_filtered.append(x)
    if wrFile: wrFile.close()
    if not suppressed: print("Binary Search Analysis Complete!")
Ejemplo n.º 15
0
def fixThumbPushPopFuncRanges(start_ea, end_ea, verbose=True):
    """
    This is heusterical, it fixes problems that occur in the IDA anlysis.
    This fix only applies to thumb functions started with PUSH {LR}.
    Two cases are considered valid:
    - A function is cut off at a BL. Change its range to the nearest POP {PC}
    - A function is cut off at a BX, and a CPU mode change error occurs.
    - A function is cut off at a POP {PC}, but there is no PUSH {LR} before teh occurrance of the next POP{PC}
    The fixing process involves turning data into code, if needed, and changing to thumb mode until the next POP.
    :param start_ea: start of the range to look for broken functions in
    :param end_ea: end of the range to look for functions in
    :param verbose: prints info messages
    :return: fix status. False if any problems occur
    """
    ea = start_ea
    while ea < end_ea:
        if Function.isFunction(ea):
            func = Function.Function(ea)
            if func.isThumb():
                # ensure it's a PUSH/POP function
                firstInsn = Instruction.Insn(func.func_ea)
                # print(idc.GetDisasm(firstInsn.ea))
                if (firstInsn.itype == idaapi.ARM_push
                        and (firstInsn.getPushPopFlags() & (1 << 14)) != 0):
                    # check the last instruction. make sure it's a POP {..., PC}, BL, or BX.
                    lastInsn_ea = func.func_ea + func.getSize(withPool=False)
                    if idc.get_item_size(lastInsn_ea - 4) == 4:
                        lastInsn_ea -= 4  # in case of BL, which is of size 4
                    else:
                        lastInsn_ea -= 2
                    lastInsn = Instruction.Insn(lastInsn_ea)
                    # print(idc.GetDisasm(lastInsn.ea))
                    if ((lastInsn.itype == idaapi.ARM_pop and
                         (lastInsn.getPushPopFlags() & (1 << 15)) != 0)
                            or lastInsn.itype == idaapi.ARM_bl
                            or lastInsn.itype == idaapi.ARM_bx):
                        # print('OK')
                        extendThumbFuncToLastPop(func.func_ea, lastInsn_ea,
                                                 verbose)

            ea += func.getSize(withPool=True)
        else:
            ea += Data.Data(ea).getSize()
Ejemplo n.º 16
0
def fix_unsynced_functions(source_units, address_space):
    synced_functions, synced_data, synced_unk = find_synced_units(source_units, address_space)

    count = 0
    ea_mismatch_count = 0
    size_mismatch_count = 0
    unidentified_count = 0
    for i, ea in enumerate(address_space):
        if not common.found_in_any(ea, [synced_functions, synced_data, synced_unk]):
            unit = source_unit.get_physical_unit(source_units, ea)
            unit_size = source_unit.compute_unit_size_from_index(address_space, i)

            if unit['unit']['id'] == AsmFile.UNIT_IDS.FUNCTION:

                # check if it's thumb or arm
                # print(unit['unit']['content'])
                if 'arm' in unit['unit']['content']:
                    print('ARM function found!')
                    is_thumb = 0
                else:
                    is_thumb = 1

                if Function.isFunction(ea):
                    func = Function.Function(ea)
                    if not func.func_ea == ea:
                        report = '{}::{}. {:X} <{}>: IDA func_ea=0x{:X} != ea=0x{:X}'.format(count, ea_mismatch_count, ea, unit['name'], func.func_ea, ea)
                        ops.delete_and_make_function(ea, unit_size, unit['name'], is_thumb)
                        ea_mismatch_count += 1
                        count += 1
                        print(report)
                    elif not func.getSize(withPool=True) == unit_size:
                        report = '{}::{}. {:X} <{}>: size mismatch: unit size {} != {}'.format(count, size_mismatch_count, ea, unit['name'], unit_size, func.getSize(withPool=True))
                        ops.delete_and_make_function(ea, unit_size, unit['name'], is_thumb)
                        size_mismatch_count += 1
                        count += 1
                        print(report)
                else:
                    report = '{}::{}. {:X} <{}>: unidentified function in IDA'.format(count, unidentified_count, ea, unit['name'])
                    ops.delete_and_make_function(ea, unit_size, unit['name'], is_thumb)
                    unidentified_count += 1
                    count += 1
                    print(report)
Ejemplo n.º 17
0
def fnrepl(start_ea, end_ea, oldstr, newstr, log=True):
    """
    replace a string once if detected in the names of all functions within range
    :param start_ea: start of the range
    :param end_ea: end of the range
    :param oldstr: string to replace
    :param newstr: replacement string
    """
    ea = start_ea
    while ea < end_ea:
        if Function.isFunction(ea):
            f = Function.Function(ea)
            if oldstr in f.getName():
                name = f.getName()
                f.setName(f.getName().replace(oldstr, newstr))
                if log: print("Renamed %s -> %s" % (name, f.getName()))

            ea += f.getSize(withPool=True)
        else:
            ea += Data.Data(ea).getSize()
Ejemplo n.º 18
0
def fixFunctionRanges(start_ea, end_ea):
    """
    Fixes all functions with improper returns, by finding their returns and changing their ranges
    For each function, it will ensure that it ends properly until the start of another function, or a data element
    with data xrefs to it. If it ends improperly, or there exists a matching return that is
    not part of the function, it's made part of the function
    This may not behave correctly around dead functions or null_subs. Run tools to Detect and fix those first.
    :param start_ea: start of the range to fix functions in
    :param end_ea: end of the range to fix functions in
    :return:
    """
    # only look 50 instructions ahead, for range change
    searchLimit = 50
    for func_ea in idautils.Functions(start_ea, end_ea):
        f = Function.Function(func_ea)
        # absolute end address of the function (if another function is detected or a data item with data xrefs is)
        stop_ea = f.func_ea + f.getSize()
        for i in range(stop_ea, stop_ea + searchLimit):
            if Function.isFunction(i) or Data.Data(i).getXRefsTo()[1]:
                stop_ea = i
                break
        # figure out the first return, and return type of this function. That should be consistent
        ret_ea = next.ret(f.func_ea, ui=False, hexOut=False)
        try:
            retType = InstDecoder.Inst(ret_ea).fields['magic']
            # modify the function range to include all returns
            if Function.isFunction(ret_ea):
                ret_ea = next.unkret(f.func_ea, ui=False, hexOut=False)
                # this ret_ea is not within the function, if the return type is different
                if InstDecoder.Inst(ret_ea).fields['magic'] != retType:
                    continue
            while f.func_ea < ret_ea < stop_ea:
                # detected that the function range is invalid, fix range
                print('ret %07X' % ret_ea)
                ret_ea = next.unkret(ret_ea, ui=False, hexOut=False)
                # this ret_ea is not within the function, if the return type is different
                if InstDecoder.Inst(ret_ea).fields['magic'] != retType:
                    break
                idc.add_func(func_ea, ret_ea + 2)
        except ValueError:
            continue
Ejemplo n.º 19
0
 def testFunctionDefinition(self):
     # type: () -> None
     """
     This decompiles the function and tries to get a C-style pointer #define
     macro
     It also tests just getting the prototype. This requires decompilation.
     """
     for td in self.testData:
         f = Function.Function(td['ea'])
         macro = "#define %s ((void (*) ()) (0x%08X +1))" % (td['name'],
                                                             td['ea'])
         Test.assertEquals(f.getFuncPtrCMacro(), macro,
                           "macro mismatch: %s" % f.getFuncPtrCMacro())
Ejemplo n.º 20
0
 def testXRefsFrom(self):
     # type: () -> None
     """
     Tests valid code/data xrefs to the function
     """
     for td in self.testData:
         f = Function.Function(td['ea'])
         # testing xrefs from function
         Test.assertEquals(
             f.getXRefsFrom(), td['xrefsFrom'],
             "XrefsFrom Mismatch. Expected: '%s', Actual: '%s'" %
             (self.xrefs2str(
                 td['xrefsFrom']), self.xrefs2str(f.getXRefsFrom())))
 def formatAccessSources(self):
     # type: () -> str
     output = ''
     if self.funcs:
         output += 'Functions:\n'
         for func_ea in self.funcs:
             output += '\t%07X <%s>\n' % (
                 func_ea, Function.Function(func_ea).getName())
     if self.data:
         output += 'Data:\n'
         for data_ea in self.data:
             output += '\t%07X <%s>\n' % (data_ea,
                                          Data.Data(data_ea).getName())
     return output
Ejemplo n.º 22
0
def removeStackVarUsages(self_ea, end_ea):
    madeChanges = False
    for func_ea in idautils.Functions(self_ea, end_ea):
        changedStackVar = False
        if Function.hasStackVars(func_ea):
            stackVars = Function.getStackVars(func_ea)
            # traverse data items of function and op_bin all
            ea = func_ea
            while ea < func_ea + Function.Function(func_ea).getSize(
                    withPool=False):
                d = Data.Data(ea)
                for name, off in stackVars:
                    if name != ' s' and name in d.getOrigDisasm():
                        changedStackVar = True
                        if not madeChanges: madeChanges = True
                        idc.op_hex(d.ea, 1)
                ea += d.getSize()
            if changedStackVar:
                print('%07X: stackvars op -> hex' % func_ea)
    if madeChanges:
        print("Removed all stack variable usages!")
    else:
        print("No stack variable usages to remove!")
Ejemplo n.º 23
0
 def testPoolData(self):
     for td in self.testData:
         f = Function.Function(td['ea'])
         i = 0
         for data in f.getPoolData():
             Test.assertEquals(
                 td['pool'][i].ea, data.ea,
                 "Pool data item %d mismatch. Expected: %08X, Actual: %08X"
                 % (i, td['pool'][i].ea, data.ea))
             Test.assertEquals(
                 td['pool'][i].getContent(), data.getContent(),
                 "Pool data item %d mismatch. Expected: %08X, Actual: %08X"
                 % (i, td['pool'][i].getContent(), data.getContent()))
             i += 1
Ejemplo n.º 24
0
def fakered(ea, end_ea=None, ui=True, hexOut=True):
    """
    This finds the next occurrance of a not a red code segment that has no return pattern to it, making it unlikely
    to belong to a function.
    :param ea: ea to start searching from
    :param ui: if True, jump to address automatically
    :param end_ea: the last address of the search range. If not specified, default is used.
    :param hexOut: output hex formatted ea range instead
    :return: range of ea of next fake red code segment
    """
    # TODO: change implementation to be based on return patterns?
    ea = red(ea, end_ea, ui=False, hexOut=False)
    start_ea = ea
    end_red_ea = idaapi.BADADDR
    # flag for when the whole red segment is finished and we can go to the next red code segment
    finishedSegment = False
    # condition for if the segment has already been invalidated before reaching its end
    isFake = True

    if not end_ea: end_ea = end_ea
    while ea < end_ea:
        d = Data.Data(ea)
        inst = InstDecoder.Inst(ea).fields

        # traverse red code, and find the end of the red segment
        if Function.isFunction(ea) and d.isCode() or not d.isCode():
            # update region end to this red code region
            end_red_ea = ea
            # confirm if the return is within range, then this isn't fake code. Find the next red!
            if isFake:  # or start_ea <= unkret(start_ea-instLimit, end_ea, ui=False, hexOut=False) < end_ea:
                break
            # search the next red region
            isFake = True
            start_ea = ea = red(end_red_ea, end_ea, ui=False, hexOut=False)

        # advance through the red code
        else:
            # simple function pattern,s, if it sorta looks like it can be a function, don't count it as fake.
            # this includes return patterns, and push/pop.
            if inst and (inst['magic'] == InstDecoder.INST_MOV_PC_LR
                         or inst['magic'] == InstDecoder.INST_BX
                         and inst['reg'] == 14 or inst['magic']
                         == InstDecoder.INST_PUSHPOP and inst['lr']):
                isFake = False
            ea += d.getSize()
    if ui: idc.jumpto(end_red_ea - 2)
    if hexOut: return '(%07X, %07X)' % (start_ea, end_red_ea)
    return (start_ea, end_red_ea)
Ejemplo n.º 25
0
 def testComments(self):
     # type: () -> None
     """
     Makes sure that function comments are viewable and modifiable
     """
     # there's an issue where GUI comments filter out system input comments, but
     # both exist anyway. Only one is showed in the GUI.
     for td in self.testData:
         f = Function.Function(td['ea'])
         Test.assertEquals(f.getComment(), td['cmt'],
                           "comment mismatch: '%s'" % f.getComment())
         f.setComment(td['cmt'] + "0")
         Test.assertEquals(f.getComment(), td['cmt'] + "0",
                           "setComment() not modifying")
         f.setComment(td['cmt'])
         Test.assertEquals(f.getComment(), td['cmt'],
                           "comment didn't return to original")
Ejemplo n.º 26
0
def deadfunc(ea, end_ea=None, ui=True, hexOut=True):
    """
    This finds the next occurrance of a dead function not recognized as a function (ie, red code or data)
    This can only find functions ranges it can guarantee, ie, only PUSH {..., LR} POP {..., PC} patterns.
    :param ea: ea to start searching from
    :param end_ea: the last address of the search range
    :param ui: if True, jump to address automatically
    :param hexOut: output hex formatted ea range instead
    :return: range of ea of next dead function
    """
    # don't count this item
    ea = Data.Data(ea).ea + Data.Data(ea).getSize()
    foundPush = False
    push_ea = idaapi.BADADDR
    pop_ea = idaapi.BADADDR
    push_regs = None
    if not end_ea: end_ea = end_ea
    while ea < end_ea:
        # the current item must not belong to a function, or have any data xrefs
        if not Function.isFunction(ea) and not Data.Data(ea).getXRefsTo()[1]:
            try:
                inst = InstDecoder.Inst(ea).fields
            except ValueError:
                ea += 2
                continue
            # if PUSH {..., LR}
            if inst and inst['magic'] == InstDecoder.INST_PUSHPOP and not inst[
                    'pop'] and inst['lr']:
                foundPush = True
                push_ea = ea
                push_regs = inst['Rlist']
            # detected a POP {..., PC} after the PUSH {..., LR}, and the registers match
            if (foundPush and inst
                    and inst['magic'] == InstDecoder.INST_PUSHPOP
                    and inst['pop'] and inst['lr']
                    and inst['Rlist'] == push_regs):
                pop_ea = ea
                break
        else:
            foundPush = False
        ea += 2

    if ui: idc.jumpto(push_ea)
    if hexOut: return '(%07X, %07X)' % (push_ea, pop_ea)
    return (push_ea, pop_ea)
Ejemplo n.º 27
0
 def getModuleFunctions(self):
     """
     This traverses all segments, and all defined modules to retrieve all functions with that module name.
     :return: a list of Functions that are in this module, saved in the database.
     """
     output = []
     for seg_ea in idautils.Segments():
         for func_ea in idautils.Functions(idc_bc695.SegStart(seg_ea),
                                           idc_bc695.SegEnd(seg_ea)):
             func = Function.Function(func_ea)
             # if the function starts with '<moduleName>'...
             funcName = func.getName()
             inModel = len(funcName) >= len(
                 self.name) + 1 and funcName[0:len(self.name) +
                                             1] == self.name + '_'
             if inModel:
                 output.append(func)
     return output
Ejemplo n.º 28
0
 def nextred(self, ea, ui=True):
     """
     Looks for code items outside function items. The first detected is returned
     :param ea: ea to start searching from
     :param ui: if True, jump to address automatically
     :return: hex formatted ea of next name
     """
     # don't count this item
     ea = Data.Data(ea).ea + Data.Data(ea).getSize()
     output = idaapi.BADADDR
     while ea < self.end_ea:
         d = Data.Data(ea)
         if d.isCode() and not Function.isFunction(d.ea):
             output = ea
             break
         ea += d.getSize()
     if ui: idaapi.jumpto(ea)
     return '%07X' % output
Ejemplo n.º 29
0
def removeRedCode(start_ea, end_ea):
    """
    unconditionally removes all red code within a specified region
    :param start_ea: start of the region
    :param end_ea: end of the region
    :return:
    """
    srchNext = next
    redStart_ea = redEnd_ea = srchNext.red(start_ea, end_ea, ui=False)
    while redEnd_ea < end_ea:
        d = Data.Data(redEnd_ea)
        while d.isCode() and not Function.isFunction(d.ea):
            redEnd_ea += 2
            d = Data.Data(redEnd_ea)
        # change to bytes
        print("%07X: del red code (%07X, %07X)" %
              (redStart_ea, redStart_ea, redEnd_ea))
        idc.del_items(redStart_ea, 0, redEnd_ea - redStart_ea)
        redStart_ea = redEnd_ea = srchNext.red(redEnd_ea, end_ea, ui=False)
Ejemplo n.º 30
0
def red(ea, end_ea=None, ui=True):
    """
    Looks for code items outside function items. The first detected is returned
    :param ea: ea to start searching from
    :param end_ea: the last address of the search range, or default if None
    :param ui: if True, jump to address automatically
    :return: ea of next name
    """
    # don't count this item
    ea = Data.Data(ea).ea + Data.Data(ea).getSize()
    output = idaapi.BADADDR
    if not end_ea: end_ea = end_ea
    while ea < end_ea:
        d = Data.Data(ea)
        if d.isCode() and not Function.isFunction(d.ea):
            output = ea
            break
        ea += d.getSize()
    if ui: idaapi.jumpto(ea)
    return output