def getSize(self, withPool=False): """ Computes the size of the function the first time this is called, and caches that computation for later Parsed Comment commands: <endpool> specifies the last element in the pool. That element's size is included in the pool. to specify a function has no pool at all, put the comment command at its last instruction. :param withPool: (bool) somewhat of a heuristic. Computes the pool size as simply the amount of bytes since the function's code portion finished (endEA) until a new code head is detected :return: Returns the size of the Function in bytes: EndEA - StartEA (if no pool selected, otherwise + pool) """ if not withPool: return self.func.end_ea - self.func.start_ea head = self.func.end_ea # check if the function is set to have no pool instSize = self.isThumb() and 2 or 4 endCmt = idc.Comment(self.func.end_ea - instSize) if endCmt and '<endpool>' in endCmt: return self.func.end_ea - self.func.start_ea while not idc.isCode(idc.GetFlags(head)): # manual pool computation, trust and assume that this is the last element in the pool! if idc.Comment(head) and '<endpool>' in idc.Comment(head): head += idc.get_item_size(head) break # advance to next data element head += idc.get_item_size(head) return head - self.func.start_ea
def walk_stack(ea, func_args): found_args = [] while len(found_args) < func_args: ea = previous_ea(ea) if idc.Comment(ea): found_args.append((ea, idc.Comment(ea))) return found_args
def get_patch_byte(self, ea, fpos, org_val, patch_val): # Aggregate contiguous bytes (base ea + length) # NOTE: Looking at the last item [-1] is sufficient # since we are dealing with sorted data. if len(self.items_data) and (ea - self.items_data[-1][0] == self.items_data[-1][2]): # Increment length self.items_data[-1][2] += 1 self.items[-1][2] = str(self.items_data[-1][2]) # Append patched bytes self.items_data[-1][3].append(patch_val) self.items[-1][3] = " ".join(["%02X" % x for x in self.items_data[-1][3]]) # Append original bytes self.items_data[-1][4].append(org_val) self.items[-1][4] = " ".join(["%02X" % x for x in self.items_data[-1][4]]) # Add new patch byte to the list else: name = idc.SegName(ea) if idc.GetFunctionName(ea) or idc.Name(ea): name += ": %s" % idc.GetFunctionName(ea) or idc.Name(ea) comment = idc.Comment(ea) or idc.RptCmt(ea) or "" # DATA STORAGE FORMAT: address, function / fpos, len, patched byte(s), original byte(s), comments self.items.append( ["%08X" % ea, name, "1", "%02X" % patch_val, "%02X" % org_val, comment]) self.items_data.append([ ea, fpos, 1, [patch_val], [org_val], None] ) return 0
def get_single_comment(ea, is_func=False): """IDA has repeatable and regular comments. BN only has regular comments. This function constructs a single comment from both repeatable and regular comments """ regular_comment = "" repeatable_comment = "" if is_func: regular_comment = idc.GetFunctionCmt(ea, 0) repeatable_comment = idc.GetFunctionCmt(ea, 1) else: regular_comment = idc.Comment(ea) repeatable_comment = idc.RptCmt(ea) if regular_comment is None: return repeatable_comment elif repeatable_comment is None: return regular_comment elif repeatable_comment is None and regular_comment is None: return None else: if len(regular_comment) == 0: return repeatable_comment if len(repeatable_comment) == 0: return repeatable_comment return regular_comment + "\n" + repeatable_comment return None
def get_patch_byte(self, ea, fpos, org_val, patched_val): org_byte = "%02x" % org_val patched_byte = "%02x" % patched_val if self.prev_addr is None or ea != (self.prev_addr + 1): name = idc.SegName(ea) if idc.GetFunctionName(ea) or idc.Name(ea): name += ": %s" % idc.GetFunctionName(ea) or idc.Name(ea) comment = idc.Comment(ea) or idc.RptCmt(ea) or "" self.patched_bytes.append({ 'name': name, 'begin_addr': ea, 'original': org_byte, 'patched': patched_byte, 'comment': comment }) else: self.patched_bytes[-1]['original'] += org_byte self.patched_bytes[-1]['patched'] += patched_byte self.prev_addr = ea return 0
def append_comment(ea, s, repeatable=False): """ add the given string as a (possibly repeating) comment to the given address. does not add the comment if it already exists. adds the comment on its own line. Args: ea (int): the address at which to add the comment. s (str): the comment text. repeatable (bool): if True, set a repeatable comment. Raises: UnicodeEncodeError: if the given string is not ascii. """ # see: http://blogs.norman.com/2011/security-research/improving-ida-analysis-of-x64-exception-handling s = s.encode("ascii") if repeatable: string = idc.RptCmt(ea) else: string = idc.Comment(ea) if not string: string = s # no existing comment else: if s in string: # ignore duplicates return string = string + "\\n" + s if repeatable: idc.MakeRptCmt(ea, string) else: idc.MakeComm(ea, string)
def OnDeleteLine(self, n): ans = idaapi.askyn_c( 1, "HIDECANCEL\nAre you sure you want to delete function [%s] @ [%s]?" % (self.items[n][3], self.items[n][4])) if ans == 1: asms = Assembler.LoadSavedAssemblers() item = int(self.items[n][2], 16) if asms != None and len(asms.keys()) > 0: for asm in asms.itervalues(): if asm.functions.has_key(item): print "Removed [%08x]!" % item del asm.functions[item] asm.SaveState() opty_ea = int(self.items[n][4], 16) print "set_name[%08x]" % opty_ea idc.MakeComm(opty_ea, "") idaapi.set_name(opty_ea, "") idc.DelFunction(opty_ea) comment = idc.Comment(item) comment = re.sub(r"(?i)OPTY@\[[\d+abcdef]+\];\s*", "", comment) idc.MakeComm(item, comment) self.populate_items() return n
def visit_line(self, line): comm = {} for address in line.extract_addresses(): idacomm = idc.Comment(address) newcomm = idacomm and bap_comment.parse(idacomm) or {} union(comm, newcomm) if comm: line.widget.line += COLOR_START line.widget.line += bap_comment.dumps(comm) line.widget.line += COLOR_END
def activate(self, ctx): pos = idc.ScreenEA() # Get current comment for this instruction and remove the C define from it, if present comment = idc.Comment(pos) code = get_operand_value(pos) define = ioctl_decoder.get_define(code) comment = comment.replace(define, "") idc.MakeComm(pos, comment) # Remove the ioctl from the valid list and add it to the invalid list to avoid 'find_all_ioctls' accidently re-indexing it. ioctl_tracker.remove_ioctl(pos)
def make_comment(pos, string): """ Creates a comment with contents `string` at address `pos`. If the address is already commented append the new comment to the existing comment """ current_comment = idc.Comment(pos) if not current_comment: idc.MakeComm(pos, string) elif string not in current_comment: idc.MakeComm(pos, current_comment + " " + string)
def cmt_changed(self, *args): """ A comment changed somewhere """ addr, rpt = args if rpt: cmt = idc.RptCmt(addr) else: cmt = idc.Comment(addr) if not SkelUtils.filter_coms_blacklist(cmt): self.skel_conn.push_comment(addr, cmt) return idaapi.IDB_Hooks.cmt_changed(self, *args)
def postprocess(self): try: if "MakeComment" in self.cmdname: if idc.Comment(self.addr) is not None: self.skel_conn.push_comment(self.addr, idc.Comment(self.addr)) if idc.GetFunctionCmt(self.addr, 0) != "": self.skel_conn.push_comment( self.addr, idc.GetFunctionCmt((self.addr), 0)) elif "MakeRptCmt" in self.cmdname: if idc.GetCommentEx(self.addr, 1) != "": self.skel_conn.push_comment( self.addr, idc.GetCommentEx(self.addr, 1)) if idc.GetFunctionCmt(self.addr, 1) != "": self.skel_conn.push_comment( self.addr, idc.GetFunctionCmt(self.addr, 1)) elif self.cmdname == "MakeFunction": if idc.GetFunctionAttr(self.addr, 0) is not None: pass #push_change("idc.MakeFunction", shex(idc.GetFunctionAttr( # self.addr, 0)), shex(idc.GetFunctionAttr(self.addr, 4))) elif self.cmdname == "DeclareStructVar": print "Fixme : declare Struct variable" elif self.cmdname == "SetType": newtype = idc.GetType(self.addr) if newtype is None: newtype = "" else: newtype = SkelUtils.prepare_parse_type( newtype, self.addr) self.skel_conn.push_type(int(self.addr), newtype) # XXX IMPLEMENT elif self.cmdname == "OpStructOffset": print "Fixme, used when typing a struct member/stack var/data pointer to a struct offset " except KeyError: pass return 0
def AppendComment(ea, s, repeatable=False): # see williutils and http://blogs.norman.com/2011/security-research/improving-ida-analysis-of-x64-exception-handling if repeatable: string = idc.RptCmt(ea) else: string = idc.Comment(ea) if not string: string = s # no existing comment else: if s in string: # ignore duplicates return string = string + "\n" + s if repeatable: idc.MakeRptCmt(ea, string) else: idc.MakeComm(ea, string)
def execute_comment(comment): """ Thread safe comment wrapper """ def make_rpt(): idc.MakeRptCmt(comment["address"], comment["data"].encode('ascii', 'replace')) cmt = idc.Comment(comment["address"]) if cmt != comment["data"] and idc.RptCmt( comment["address"]) != comment["data"]: g_logger.debug("[x] Adding comment %s @ 0x%x ", comment["data"], comment["address"]) return idaapi.execute_sync(make_rpt, idaapi.MFF_WRITE) else: pass
def activate(self, ctx): # get item and remove ind = ctx.chooser_selection.at(0) ioctl = self.items[ind - 1] pos = int(ioctl[0], 16) define = ioctl[5] global ioctl_tracker for (addr, val) in ioctl_tracker.ioctls: if addr == pos: code = val break # Get current comment for this instruction and remove the C define from it, if present comment = idc.Comment(pos) comment = comment.replace(define, "") idc.MakeComm(pos, comment) # Remove the ioctl from the valid list and add it to the invalid list to avoid 'find_all_ioctls' accidently re-indexing it. ioctl_tracker.remove_ioctl(pos, code)
def get_comments(self, _start, _end): comments = '' # parse all the instructions for instr in idautils.Heads(_start, _end): if idc.GetFlags(instr) & idc.FF_COMM: # is it a repeatable comment? tmp_comment = idc.Comment(instr) if tmp_comment is not None: # add the current regular comment comments += struct.pack( '>I', len(tmp_comment)) + struct.pack( ">I", instr - _start) + tmp_comment else: # add the current repeatable comment comments += struct.pack('>I', len( idc.RptCmt(instr))) + struct.pack( ">I", instr - _start) + idc.RptCmt(instr) return comments
def get_comments(ea): comments = [] text = idc.RptCmt(ea) if text and len(text) > 0: comments.append({ 'type': 'repeatable', 'comment': text, 'offset': str(hex(ea)).rstrip("L").upper().replace("0X", "0x") }) text = idc.Comment(ea) if text and len(text) > 0: comments.append({ 'type': 'regular', 'comment': text, 'offset': str(hex(ea)).rstrip("L").upper().replace("0X", "0x") }) text = _iter_extra_comments(ea, idaapi.E_PREV) if text and len(text) > 0: comments.append({ 'type': 'anterior', 'comment': text, 'offset': str(hex(ea)).rstrip("L").upper().replace("0X", "0x") }) text = _iter_extra_comments(ea, idaapi.E_NEXT) if text and len(text) > 0: comments.append({ 'type': 'posterior', 'comment': text, 'offset': str(hex(ea)).rstrip("L").upper().replace("0X", "0x") }) return comments
def import_comments(comments): """Import BN comments """ for addr, comment in comments.items(): addr = int(addr) comment = comment.encode("utf-8") current_comment = idc.Comment(addr) # make a new comment if not current_comment: idc.MakeComm(addr, comment) continue # ensure comments hasn't already been imported if comment in current_comment: continue # append to comment idc.MakeComm(addr, current_comment + " " + comment)
def imp_cb(ea, name, ord): if name in funcs: for xref in idautils.XrefsTo(ea): call_addr = xref.frm caller_name = idc.GetFunctionName(call_addr) prev = idc.PrevHead(call_addr) for _ in range(10): if idc.Comment(prev) == 'Tag' and idc.GetOpType(prev, 1) == 5: tag_raw = idc.GetOperandValue(prev, 1) tag = '' for i in range(3, -1, -1): tag += chr((tag_raw >> 8 * i) & 0xFF) if tag in tags.keys(): tags[tag].add(caller_name) else: tags[tag] = set([caller_name]) break prev = idc.PrevHead(prev) return True
def linearize_comment(ea, function_comment=False): regular_comment = "" repeatable_comment = "" if function_comment: regular_comment = idc.GetFunctionCmt(ea, 0) repeatable_comment = idc.GetFunctionCmt(ea, 1) else: regular_comment = idc.Comment(ea) repeatable_comment = idc.RptCmt(ea) if regular_comment is None and repeatable_comment is None: return None elif regular_comment is not None and repeatable_comment is None: return regular_comment elif repeatable_comment is not None and regular_comment is None: return repeatable_comment else: if len(regular_comment) == 0: return repeatable_comment if len(repeatable_comment) == 0: return repeatable_comment return regular_comment + "\n" + repeatable_comment return None
def Assemble(self, function, nasm=True): ''' Main Assemble Function ''' if self.functions.has_key(function.start_ea): ans = idaapi.askyn_c( 1, "HIDECANCEL\nFunction @ [%08x] was already optimized, are you sure you want to overwrite old one?" ) if ans == 0: return idc.Batch(1) self.jmp_table = "" self.jmp_table_refs = [] self.jmp_deps = {} self.ref_instr = {} self.bb_head_ea = {} function_ea = self.free_ea function_end = None if debug: print ">Assemble(%08x) - Starting" % function.start_ea if nasm: if self.opty_dir == None: self.opty_dir = idc.GetIdbPath() self.opty_dir = self.opty_dir[:self.opty_dir.rfind( os.sep)] + os.sep + "optimice_%s" % idc.GetInputFile() if not os.path.isdir(self.opty_dir): os.mkdir(self.opty_dir) self.nasm = True self.nasmfw = open( self.opty_dir + os.sep + 'f_%08x.asm' % function.start_ea, "w+") self.NasmWriteToFile("[BITS 32]\n") self.NasmWriteToFile("\torg %09xh\n" % function_ea) else: self.nasm = False for bb_ea in function.DFSFalseTraverseBlocks(): if self.nasm: self.NasmWriteToFile("\nL%08x:\n" % bb_ea) self.bb_head_ea[bb_ea] = True else: self.bb_head_ea[bb_ea] = self.free_ea for instr in function.GetBBInstructions(bb_ea): if instr == None: continue mnem = self.BuildAsmString(instr, function) if debug: print ">Assemble - Assembling @ %08x [%s]" % ( instr.GetOriginEA(), mnem) if not self.nasm: self.AsmAndWrite(mnem, instr, function=function) if not instr.IsCFI(): ref_from = list(function.GetRefsFrom( instr.GetOriginEA()))[0][0] if self.bb_head_ea.has_key(ref_from): if self.nasm: self.NasmWriteToFile("\tjmp L%08x ; \n" % ref_from) else: self.AsmAndWrite("jmp %09xh" % self.bb_head_ea[ref_from]) elif instr.GetMnem() == "call": for ref, path in function.GetRefsFrom(instr.GetOriginEA()): if path == False: if self.bb_head_ea.has_key(ref): if self.nasm: self.NasmWriteToFile( "\tjmp L%08x ; ###FAKE 2 JMP###\n" % ref) else: self.AsmAndWrite("jmp %09xh" % self.bb_head_ea[ref]) elif instr.IsJcc(): ref_from = list(function.GetRefsFrom(instr.GetOriginEA())) for ref, path in ref_from: if path == False: break if path == True: raise MiscError elif self.bb_head_ea.has_key(ref): print "Check this out @ [%08x] ref:[%08x]" % ( instr.GetOriginEA(), ref) if self.nasm: self.NasmWriteToFile( "\tjmp L%08x ; ###FAKE 2 JMP###\n" % ref) else: self.AsmAndWrite("jmp %09xh" % self.bb_head_ea[ref]) ret = None if self.nasm: self.NasmWriteToFile(self.jmp_table) self.nasmfw.close() self.nasmfw = None ret = self.NasmAssemble(function.start_ea, self.free_ea) if ret != None: self.free_ea = ret else: ret = self.AsmAndWriteBranches(function) self.free_ea = ret self.ref_instr = {} idc.Batch(0) if ret != None: idc.MakeFunction(function_ea) comment = idc.Comment(function_ea) if comment != None: comment = 'Origin@[%08x]; %s' % (function.start_ea, comment) else: comment = 'Origin@[%08x];' % function.start_ea idc.MakeComm(function_ea, comment) comment = idc.Comment(function.start_ea) if comment != None: comment = 'OPTY@[%08x]; %s' % (function_ea, comment) else: comment = 'OPTY@[%08x];' % function_ea idc.MakeComm(function.start_ea, comment) idaapi.set_name(function_ea, "om_%s" % idc.Name(function.start_ea)) function_end = self.free_ea self.functions[function.start_ea] = (function_ea, function_end) idaapi.refresh_idaview_anyway()
def regular(self): """Regular Comment""" return idc.Comment(self._ea)