def getComment(self): # type: () -> str """ Sometimes the comment is repeatable (created through decomp) or not (created through disass). Returning disass comment """ cmt = idc.get_func_cmt(self.func_ea, 1) if not cmt: cmt = idc.get_func_cmt(self.func_ea, 0) return cmt
def yacheck_comments(self): eas = yaunit.load('comments') i = 0 for offset in range(0, 3): for fn_cmt, fn_rpt, cmt, rpt, post, ant in tests: ea = eas[i] logger.debug("checking at 0x%08X : %r, %r, %r, %r, %r, %r" % (ea, fn_cmt, fn_rpt, cmt, rpt, post, ant)) i += 1 self.assertEqual(idc.get_func_cmt(ea, False), fn_cmt) self.assertEqual(idc.get_func_cmt(ea, True), fn_rpt) self.assertEqual(idc.get_cmt(ea, False), cmt) self.assertEqual(idc.get_cmt(ea, True), rpt) self.assertEqual(self.get_extra(ea, idc.E_NEXT), post) self.assertEqual(self.get_extra(ea, idc.E_PREV), ant)
def update(self): global OPEN_TAG global CLOSE_TAG global TAG_SEPARATOR self.clear() for function_address in idautils.Functions(): function_comment = idc.get_func_cmt(function_address, False) tag_list_start = function_comment.find(OPEN_TAG) if tag_list_start == -1: continue tag_list_end = function_comment.find(CLOSE_TAG, tag_list_start) if tag_list_end == -1: continue tag_list = function_comment[tag_list_start + len(OPEN_TAG): tag_list_end] if len(tag_list) == 0: continue current_function_name = idc.get_func_name(function_address) self._function_list[current_function_name] = tag_list.split( TAG_SEPARATOR) tag_list = tag_list.split(TAG_SEPARATOR) for tag_name in tag_list: if tag_name not in self._tag_list: self._tag_list[tag_name] = [] self._tag_list[tag_name].append(current_function_name)
def range_cmt_changed(self, kind, a, cmt, repeatable): print("range cmt changed") # verify it's a function comment cmt = idc.get_func_cmt(a.start_ea, repeatable) if cmt: self.ida_comment_changed(cmt, a.start_ea, "range") return 0
def getComment(self, repeatable=False): # type: () -> str """ returns the comment associated with the function. This could be its repeatable comment to show in all disassembly, or just its docs """ mode = 1 if repeatable else 0 cmt = idc.get_func_cmt(self.func_ea, mode) return cmt
def get_func_item(self, offset): while True: ea = get_func_item(offset) skip = False def getlen(x): return len(x) if x else 0 for x in [False, True]: skip |= getlen(idc.get_func_cmt(ea, x)) skip |= getlen(idc.get_cmt(ea, x)) for x in [idc.E_PREV, idc.E_NEXT]: skip |= getlen(self.get_extra(ea, x)) if not skip: return ea
def rngInc(start_ea, end_ea): """ Reports back the exposed (or public) symbols of the range The symbols are .global forwarded, and represent the symbols defined within the range :param start_ea: linear address of the start of the range :param end_ea: linear address of the end of the range, exclusive :return: a series of .equ's representing the public (and private) interface of the range """ ea = start_ea pubrefs = [] # fwdrefs = [] while ea < end_ea: d = Data.Data(ea) if d.getName(): # check xrefs to the item, if any is outside the range, it's a public reference isPublic = False xrefsTo = d.getXRefsTo() for cref in xrefsTo[0]: if cref < start_ea or cref >= end_ea: isPublic = True for dref in xrefsTo[1]: if dref < start_ea or dref >= end_ea: isPublic = True if isPublic: pubrefs.append((d.getName(), d.ea)) # For debugging purposes # else: # fwdrefs.append((d.getName(), d.ea)) ea = ea + d.getSize() # string build includes inc = '/* Public Symbols */\n' for name, ea in pubrefs: d = Data.Data(ea) if d.isFunctionStart(): cmt = idc.get_func_cmt(ea, repeatable=1) if cmt: cmt = ' // ' + cmt.replace('\n', '\n// ') else: cmt = '' inc += '.global %s%s\n' % (name, cmt) # For debugging purposes, defining forward references could be useful in include files # inc += "\n// Forward Reference\n" # for name, ea in fwdrefs: # inc += ".equ %s, 0x%07X\n" % (name, ea) inc += "\n" return inc
def removeAllTags(self): global OPEN_TAG global CLOSE_TAG for function_address in idautils.Functions(): function_comment = idc.get_func_cmt(function_address, False) tag_list_start = function_comment.find(OPEN_TAG) if tag_list_start == -1: continue tag_list_end = function_comment.find(CLOSE_TAG, tag_list_start) if tag_list_end == -1: continue set_func_cmt(function_address, function_comment.replace( function_comment[tag_list_start: tag_list_end + 1], ""), 0)
def _addTagToFunction(self, function_name, tag_name): global OPEN_TAG global CLOSE_TAG global TAG_SEPARATOR function_address = idc.get_name_ea_simple(function_name) function_comment = idc.get_func_cmt(function_address, False) tag_list_start = function_comment.find(OPEN_TAG) if tag_list_start == -1: idc.set_func_cmt( function_address, function_comment + OPEN_TAG + tag_name + CLOSE_TAG, False) return tag_list_end = function_comment.find(CLOSE_TAG, tag_list_start) if tag_list_end == -1: print( "IDA Function Tagger: Malformed tag list found at address 0x%X" % function_address) return tag_list = function_comment[tag_list_start:tag_list_end + 1] function_comment = function_comment.replace(tag_list, "") tag_list = tag_list[len(OPEN_TAG):len(tag_list) - len(CLOSE_TAG)] tag_list = tag_list.split(TAG_SEPARATOR) if tag_name not in tag_list: tag_list.append(tag_name) tag_list.sort() function_comment = function_comment + OPEN_TAG for tag in tag_list: function_comment = function_comment + tag + TAG_SEPARATOR function_comment = function_comment[:-1] + CLOSE_TAG idc.set_func_cmt(function_address, function_comment, False)
def rngSyncedExterns(self, start_ea, end_ea): """ The same as rngExterns(), except it includes header files if they exist. This is based on all header files for asm files in self.gameFiles. when a header file is included, used symbols from the header file are shown commented out after it :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 and .includes """ xrefs = self.rngExterns(start_ea, end_ea, toStr=False) includes = {} # compute includes, and find the ones not declared anywhere undeclaredXrefs = [] dataFileLabels = [] # those are declared in _rom.s and must not be .equ'd. for xref in xrefs: # figure out if it's in any include (within asmFile ranges) isDeclared = False for file in sorted(self.gameFiles.keys(), key=self.gameFiles.__getitem__): if file.endswith('.s'): # if xref is within file range if self.gameFiles[file][0] <= xref < self.gameFiles[file][1]: if file not in includes: includes[file] = (self.gameFiles[file][0], [xref]) else: includes[file][1].append(xref) # we found what file that xref belongs to now isDeclared = True break else: dataFileLabels.append(self.gameFiles[file][0]) # xref doesn't belong to any header file if not isDeclared and xref not in undeclaredXrefs: undeclaredXrefs.append(xref) # output includes and specific usages output = '/* External Symbols */\n' for include, _ in sorted(includes.items(), key=lambda x:x[1][0]): output += '.include "%s.inc"\n' % (include[:include.rindex('.')]) for xref in includes[include][1]: # Only global if all symbols are defined somewhere. d = Data.Data(xref) if d.isFunctionStart(): cmt = idc.get_func_cmt(xref, repeatable=1) if cmt: cmt = ' // ' + cmt.replace('\n', '\n// ') else: cmt = '' # TODO: while debugging/actively disassembling .set is more convenient output += '// .global %s%s\n' % (d.getName(), cmt) # output += '.set %s, 0x%07X\n' % (Data.Data(xref).getName(), Data.Data(xref).ea) output += '\n' # output remaining xrefs if undeclaredXrefs: output += '\n/* Undeclared Symbols */\n' for xref in undeclaredXrefs: # make sure the undeclared xref is not declared in _rom.s (data files) d = Data.Data(xref) name = d.getName() xref = d.ea if xref in dataFileLabels: output += '// .global %s\n' % (name) # IWRAM/EWRAM are linked as their own objects elif xref >= 0x2000000 and xref < 0x3008000: output += '// .equ %s, 0x%07X\n' % (name, xref) else: output += '.equ %s, 0x%07X\n' % (name, xref) return output
def cmd_get_function_comment(self, a): return idc.get_func_cmt(int(a, 0), 0)
def rcomment(self): """ Property which allow access to the repeatable comment. """ return idc.get_func_cmt(self.ea, True)
def comment(self): """ Property which allow access to the comment. """ return idc.get_func_cmt(self.ea, False)
def getFormattedDisasm(self, start_ea, end_ea): """ puts together the name label, comment, and disassembly, as well as proper spacing :params (start_ea, end_ea): file range to determine if global or not :return: """ name = self.getName() disasm = '' # include label if name: if self.isGlobal(start_ea, end_ea): disasm = name + '::' else: disasm = name + ':' # only add a new line for code labels if self.isCode(): disasm += "\n" # include comment comment = '' inlineComment = '' # include the data item's comment comment = self.getComment() if comment: # TODO: parsing the force out of the comment, but replacing with original if '<force>' in comment: origDisasm = idc.GetDisasm(self.ea) if ';' in origDisasm: origDisasm = origDisasm[:origDisasm.index(';')] comment = comment[:comment.index( '<force>')] + '<il><force> ' + origDisasm if '<il>' in comment: splitIdx = comment.index('<il>') inlineComment = comment[splitIdx + len('<il>'):] comment = comment[:splitIdx] if comment: comment = "// " + comment.replace('\n', '\n\t// ') + '\n' if self.isCode(): disasm = disasm + '\t' + comment # label comes before comment else: # for data, comment comes before label if name: disasm = disasm + '\n' disasm = disasm + '\t' + comment # label comes before comment # include disassembly if self.isCode() or not name: disasm += '\t' + self.getDisasm() else: if comment: disasm += '\t' + self.getDisasm() else: disasm += ' ' + self.getDisasm() # include repeatable comments for function calls in the same line if self.isCode() and 'BL ' in self.getOrigDisasm(): repCmt = idc.get_func_cmt(self.getXRefsFrom()[0][0], repeatable=1) repCmt = ' // ' + repCmt if repCmt else '' disasm += repCmt if inlineComment: disasm += ' // ' + inlineComment # TODO: tabs or spaces? # disasm = self._convertTabs(disasm) return disasm