def analyse_function_returns(): """Find all function call instructions, and mark the blocks associated with their return addresses as having their addresses taken.""" log.info("Analysing targets of function call return instructions") for block in program.basic_blocks(): term_inst = block.terminator if not term_inst or not term_inst.is_function_call(): continue return_target_block = program.get_basic_block(term_inst.next_ea) return_target_block.address_is_taken = True
def analyse_indirect_jump(block, jump_inst, blocks): """Analyse an indirect jump and try to determine its targets.""" log.info("Analysing indirect jump at {:08x}".format(jump_inst.ea)) si = idaapi.get_switch_info_ex(jump_inst.ea) target_eas = set() if si: num_targets = si.get_jtable_size() log.info("IDA identified a jump table at {:08x} with {} targets".format( jump_inst.ea, num_targets)) target_eas.update(idautils.CodeRefsFrom(jump_inst.ea, True)) for target_ea in target_eas: block = program.get_basic_block(target_ea) block.address_is_taken = True
def analyse_indirect_jump(block, jump_inst, blocks): """Analyse an indirect jump and try to determine its targets.""" log.info("Analysing indirect jump at {:08x}".format(jump_inst.ea)) si = idaapi.get_switch_info_ex(jump_inst.ea) target_eas = set() if si: num_targets = si.get_jtable_size() log.info( "IDA identified a jump table at {:08x} with {} targets".format( jump_inst.ea, num_targets)) target_eas.update(idautils.CodeRefsFrom(jump_inst.ea, True)) for target_ea in target_eas: block = program.get_basic_block(target_ea) block.address_is_taken = True
def analyse_callbacks(): """Analyse functions that are used as callbacks.""" global POSSIBLE_CODE_REFS log.info("Analysing callbacks") for ea in POSSIBLE_CODE_REFS: if program.has_basic_block(ea): block = program.get_basic_block(ea) if not block.address_is_taken: block.address_is_taken = True log.info("Block {:08x} is a callback".format(ea)) for block in program.basic_blocks(): if not block.address_is_taken: if len(tuple(idautils.DataRefsTo(block.ea))): block.address_is_taken = True log.info("Block {:08x} is a callback".format(block.ea))
def analyse_callbacks(): """Analyse functions that are used as callbacks.""" global POSSIBLE_CODE_REFS log.info("Analysing callbacks") for ea in POSSIBLE_CODE_REFS: if program.has_basic_block(ea): block = program.get_basic_block(ea) if not block.address_is_taken: block.address_is_taken = True log.info("Block {:08x} is a callback (1)".format(ea)) for block in program.basic_blocks(): if not block.address_is_taken: if len(tuple(idautils.DataRefsTo(block.ea))): block.address_is_taken = True log.info("Block {:08x} is a callback (2)".format(block.ea))
def analyse_subroutine(sub): """Goes through the basic blocks of an identified function.""" if len(sub.blocks): return # We've already processed this subroutine. log.info("Analysing subroutine {} at {:08x}".format(sub.name, sub.ea)) block_head_eas = set() block_head_eas.add(sub.ea) # Try to get IDA to give us function information. f = idaapi.get_func(sub.ea) if f: for b in idaapi.FlowChart(f): block_head_eas.add(b.startEA) for chunk_start_ea, chunk_end_ea in idautils.Chunks(sub.ea): block_head_eas.add(chunk_start_ea) else: log.warning("IDA does not recognise subroutine at {:08x}".format( sub.ea)) # Iteratively scan for block heads. This will do linear sweeps looking for # block terminators. These linear sweeps do not consider flows incoming # flows from existing blocks that logically split a block into two. found_block_eas = set() while len(block_head_eas): block_head_ea = block_head_eas.pop() if block_head_ea in found_block_eas: continue found_block_eas.add(block_head_ea) # Try to make sure that analysis will terminate if we accidentally # walk through a noreturn call. if block_head_ea != sub.ea and program.has_subroutine(block_head_ea): continue log.debug("Found block head at {:08x}".format(block_head_ea)) if program.has_basic_block(block_head_ea): existing_block = program.get_basic_block(block_head_ea) term_inst = existing_block.terminator assert term_inst is not None else: term_inst = find_linear_terminator(block_head_ea) log.debug("Linear terminator of {:08x} is {:08x}".format( block_head_ea, term_inst.ea)) succ_eas = tuple(get_static_successors(term_inst)) if succ_eas: log.debug("Static successors of {:08x} are {}".format( term_inst.ea, ", ".join("{:08x}".format(sea) for sea in succ_eas))) block_head_eas.update(succ_eas) # Create blocks associated with this subroutine for each block # head that the prior analysis discovered. blocks = [] for block_head_ea in found_block_eas: if block_head_ea != sub.ea and program.has_subroutine(block_head_ea): continue block = sub.get_basic_block(block_head_ea) blocks.append(block) log.debug("Subroutine {:08x} has {} blocks".format(sub.ea, len(blocks))) # Analyse the blocks blocks.sort(key=lambda b: b.ea) for block in blocks: analyse_block(sub, block) log.debug("Block at {:08x} has {} instructions".format( block.ea, len(block.instructions)))
def analyse_subroutine(sub): """Goes through the basic blocks of an identified function.""" if len(sub.blocks): return # We've already processed this subroutine. log.info("Analysing subroutine {} at {:08x}".format(sub.name, sub.ea)) block_head_eas = set() block_head_eas.add(sub.ea) # Try to get IDA to give us function information. f = idaapi.get_func(sub.ea) if f: for b in idaapi.FlowChart(f): block_head_eas.add(b.startEA) for chunk_start_ea, chunk_end_ea in idautils.Chunks(sub.ea): block_head_eas.add(chunk_start_ea) else: log.warning("IDA does not recognise subroutine at {:08x}".format(sub.ea)) # Iteratively scan for block heads. This will do linear sweeps looking for # block terminators. These linear sweeps do not consider flows incoming # flows from existing blocks that logically split a block into two. found_block_eas = set() while len(block_head_eas): block_head_ea = block_head_eas.pop() if block_head_ea in found_block_eas: continue if not is_code(block_head_ea): log.error("Block head at {:08x} is not code.".format(block_head_ea)) continue found_block_eas.add(block_head_ea) # Try to make sure that analysis will terminate if we accidentally # walk through a noreturn call. if block_head_ea != sub.ea and program.has_subroutine(block_head_ea): continue log.debug("Found block head at {:08x}".format(block_head_ea)) if program.has_basic_block(block_head_ea): existing_block = program.get_basic_block(block_head_ea) term_inst = existing_block.terminator assert term_inst is not None else: term_inst = find_linear_terminator(block_head_ea) if not term_inst: log.error("Block at {:08x} has no terminator!".format(block_head_ea)) found_block_eas.remove(block_head_ea) continue log.debug("Linear terminator of {:08x} is {:08x}".format( block_head_ea, term_inst.ea)) succ_eas = tuple(get_static_successors(term_inst)) if succ_eas: log.debug("Static successors of {:08x} are {}".format( term_inst.ea, ", ".join("{:08x}".format(sea) for sea in succ_eas))) block_head_eas.update(succ_eas) # Create blocks associated with this subroutine for each block # head that the prior analysis discovered. blocks = [] for block_head_ea in found_block_eas: if block_head_ea != sub.ea and program.has_subroutine(block_head_ea): continue block = sub.get_basic_block(block_head_ea) blocks.append(block) log.debug("Subroutine {:08x} has {} blocks".format(sub.ea, len(blocks))) # Analyse the blocks blocks.sort(key=lambda b: b.ea) for block in blocks: analyse_block(sub, block) log.debug("Block at {:08x} has {} instructions".format( block.ea, len(block.instructions)))