示例#1
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)
示例#3
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()
示例#4
0
def extendThumbFuncToLastPop(func_ea, lastInsn_ea, verbose=True):
    """
    Looks for another POP {..., PC}. Stops at the start of a new function, or at the start of
    labeled data. Otherwise, it makes sure the code is disassembled, and is thumb, and it extends the
    range of the function to the found POP {..., PC}.
    A corner case not accounted by this algorithm, is if the data is in the middle of code, but
    is jumped over.
    :param func_ea: addr to function to fix
    :param lastInsn_ea: address to the last instruction within the function, as registered in the IDB.
    :return: whether a fix ocurred or not
    """
    ea = lastPop_ea = lastInsn_ea
    while not idc.Name(ea) or not idc.isData(idc.GetFlags(ea)):
        if idc.GetReg(ea, 'T') == 0:
            idc.SetRegEx(ea, 'T', 1, idc.SR_user)

        # if idc.isData(idc.GetFlags(ea)):
        #     # if not thumb, make thumb
        #     idc.del_items(ea, 0, 2)
        #     idc.MakeCode(ea)

        if Instruction.isInsn(ea):
            insn = Instruction.Insn(ea)
            # update last POP {..., PC} detected
            if insn.itype == idaapi.ARM_pop and ((insn.getPushPopFlags() &
                                                  (1 << 15)) != 0):
                lastPop_ea = ea
            # stop condition, assuming no  PUSH {..., LR} in current function
            if insn.itype == idaapi.ARM_push and ((insn.getPushPopFlags() &
                                                   (1 << 14)) != 0):
                break

        ea += idaapi.get_item_size(ea)

    # extend last function to last pop detected
    if lastPop_ea != lastInsn_ea:
        if verbose:
            print('%07X: End -> %07X <%s>' %
                  (func_ea, lastPop_ea, Data.Data(lastPop_ea).getDisasm()))
        idc.SetFunctionEnd(func_ea, lastPop_ea + 2)
        return True
    return False
def traceRegWrites(start_ea, end_ea, reg):
    """
    Takes a list of instructions as it is unsafe to reinitiate Instruction objects
    Specifies all the times the register has been written
    :param insts: list of instructions to analyze.
    :param reg: int. register number to check writes to
    :return: list of eas of writes, or False if not in code
    """

    writes = []
    ea = start_ea
    while ea < end_ea:
        insn = Instruction.Insn(ea)
        # if the reg is written, its writeCount increases
        if insn.isComputationalInsn():
            if insn.ops[0].reg == reg:
                writes.append(ea)
        ea += idc.get_item_size(ea)
    return writes
def traceRegVar(start_ea, end_ea, reg, writeIdx):
    """
    Takes a list of instructions as it is unsafe to reinitiate Instruction objects
    Runs through the function and traces all accesses to a register with a particular writeIdx.
    The writeIdx with the register forms the current local variable in that register.
    :param insts: instructions to analyze. list of IDAItems.Instruction objects
    :param reg: the register to trace, 0 to 15
    :param writeIdx: the counts of writes to this register before being traced. If 0, it will be traced
    as an input register to the function. If 1, it will have to be written to once before being traced.
    And so on.
    :return: list of EAs of register usages/reads or False if not in a valid function
    """

    writeCount = 0
    accesses = []
    ea = start_ea
    while ea < end_ea:
        insn = Instruction.Insn(ea)
        # if the reg is written, its writeCount increases
        if insn.isComputationalInsn():
            # check read accesses, even if a write occurs to this register, its previous value can be read
            for i in range(1, ida_ua.UA_MAXOP):
                if (insn.ops[i].type in [ida_ua.o_reg, ida_ua.o_displ
                                         ]  # normal reg or ldr/str
                        and writeCount == writeIdx and insn.ops[i].reg == reg):
                    # print(hex(ea), idc.GetDisasm(ea))
                    accesses.append(ea)
                    break
            if insn.ops[0].reg == reg:
                writeCount += 1
        else:
            for i in range(0, ida_ua.UA_MAXOP):
                if (insn.ops[i].type in [ida_ua.o_reg, ida_ua.o_displ
                                         ]  # normal reg or ldr/str
                        and writeCount == writeIdx and insn.ops[i].reg == reg):
                    accesses.append(ea)
                    break
        ea += idc.get_item_size(ea)
    return accesses