def _event(cls): while True: # cmt_changing event ea, rpt, new = (yield) fn = idaapi.get_func(ea) old = utils.string.of(idaapi.get_func_cmt(fn, rpt)) o, n = internal.comment.decode(old), internal.comment.decode(new) # update references before we update the comment cls._update_refs(fn, o, n) # wait for cmt_changed event newea, nrpt, none = (yield) # now we can fix the user's new coment if (newea, nrpt, none) == (ea, rpt, None): ncmt = utils.string.of(idaapi.get_func_cmt(fn, rpt)) if (ncmt or '') != new: logging.warn(u"{:s}.event() : Comment from event for function {:#x} is different from database. Expected comment ({!s}) is different from current comment ({!s}).".format('.'.join((__name__, cls.__name__)), ea, utils.string.repr(new), utils.string.repr(ncmt))) # if it's non-repeatable, then fix it. # if not nrpt: # idaapi.set_func_cmt(fn, '', nrpt) # # write the tag back to the function # if internal.comment.check(new): idaapi.set_func_cmt(fn, utils.string.to(internal.comment.encode(n)), True) # # otherwise, write the comment back as long as it's valid # elif new: idaapi.set_func_cmt(fn, utils.string.to(new), True) # # otherwise, the user has deleted it..so update its refs. # else: cls._delete_refs(fn, n) # write the tag back to the function if internal.comment.check(new): idaapi.set_func_cmt(fn, utils.string.to(internal.comment.encode(n)), rpt) elif new: idaapi.set_func_cmt(fn, utils.string.to(new), rpt) else: cls._delete_refs(fn, n) continue # if the changed event doesn't happen in the right order logging.fatal(u"{:s}.event() : Comment events are out of sync for function {:#x}, updating tags from previous comment. Expected comment ({!s}) is different from current comment ({!s}).".format('.'.join((__name__, cls.__name__)), ea, utils.string.repr(o), utils.string.repr(n))) # delete the old comment cls._delete_refs(fn, o) idaapi.set_func_cmt(fn, '', rpt) logging.warn(u"{:s}.event() : Deleted comment for function {:#x} was ({!s}).".format('.'.join((__name__, cls.__name__)), ea, utils.string.repr(o))) # new comment newfn = idaapi.get_func(newea) new = utils.string.of(idaapi.get_func_cmt(newfn, nrpt)) n = internal.comment.decode(new) cls._create_refs(newfn, n) continue return
def export_func_names(fname): s = idautils.Strings(False) s.setup(strtypes=idautils.Strings.STR_UNICODE | idautils.Strings.STR_C) jsontable = [] for v in s: if v is None: print("Failed to retrieve string index") else: xrefs = [x.frm for x in idautils.XrefsTo(v.ea)] if len(xrefs) != 1: continue print("%x: len=%d type=%d -> '%s'" % (v.ea, v.length, v.type, unicode(v))) d = {} d['string'] = unicode(v) d['str_type'] = v.type func = idaapi.get_func(xrefs[0]) if func is None: continue d['func_name'] = idc.GetFunctionName(func.startEA) d['func_demangled'] = demangle(d['func_name']) d['func_c_decl'] = idc.GetType(func.startEA) d['func_comment'] = idaapi.get_func_cmt(func, 1) jsontable.append(d) f = open(fname,'w') json.dump(jsontable,f,indent=4) f.close()
def changing(cls, cb, a, cmt, repeatable): logging.debug( u"{:s}.changing({!s}, {:#x}, {!s}, {:d}) : Received comment.changing event for a {:s} comment at {:x}." .format('.'.join((__name__, cls.__name__)), utils.string.repr(cb), interface.range.start(a), utils.string.repr(cmt), repeatable, 'repeatable' if repeatable else 'non-repeatable', interface.range.start(a))) fn = idaapi.get_func(interface.range.start(a)) if fn is None and not cmt: return oldcmt = utils.string.of(idaapi.get_func_cmt(fn, repeatable)) try: cls.event.send((interface.range.start(fn), bool(repeatable), utils.string.of(cmt))) except StopIteration, e: logging.fatal( u"{:s}.changing({!s}, {:#x}, {!s}, {:d}) : Unexpected termination of event handler. Re-instantiating it." .format('.'.join((__name__, cls.__name__)), utils.string.repr(cb), interface.range.start(a), utils.string.repr(cmt), repeatable)) cls.event = cls._event() next(cls.event)
def changed(cls, cb, a, cmt, repeatable): fn = idaapi.get_func(a.startEA) newcmt = idaapi.get_func_cmt(fn, repeatable) try: cls.event.send((fn.startEA, bool(repeatable), None)) except StopIteration, e: logging.fatal("{:s}.changed : Unexpected termination of event handler. Re-instantiating it.".format('.'.join((__name__,cls.__name__)))) cls.event = cls._event(); next(cls.event)
def _handle_tags(self, fn, fn_an, known_refs): if known_refs: known_refs = dict(known_refs) for k, names in known_refs.items(): existing = set(fn_an['tags'][k]) new = set(names) - existing if new: fn_an['tags'][k] += list(new) tags = dict(fn_an['tags']) if not tags: return print 'fn: %#08x tags: %s' % (fn.startEA, tags) cmt = idaapi.get_func_cmt(fn, True) if cmt: cmt += '\n' s = str(tags.keys()) name = idaapi.get_ea_name(fn.startEA) item = {'ea': fn.startEA, 'name': name, 'tags': tags} if not cmt or s not in cmt: idaapi.set_func_cmt(fn, '%sTAGS: %s' % (cmt or '', s), True) # self.mark_position(fn.startEA, 'TAGS: %s' % s) for tag in tags: if tag not in self._data: self._data[tag] = list() self._data[tag].append(item)
def changed(cls, cb, a, cmt, repeatable): logging.debug("{:s}.changing(...) : Received comment.changed at {:x} cb={!r} repeatable={:d} comment={!r}".format('.'.join((__name__, cls.__name__)), a.startEA, cb, repeatable, cmt)) fn = idaapi.get_func(a.startEA) if fn is None and not cmt: return newcmt = idaapi.get_func_cmt(fn, repeatable) try: cls.event.send((fn.startEA, bool(repeatable), None)) except StopIteration, e: logging.fatal("{:s}.changed(...) : Unexpected termination of event handler. Re-instantiating it.".format('.'.join((__name__, cls.__name__)))) cls.event = cls._event(); next(cls.event)
def _handle_tags(self, fn, fn_an): tags = dict(fn_an['tags']) if not tags: return print 'fn: %#08x tags: %s' % (fn.startEA, tags) cmt = idaapi.get_func_cmt(fn, True) if cmt: cmt += '\n' s = str(tags.keys()) name = idaapi.get_ea_name(fn.startEA) item = {'ea': fn.startEA, 'name': name, 'tags': tags} if not cmt or s not in cmt: idaapi.set_func_cmt(fn, '%sTAGS: %s' % (cmt or '', s), True) # self.mark_position(fn.startEA, 'TAGS: %s' % s) for tag in tags: if tag not in self._data: self._data[tag] = list() self._data[tag].append(item)
def handle_function_comments(delta, segs): for f in get_all_funcs(): for cmt_type in (True, False): cmt = idaapi.get_func_cmt(f, cmt_type) if cmt: new_cmt = rebase_comment(segs, delta, cmt) if new_cmt: idaapi.set_func_cmt(f, cmt, cmt_type) cmts = idaapi.restore_user_cmts(f.start_ea) if not cmts: continue changed = False for (treeloc, citm) in cmts.items(): if citm: citm = citm.c_str() new_cmt = rebase_comment(segs, delta, citm) if new_cmt: changed = True it = cmts.find(treeloc) cmts.erase(it) cmts.insert(treeloc, idaapi.citem_cmt_t(new_cmt)) if changed: idaapi.save_user_cmts(f.start_ea, cmts)
def process_function(arch, func_ea): func_end = idc.FindFuncEnd(func_ea) packet = DismantlerDataPacket() ida_chunks = get_chunks(func_ea) chunks = set() # Add to the chunks only the main block, containing the # function entry point # chunk = get_flow_code_from_address(func_ea) if chunk: chunks.add(chunk) # Make "ida_chunks" a set for faster searches within ida_chunks = set(ida_chunks) ida_chunks_idx = dict(zip([c[0] for c in ida_chunks], ida_chunks)) func = idaapi.get_func(func_ea) comments = [idaapi.get_func_cmt(func, 0), idaapi.get_func_cmt(func, 1)] # Copy the list of chunks into a queue to process # chunks_todo = [c for c in chunks] while True: # If no chunks left in the queue, exit if not chunks_todo: if ida_chunks: chunks_todo.extend(ida_chunks) else: break chunk_start, chunk_end = chunks_todo.pop() if ida_chunks_idx.has_key(chunk_start): ida_chunks.remove(ida_chunks_idx[chunk_start]) del ida_chunks_idx[chunk_start] for head in idautils.Heads(chunk_start, chunk_end): comments.extend((idaapi.get_cmt(head, 0), idaapi.get_cmt(head, 1))) comment = '\n'.join([c for c in comments if c is not None]) comment = comment.strip() if comment: packet.add_comment(head, comment) comments = list() if idc.isCode(idc.GetFlags(head)): instruction = arch.process_instruction(packet, head) # if there are other references than # flow add them all. if list(idautils.CodeRefsFrom(head, 0)): # for each reference, including flow ones for ref_idx, ref in enumerate( idautils.CodeRefsFrom(head, 1)): if arch.is_call(instruction): # This two conditions must remain separated, it's # necessary to enter the enclosing "if" whenever # the instruction is a call, otherwise it will be # added as an uncoditional jump in the last else # if ref in list(idautils.CodeRefsFrom(head, 0)): packet.add_direct_call(head, ref) elif ref_idx > 0 and arch.is_conditional_branch( instruction): # The ref_idx is > 0 in order to avoid processing the # normal flow reference which would effectively imply # that the conditional branch is processed twice. # It's done this way instead of changing the loop's head # from CodeRefsFrom(head, 1) to CodeRefsFrom(head, 0) in # order to avoid altering the behavior of other conditions # which rely on it being so. # FIXME # I don't seem to check for the reference here # to point to valid, defined code. I suspect # this could lead to a failure when exporting # if such situation appears. I should test if # it's a likely scenario and probably just add # an isHead() or isCode() to address it. packet.add_conditional_branch_true(head, ref) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, ref) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) #skip = False for ref in idautils.DataRefsFrom(head): packet.add_data_reference(head, ref) # Get a data reference from the current reference's # location. For instance, if 'ref' points to a valid # address and such address contains a data reference # to code. target = list(idautils.DataRefsFrom(ref)) if target: target = target[0] else: target = None if target is None and arch.is_call(instruction): imp_name = idc.Name(ref) imp_module = get_import_module_name(ref) imported_functions.add((ref, imp_name, imp_module)) packet.add_indirect_virtual_call(head, ref) elif target is not None and idc.isHead(target): # for calls "routed" through this reference if arch.is_call(instruction): packet.add_indirect_call(head, target) # for unconditional jumps "routed" through this reference elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, target) # for conditional "routed" through this reference elif arch.is_conditional_branch(instruction): packet.add_conditional_branch_true(head, target) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) f = FunctionAnalyzer(arch, func_ea, packet) instrumentation.new_packet(packet) instrumentation.new_function(f)
def getComment(ea, repeatable=1): fn = byAddress(ea) return idaapi.get_func_cmt(fn, repeatable)
def repeat(self): """Repeatable Function Comment""" return idaapi.get_func_cmt(self._function._func, True)
def regular(self): """Function Comment""" return idaapi.get_func_cmt(self._function._func, False)
def process_function(arch, func_ea): func_end = idc.FindFuncEnd(func_ea) packet = DismantlerDataPacket() ida_chunks = get_chunks(func_ea) chunks = set() # Add to the chunks only the main block, containing the # function entry point # chunk = get_flow_code_from_address(func_ea) if chunk: chunks.add( chunk ) # Make "ida_chunks" a set for faster searches within ida_chunks = set(ida_chunks) ida_chunks_idx = dict(zip([c[0] for c in ida_chunks], ida_chunks)) func = idaapi.get_func(func_ea) comments = [idaapi.get_func_cmt(func, 0), idaapi.get_func_cmt(func, 1)] # Copy the list of chunks into a queue to process # chunks_todo = [c for c in chunks] while True: # If no chunks left in the queue, exit if not chunks_todo: if ida_chunks: chunks_todo.extend(ida_chunks) else: break chunk_start, chunk_end = chunks_todo.pop() if ida_chunks_idx.has_key(chunk_start): ida_chunks.remove(ida_chunks_idx[chunk_start]) del ida_chunks_idx[chunk_start] for head in idautils.Heads(chunk_start, chunk_end): comments.extend( (idaapi.get_cmt(head, 0), idaapi.get_cmt(head, 1)) ) comment = '\n'.join([c for c in comments if c is not None]) comment = comment.strip() if comment: packet.add_comment(head, comment) comments = list() if idc.isCode(idc.GetFlags(head)): instruction = arch.process_instruction(packet, head) # if there are other references than # flow add them all. if list( idautils.CodeRefsFrom(head, 0) ): # for each reference, including flow ones for ref_idx, ref in enumerate(idautils.CodeRefsFrom(head, 1)): if arch.is_call(instruction): # This two conditions must remain separated, it's # necessary to enter the enclosing "if" whenever # the instruction is a call, otherwise it will be # added as an uncoditional jump in the last else # if ref in list( idautils.CodeRefsFrom(head, 0) ): packet.add_direct_call(head, ref) elif ref_idx>0 and arch.is_conditional_branch(instruction): # The ref_idx is > 0 in order to avoid processing the # normal flow reference which would effectively imply # that the conditional branch is processed twice. # It's done this way instead of changing the loop's head # from CodeRefsFrom(head, 1) to CodeRefsFrom(head, 0) in # order to avoid altering the behavior of other conditions # which rely on it being so. # FIXME # I don't seem to check for the reference here # to point to valid, defined code. I suspect # this could lead to a failure when exporting # if such situation appears. I should test if # it's a likely scenario and probably just add # an isHead() or isCode() to address it. packet.add_conditional_branch_true(head, ref) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, ref) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) #skip = False for ref in idautils.DataRefsFrom(head): packet.add_data_reference(head, ref) # Get a data reference from the current reference's # location. For instance, if 'ref' points to a valid # address and such address contains a data reference # to code. target = list( idautils.DataRefsFrom(ref) ) if target: target = target[0] else: target = None if target is None and arch.is_call(instruction): imp_name = idc.Name(ref) imp_module = get_import_module_name(ref) imported_functions.add((ref, imp_name, imp_module)) packet.add_indirect_virtual_call(head, ref) elif target is not None and idc.isHead(target): # for calls "routed" through this reference if arch.is_call(instruction): packet.add_indirect_call(head, target) # for unconditional jumps "routed" through this reference elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, target) # for conditional "routed" through this reference elif arch.is_conditional_branch(instruction): packet.add_conditional_branch_true(head, target) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) f = FunctionAnalyzer(arch, func_ea, packet) instrumentation.new_packet(packet) instrumentation.new_function(f)
def comment(fn, comment=None, repeatable=1): fn = by(fn) if comment is None: return idaapi.get_func_cmt(fn, repeatable) return idaapi.set_func_cmt(fn, comment, repeatable)