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. """ # see: http://blogs.norman.com/2011/security-research/improving-ida-analysis-of-x64-exception-handling if repeatable: string = idc.get_cmt(ea, 1) else: string = idc.get_cmt(ea, 0) if not string: string = s # no existing comment else: if s in string: # ignore duplicates return string = string + "\\n" + s if repeatable: idc.set_cmt(ea, string, 1) else: idc.set_cmt(ea, string, 0)
def yacheck_data_comments(self): eas = yaunit.load('data_comments') i = 0 for offset in range(0, 3): for cmt, rpt, post, ant in tests_data: ea = eas[i] logger.debug( "checking data comment at 0x%08X : %r, %r, %r, %r" % (ea, cmt, rpt, post, ant)) i += 1 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 do_unpatch_call(va_callsite): size = idc.get_item_size(va_callsite) ida_xref.del_cref(va_callsite, fva_stub, 0) cmt = idc.get_cmt(va_callsite, 0) newcmt = cmt # Remove automated comments if newcmt.startswith(g_patched_call_cmt): newcmt = newcmt[newcmt.find('\n') + 1:] if newcmt.find('\n') == -1: newcmt = '' else: newcmt = newcmt[newcmt.find('\n') + 1:] if newcmt.startswith(g_cmt_pointed): if newcmt.find('\n') == -1: newcmt = '' else: newcmt = newcmt[newcmt.find('\n') + 1:] if newcmt != cmt: idc.set_cmt(va_callsite, newcmt, 0) if idc.get_operand_type(va_callsite, 0) == ida_ua.o_mem: patch_import(va_callsite, idc.BADADDR) elif idc.get_operand_type(va_callsite, 0) == ida_ua.o_reg: va_imp = self._get_imp_for_register_call(va_callsite) if va_imp: patch_pointer_width(va_imp, idc.BADADDR) else: revert_patch(va_callsite, size)
def set_name(addr, name): # FIXME creates unnamed_178 etc. instead of proper function name in IDA 7.2 on CYW20735 name = name.replace("sub_", "unnamed_") cmt = idc.get_cmt(addr, 0) name_cmt = "fcn.%s" % name if cmt: name_cmt = cmt + ", " + name_cmt idc.set_cmt(addr, name_cmt, 0) if not add_names: return if name.isupper(): try: # hackish way to stop warning about hex import # FIXME leave 'sub_' intact if it already exists # FIXME do not set the name to the hex prologue because it has duplicates, set it to # 'pp_{position}' instead a = int(name, 16) """continue""" except ValueError: pass ok = idc.set_name(addr, name, idc.SN_CHECK) if not ok: data = (name, size, addr) failed_fncs.append((data, "name")) print("{0:#x}: cannot add name, {1}".format(addr, name)) countername_Fail = countername_Fail + 1 else: imported_functions[addr].add(name)
def execute_comment(comment): """ Thread safe comment wrapper """ def make_rpt(): """ Inserting a comment """ ida_bytes.set_cmt(comment["address"], comment["data"].encode('ascii', 'replace'), 1) cmt = idc.get_cmt(comment["address"], 0) if cmt != comment["data"] and idc.get_cmt(comment["address"], 1) != comment["data"]: logger.debug("[x] Adding comment %s @ 0x%x ", comment["data"], comment["address"]) return idaapi.execute_sync(make_rpt, idaapi.MFF_FAST) return None
def activate(self, ctx): pos = idc.get_screen_ea() # Get current comment for this instruction and remove the C define from it, if present comment = idc.get_cmt(pos, 0) code = get_operand_value(pos) define = ioctl_decoder.get_define(code) comment = comment.replace(define, "") idc.set_cmt(pos, comment, 0) # 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 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.get_cmt(pos, 0) if not current_comment: idc.set_cmt(pos, string, 0) elif string not in current_comment: idc.set_cmt(pos, current_comment + " " + string, 0)
def cmt_changed(self, *args): """ A comment changed somewhere """ addr, rpt = args logger.debug("Changed cmt at 0x%x rpt is %d", addr, rpt) cmt = idc.get_cmt(addr, rpt) if not SkelUtils.filter_coms_blacklist(cmt): self.skel_conn.push_comment(addr, cmt) return ida_idp.IDB_Hooks.cmt_changed(self, *args)
def main(): print("[*] loading crypto constants") for const in non_sparse_consts: const["byte_array"] = convert_to_byte_array(const) for start in idautils.Segments(): print("[*] searching for crypto constants in %s" % idc.get_segm_name(start)) ea = start while ea < idc.get_segm_end(start): bbbb = list(struct.unpack("BBBB", idc.get_bytes(ea, 4))) for const in non_sparse_consts: if bbbb != const["byte_array"][:4]: continue if map(lambda x:ord(x), idc.get_bytes(ea, len(const["byte_array"]))) == const["byte_array"]: print(("0x%0" + str(digits) + "X: found const array %s (used in %s)") % (ea, const["name"], const["algorithm"])) idc.set_name(ea, const["name"]) if const["size"] == "B": idc.create_byte(ea) elif const["size"] == "L": idc.create_dword(ea) elif const["size"] == "Q": idc.create_qword(ea) idc.make_array(ea, len(const["array"])) ea += len(const["byte_array"]) - 4 break ea += 4 ea = start if idc.get_segm_attr(ea, idc.SEGATTR_TYPE) == 2: while ea < idc.get_segm_end(start): d = ida_bytes.get_dword(ea) for const in sparse_consts: if d != const["array"][0]: continue tmp = ea + 4 for val in const["array"][1:]: for i in range(8): if ida_bytes.get_dword(tmp + i) == val: tmp = tmp + i + 4 break else: break else: print(("0x%0" + str(digits) + "X: found sparse constants for %s") % (ea, const["algorithm"])) cmt = idc.get_cmt(idc.prev_head(ea), 0) if cmt: idc.set_cmt(idc.prev_head(ea), cmt + ' ' + const["name"], 0) else: idc.set_cmt(idc.prev_head(ea), const["name"], 0) ea = tmp break ea += 1 print("[*] finished")
def execute_comment(comment): """ Thread safe comment wrapper """ def make_rpt(): """ Inserting a comment """ ida_bytes.set_cmt( comment["address"], comment["data"].encode( 'ascii', 'replace'), 1) cmt = idc.get_cmt(comment["address"], 0) if cmt != comment["data"] and idc.get_cmt( comment["address"], 1) != comment["data"]: logger.debug( "[x] Adding comment %s @ 0x%x ", comment["data"], comment["address"]) return idaapi.execute_sync(make_rpt, idaapi.MFF_FAST) return None
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 append_comment(va, new_cmt, repeatable=False): """ Append a comment to an address in IDA Pro. :param va: comment address :param new_cmt: comment string :param repeatable: if True, append as repeatable comment :return: True if success """ cmt = idc.get_cmt(va, repeatable) if not cmt: # no existing comment cmt = new_cmt else: if new_cmt in cmt: # comment already exists return True cmt = cmt + "\n" + new_cmt return idc.set_cmt(va, cmt, repeatable)
def imp_cb(ea, name, ord): if name in funcs: for xref in idautils.XrefsTo(ea): call_addr = xref.frm caller_name = idc.get_func_name(call_addr) prev = idc.prev_head(call_addr) for _ in range(10): if idc.get_cmt(prev, 0) == 'Tag' and idc.get_operand_type( prev, 1) == 5: tag_raw = idc.get_operand_value(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.prev_head(prev) return True
def get_segment_end_ea(ea): """ Return address where next MSDN info can be written to in added segment. Argument: ea -- effective address within added segment where search starts """ addr = ea while idc.get_cmt(addr, 0) is not None: addr = addr + 1 if addr > idc.get_segm_end(ea): g_logger.debug( 'Address {} out of segment bounds. Expanding segment.'.format( hex(addr))) try: expand_segment(ea) except FailedToExpandSegmentException as e: g_logger.warning(e.message) raise e else: return addr
def find_arg_ea(ea_call, arg_name): """ Return ea of argument by looking backwards from library function call. Arguments: ea_call -- effective address of call arg_name -- the argument name to look for """ # the search for previous instruction/data will stop at the specified # address (inclusive) prev_instr = idc.prev_head(ea_call, ea_call - PREVIOUS_INSTR_DELTA) while prev_instr > (ea_call - ARG_SEARCH_THRESHOLD) and \ prev_instr != idaapi.BADADDR: # False indicates not to look for repeatable comments comment = idc.get_cmt(prev_instr, False) if comment == arg_name: return prev_instr prev_instr = idc.prev_head(prev_instr, prev_instr - PREVIOUS_INSTR_DELTA) raise ArgumentNotFoundException( ' Argument {} not found within threshold'.format(arg_name))
def report_strings(self, strs, stack, stack_min_value = None, stack_max_value = None): """ Parses and returns Stack strings as Strings """ for char_width in xrange(1, MAX_CHARACTER_WIDTH + 1): parsed_strs = self.parse_strings(stack, stack_min_value, stack_max_value, char_width) for string, eas, length in parsed_strs: start_ea, end_ea = self.find_start_and_end(eas) for string_obj in strs: if string_obj[2] == string and string_obj[0] == start_ea and string_obj[1] == end_ea: break else: # if we didn't break strs.add((start_ea, end_ea, string)) old_cmt = idc.get_cmt(eas[0], 0) old_cmt = '' if not old_cmt else old_cmt if not is_string_ascii(string): new_cmt = string.encode('hex') new_cmt = old_cmt + '\nStack String (hex): ' + new_cmt else: new_cmt = string.encode('string-escape').replace('\\x00', '\nStack String: ') new_cmt = old_cmt + '\nStack String: ' + new_cmt new_cmt += '\nSize: ' + str(length) new_cmt = '\n'.join(list(set(new_cmt.split('\n')))) # Remove duplicates. idc.set_cmt(eas[0], str(new_cmt).strip('\r\n'), 0)
def report_strings(self, strs, stack, stack_min_value=None, stack_max_value=None): """ Parses and returns Stack strings as Strings """ for char_width in range(1, MAX_CHARACTER_WIDTH + 1): parsed_strs = self.parse_strings(stack, stack_min_value, stack_max_value, char_width) for string, eas, length in parsed_strs: start_ea, end_ea = self.find_start_and_end(eas) for string_obj in strs: if string_obj[2] == string and string_obj[0] == start_ea and string_obj[1] == end_ea: break else: # if we didn't break strs.add((start_ea, end_ea, string)) old_cmt = idc.get_cmt(eas[0], 0) old_cmt = "" if not old_cmt else old_cmt if not is_string_ascii(string): new_cmt = string.hex() new_cmt = old_cmt + "\nStack String (hex): " + new_cmt else: new_cmt = string.decode("unicode-escape").replace("\\x00", "\nStack String: ") new_cmt = old_cmt + "\nStack String: " + new_cmt new_cmt += "\nSize: " + str(length) new_cmt = "\n".join(list(set(new_cmt.split("\n")))) # Remove duplicates. idc.set_cmt(eas[0], str(new_cmt).strip("\r\n"), 0)
def do_patch_call(va): retval = False stub_loc = idc.get_name_ea_simple(self._stubname(nm)) # Preserve original disassembly and format new comment old_target = idc.print_operand(va, 0) orig_cmt = idc.get_cmt(va, 0) or '' new_cmt = '%s\n\t%s' % (g_patched_call_cmt, idc.GetDisasm(va)) if idc.get_operand_type(va, 0) == ida_ua.o_mem: retval = patch_import(va, self._stubname(nm)) new_cmt += '\n%s %s to %s)' % (g_cmt_pointed, old_target, self._stubname(nm)) elif idc.get_operand_type(va, 0) == ida_ua.o_reg: va_imp = self._get_imp_for_register_call(va, nm) if va_imp: patch_pointer_width(va_imp, stub_loc) retval = True else: logger.warn('Could not find import to patch call at %s' % (phex(va))) else: # Usually optype 7 otherwise # Won't work if displacement exceeds 32-bit operand size call_offset_loc = va + idc.get_item_size(va) if abs(call_offset_loc - stub_loc) > 0x100000000: msg = ('Call site at %s too far from %s (%s)' % (phex(va), self._stubname(nm), phex(stub_loc))) raise CodeGraftingDisplacementError(msg) retval = patch_call(va, self._stubname(nm)) if retval: if orig_cmt: new_cmt += '\n%s' % (orig_cmt) idc.set_cmt(va, new_cmt, 0) ida_xref.add_cref(va, stub_loc, ida_xref.fl_CN) return retval
def Comment(ea): return idc.get_cmt(ea, 0)
def RptCmt(ea): return idc.get_cmt(ea, 1)
def cmt_changed(self, ea, repeatable_cmt): cmt = idc.get_cmt(ea, repeatable_cmt) cmt = '' if not cmt else cmt self._send_event(CmtChangedEvent(ea, cmt, repeatable_cmt)) return 0
def main(): print("[*] loading crypto constants") for const in non_sparse_consts: const["byte_array"] = convert_to_byte_array(const) for start in idautils.Segments(): print("[*] searching for crypto constants in %s" % idc.get_segm_name(start)) ea = start while ea < idc.get_segm_end(start): bbbb = list(struct.unpack("BBBB", idc.get_bytes(ea, 4))) for const in non_sparse_consts: if bbbb != const["byte_array"][:4]: continue if map(lambda x: ord(x), idc.get_bytes(ea, len( const["byte_array"]))) == const["byte_array"]: print(("0x%0" + str(digits) + "X: found const array %s (used in %s)") % (ea, const["name"], const["algorithm"])) idc.set_name(ea, const["name"]) if const["size"] == "B": idc.create_byte(ea) elif const["size"] == "L": idc.create_dword(ea) elif const["size"] == "Q": idc.create_qword(ea) idc.make_array(ea, len(const["array"])) ea += len(const["byte_array"]) - 4 break ea += 4 ea = start if idc.get_segm_attr(ea, idc.SEGATTR_TYPE) == 2: while ea < idc.get_segm_end(start): d = ida_bytes.get_dword(ea) for const in sparse_consts: if d != const["array"][0]: continue tmp = ea + 4 for val in const["array"][1:]: for i in range(8): if ida_bytes.get_dword(tmp + i) == val: tmp = tmp + i + 4 break else: break else: print(("0x%0" + str(digits) + "X: found sparse constants for %s") % (ea, const["algorithm"])) cmt = idc.get_cmt(idc.prev_head(ea), 0) if cmt: idc.set_cmt(idc.prev_head(ea), cmt + ' ' + const["name"], 0) else: idc.set_cmt(idc.prev_head(ea), const["name"], 0) ea = tmp break ea += 1 print("[*] finished")
def main(): print("[*] loading crypto constants") for const in non_sparse_consts: const["byte_array"] = convert_to_byte_array(const) for start in idautils.Segments(): print("[*] searching for crypto constants in %s" % idc.get_segm_name(start)) ea = start while ea < idc.get_segm_end(start): bbbb = list(struct.unpack("BBBB", idc.get_bytes(ea, 4))) for const in non_sparse_consts: if bbbb != const["byte_array"][:4]: continue if list( map(lambda x: x if type(x) == int else ord(x), idc.get_bytes(ea, len( const["byte_array"])))) == const["byte_array"]: print(("0x%0" + str(digits) + "X: found const array %s (used in %s)") % (ea, const["name"], const["algorithm"])) idc.set_name(ea, const["name"], ida_name.SN_FORCE) if const["size"] == "B": idc.create_byte(ea) elif const["size"] == "L": idc.create_dword(ea) elif const["size"] == "Q": idc.create_qword(ea) idc.make_array(ea, len(const["array"])) ea += len(const["byte_array"]) - 4 break ea += 4 ea = start if idc.get_segm_attr(ea, idc.SEGATTR_TYPE) == idc.SEG_CODE: while ea < idc.get_segm_end(start): d = ida_bytes.get_dword(ea) for const in sparse_consts: if d != const["array"][0]: continue tmp = ea + 4 for val in const["array"][1:]: for i in range(8): if ida_bytes.get_dword(tmp + i) == val: tmp = tmp + i + 4 break else: break else: print(("0x%0" + str(digits) + "X: found sparse constants for %s") % (ea, const["algorithm"])) cmt = idc.get_cmt(idc.prev_head(ea), 0) if cmt: idc.set_cmt(idc.prev_head(ea), cmt + ' ' + const["name"], 0) else: idc.set_cmt(idc.prev_head(ea), const["name"], 0) ea = tmp break ea += 1 print("[*] searching for crypto constants in immediate operand") funcs = idautils.Functions() for f in funcs: flags = idc.get_func_flags(f) if (not flags & (idc.FUNC_LIB | idc.FUNC_THUNK)): ea = f f_end = idc.get_func_attr(f, idc.FUNCATTR_END) while (ea < f_end): imm_operands = [] insn = ida_ua.insn_t() ida_ua.decode_insn(insn, ea) for i in range(len(insn.ops)): if insn.ops[i].type == ida_ua.o_void: break if insn.ops[i].type == ida_ua.o_imm: imm_operands.append(insn.ops[i].value) if len(imm_operands) == 0: ea = idc.find_code(ea, idc.SEARCH_DOWN) continue for const in operand_consts: if const["value"] in imm_operands: print(("0x%0" + str(digits) + "X: found immediate operand constants for %s") % (ea, const["algorithm"])) cmt = idc.get_cmt(ea, 0) if cmt: idc.set_cmt(ea, cmt + ' ' + const["name"], 0) else: idc.set_cmt(ea, const["name"], 0) break ea = idc.find_code(ea, idc.SEARCH_DOWN) print("[*] finished")
def check_fmt_function(name, addr): """ Check if the format string argument is not valid """ function_head = idc.get_func_attr(addr, idc.FUNCATTR_START) while True: addr = idc.prev_head(addr) op = idc.print_insn_mnem(addr).lower() dst = idc.print_operand(addr, 0) if op in ("ret", "retn", "jmp", "b") or addr < function_head: return c = idc.get_cmt(addr, 0) if c and c.lower() == "format": break elif name.endswith(("snprintf_chk", )): if op in ("mov", "lea") and dst.endswith( ("r8", "r8d", "[esp+10h]")): break elif name.endswith(("sprintf_chk", )): if op in ("mov", "lea") and (dst.endswith( ("rcx", "[esp+0Ch]", "R3")) or dst.endswith("ecx") and BITS == 64): break elif name.endswith(("snprintf", "fnprintf")): if op in ("mov", "lea") and (dst.endswith( ("rdx", "[esp+8]", "R2")) or dst.endswith("edx") and BITS == 64): break elif name.endswith( ("sprintf", "fprintf", "dprintf", "printf_chk")): if op in ("mov", "lea") and (dst.endswith( ("rsi", "[esp+4]", "R1")) or dst.endswith("esi") and BITS == 64): break elif name.endswith("printf"): if op in ("mov", "lea") and (dst.endswith( ("rdi", "[esp]", "R0")) or dst.endswith("edi") and BITS == 64): break # format arg found, check its type and value # get last oprend op_index = idc.generate_disasm_line(addr, 0).count(",") op_type = idc.get_operand_type(addr, op_index) opnd = idc.print_operand(addr, op_index) if op_type == idc.o_reg: # format is in register, try to track back and get the source _addr = addr while True: _addr = idc.prev_head(_addr) _op = idc.print_insn_mnem(_addr).lower() if _op in ("ret", "retn", "jmp", "b") or _addr < function_head: break elif _op in ("mov", "lea", "ldr") and idc.print_operand( _addr, 0) == opnd: op_type = idc.get_operand_type(_addr, 1) opnd = idc.print_operand(_addr, 1) addr = _addr break if op_type == idc.o_imm or op_type == idc.o_mem: # format is a memory address, check if it's in writable segment op_addr = idc.get_operand_value(addr, op_index) seg = idaapi.getseg(op_addr) if seg: if not seg.perm & idaapi.SEGPERM_WRITE: # format is in read-only segment return print("0x%X: Possible Vulnerability: %s, format = %s" % (addr, name, opnd)) return ["0x%X" % addr, name, opnd]
def _findGraftedSegments(self): return [ s for s in SegPlanner() if idc.get_cmt(s.start, 1) == g_seg_sig_code_grafter ]
def getComment(self): """ :return: non-repeatable comment, prioritizes GUI-added comments over API added ones """ return idc.get_cmt(self.ea, 0) or ''
def cmd_get_comment(self, addr): return idc.get_cmt(int(addr, 0), 0)
def get_comment(self, ea): idc.get_cmt(ea, False)
print("objc2_analyzer_test: TESTING COMMENTS") msgSendXrefs = list( idautils.XrefsTo(idc.get_name_ea_simple("_objc_msgSend"))) # IDA gets the xrefs in places we are not interested # we rely on objc2_analyzer's new xrefs to help our testing if arch == "ARM": i = 0 while i < len(msgSendXrefs): if idc.print_insn_mnem(msgSendXrefs[i].frm) != "BLX": del (msgSendXrefs[i]) continue i += 1 for i, x in enumerate(msgSendXrefs): cmt = idc.get_cmt(x.frm, False) if arch == "ARM": if cmt != ARMComments[i]: print("objc2_analyzer_test FAILED: incorrect comment @ %016X" % x.frm) else: if cmt != comments[i]: print("objc2_analyzer_test FAILED: incorrect comment @ %016X" % x.frm) impAddrs = [] print("objc2_analyzer_test: TESTING XREFS TO IMPS") for imp in imps: addr = idc.get_name_ea_simple(imp) impAddrs.append(addr) if arch == "ARM":
def getComment(self, repeatable=False): """ :return: data item comment, prioritizes GUI-added comments over API added ones """ mode = 1 if repeatable else 0 return idc.get_cmt(self.ea, mode) or ''
def cmd_get_comment(self, addr): c = idc.get_cmt(int(addr, 0), 0) if c: return c return ""