def handle_class(self, ea): clazz_ea = ida_bytes.get_qword(ea) clazz = Objc2Class(ida_bytes.get_bytes(clazz_ea, Objc2Class.length)) # if clazz.info & 7 != 0: # swift meta_class = Objc2Class( ida_bytes.get_bytes(clazz.isa, Objc2Class.length)) meta_class.info = (meta_class.info >> 3) << 3 meta_info = Objc2ClassRo( ida_bytes.get_bytes(meta_class.info, Objc2ClassRo.length)) clazz.info = (clazz.info >> 3) << 3 clazz_info = Objc2ClassRo( ida_bytes.get_bytes(clazz.info, Objc2ClassRo.length)) c = Clazz(cstr(clazz_info.name), ea=clazz_ea) self.print('@interface', cstr(clazz_info.name)) for method in method_list(meta_info.base_meths): key = '+ ' + cstr(method.name) c.methods[key] = method.imp self.print(key) for method in method_list(clazz_info.base_meths): key = '- ' + cstr(method.name) c.methods[key] = method.imp self.print(key) self.print('@end') self.print() self.classes.append(c) self.class_lookup[c.name] = c self.lookup[ea] = c
def get_decode_xrefs(): """ Find all cross-refernces to 0x132549a in our REvil malware sample. Decode the string and annotate the IDA database, this will make analysis a lot easier """ for xref in idautils.XrefsTo(0x132549a): # first of all, we need to find the arguments to this function, the signature is: # BYTE* __cdecl decode_string(char* base, int keyOffset, int keyLen, int dataLen, BYTE* pOut); args = get_decode_args(xref.frm) if args: base, key_offset, key_len, data_len = args # get the data from the image, data = [keyBytes][encryptedData] data = ida_bytes.get_bytes(base + key_offset, key_len + data_len) str = data_to_str(decode_string(data, key_len, data_len)) print("0x%08x: %s" % (xref.frm, str)) # put a comment in the code cfunc = idaapi.decompile(xref.frm) if cfunc is not None: tl = idaapi.treeloc_t() tl.ea = xref.frm tl.itp = idaapi.ITP_SEMI cfunc.set_user_cmt(tl, str) cfunc.save_user_cmts() idaapi.set_cmt(int(xref.frm), str, True) else: # We could not get the arguments, likely because it may be a register and not an immediate # value, so we'd need to go back further and find what value that register was assigned with. # Would be easier to just tell user, and let him decode it manually (HexRays has the actual args) print("0x%08x: Could not decode arguments" % xref.frm)
def method_list(ea): if not ea: return count = ida_bytes.get_dword(ea + 4) name = idc.get_segm_name(ea) first = ea + 8 def post14format(addr): for _ in range(3): data = ida_bytes.get_bytes(addr, 4) offset, = struct.unpack('<i', data) yield addr + offset addr += 4 is14 = name and (name.endswith(':__objc_const_ax') or name.endswith(':__objc_methlist')) for i in range(count): if is14: # iOS 14 yield Post14Method(*post14format(first + i * 12)) else: ea_method_t = first + i * Objc2Method.length data = ida_bytes.get_bytes(ea_method_t, Objc2Method.length) yield Objc2Method(data)
def getBinary(self): result = b"" segment = ida_segment.get_first_seg() while segment: result += ida_bytes.get_bytes(segment.start_ea, segment.end_ea - segment.start_ea) segment = ida_segment.get_next_seg(segment.end_ea) return result
def read(self, size): ea = ida_loader.get_fileregion_ea(self.offset) if ea == idc.BADADDR: # best guess, such as if file is mapped at address 0x0. ea = self.offset logger.debug("reading 0x%x bytes at 0x%x (ea: 0x%x)", size, self.offset, ea) return ida_bytes.get_bytes(ea, size)
def main(): is_selected, sel_start, sel_end = ida_kernwin.read_selection() if not is_selected: logger.error('range must be selected') return -1 sel_end = ida_bytes.next_head(sel_end) buf = ida_bytes.get_bytes(sel_start, sel_end - sel_start) if buf is None: logger.error('failed to fetch instruction bytes') return -1 f = ida_funcs.get_func(sel_start) if f != ida_funcs.get_func(sel_end): logger.error('range must be within a single function') return -1 # find mappings from "$localN" to "custom_name" regvars = {} for i in range(0x1000): regvar = ida_frame.find_regvar(f, sel_start, '$local%d' % i) if regvar is None: continue regvars[regvar.canon] = regvar.user if len(regvars) >= f.regvarqty: break globals_ = {} for i, offset in netnode.Netnode('$ wasm.offsets').get('globals', {}).items(): globals_['$global' + i] = ida_name.get_name(offset) frame = {} if f.frame != ida_idaapi.BADADDR: names = set([]) for i in range(ida_struct.get_struc_size(f.frame)): s = ida_struct.get_struc(f.frame) if not s: continue m = ida_struct.get_member(s, i) if not m: continue name = ida_struct.get_member_name(m.id) if name in names: continue frame[i] = name names.add(name) emu = Emulator(buf) emu.run() print( emu.render(ctx={ 'regvars': regvars, 'frame': frame, 'globals': globals_, }))
def restore_x(unique_name=None, start=None): ea = ida_kernwin.get_screen_ea() # signature if not unique_name: if not start: seg = ida_segment.getseg(ea) start = seg.start_ea sig_bytes = ida_bytes.get_bytes(start, SIGNATURE_SIZE) sig_hash = hashlib.md5(sig_bytes).hexdigest() unique_name = sig_hash if not start: seg = ida_segment.getseg(ea) start = seg.start_ea if MD5_hash_data_file and os.path.isfile(MD5_hash_data_file): with open(MD5_hash_data_file, "rb") as ifile: received_data = pickle.loads(ifile.read()) saved_data = received_data print("dumpDyn::restore\n\ Name: {}\n\ Restore address: {}\n".format(unique_name, hex(start))) # (start_addr, end_addr, names, comms, bpts, funcs) if unique_name in saved_data: current_data = saved_data[unique_name] # restore names names = current_data[2] for name in names: # names: (rel_addr, name, is_code) ida_name.set_name(start + name[0], name[1]) flags = ida_bytes.get_flags(start + name[0]) if name[2] and not ida_bytes.is_code(flags): ida_auto.auto_make_code(start + name[0]) # restore comments # comms: (rel_addr, TYPE, comment) comms = current_data[3] for comm in comms: # 0:MakeComm and 1:MakeRptCmt ida_bytes.set_cmt(start + comm[0], comm[2], comm[1]) # restore breakpoints # bpts: (rel_addr, size, type) bpts = current_data[4] for bpt in bpts: ida_dbg.add_bpt(start + bpt[0], bpt[1], bpt[2]) # restore functions funcs_addr = current_data[5] for addr in funcs_addr: ida_auto.auto_make_proc(start + addr) # make code & func
def method_list(ea): if not ea: return count = ida_bytes.get_dword(ea + 4) first = ea + 8 for i in range(count): ea_method_t = first + i * Objc2Method.length data = ida_bytes.get_bytes(ea_method_t, Objc2Method.length) yield Objc2Method(data)
def _get_memory(self): result = bytearray() segment_starts = [ea for ea in idautils.Segments()] offsets = [] start_len = 0 for start in segment_starts: end = idc.get_segm_attr(start, idc.SEGATTR_END) result += ida_bytes.get_bytes(start, end - start) offsets.append((start, start_len, len(result))) start_len = len(result) return bytes(result), offsets
def _get_memory(self): result = b'' segment_starts = [ea for ea in idautils.Segments()] offsets = [] start_len = 0 for start in segment_starts: end = idc.get_segm_end(start) result += ida_bytes.get_bytes(start, end - start) offsets.append((start, start_len, len(result))) start_len = len(result) return result, offsets
def get_hex(self, start_address, end_address) -> list: result = [] start = int(start_address, 16) end = int(end_address, 16) while start <= end: # https://github.com/idapython/src/blob/master/python/idautils.py#L202 next_start = ida_bytes.next_head(start, ida_ida.cvar.inf.max_ea) result.append( binascii.hexlify(ida_bytes.get_bytes(start, next_start - start)).decode()) start = next_start return result
def main(): for segstart, segend, segname in enum_segments(): for head in idautils.Heads(segstart, segend): if not is_code(head): continue # pattern: # # lea rax, unk_6BDF88 # mov [rsp+0], rax # mov qword ptr [rsp+8], 40h if ida_ua.ua_mnem(head) != "lea": continue next_head = ida_bytes.next_head(head, idc.BADADDR) if ida_ua.ua_mnem(next_head) != "mov": continue next_head2 = ida_bytes.next_head(next_head, idc.BADADDR) if ida_ua.ua_mnem(next_head2) != "mov": continue dst = idc.get_operand_value(head, 1) if idc.get_segm_name(dst) not in (".rdata", "UPX1"): continue size = idc.get_operand_value(next_head2, 1) if size > 0x100: continue if size <= 2: continue buf = ida_bytes.get_bytes(dst, size) if not buf: continue if b"\x00" in buf: continue try: s = buf.decode("ascii") except UnicodeDecodeError: continue print("string pointer: 0x%x -> 0x%x: %s" % (head, dst, s)) ida_bytes.del_items(dst, 1) ida_bytes.create_data(dst, idc.FF_BYTE, 1, idc.BADADDR) ida_bytes.set_cmt(dst, s, True) ida_name.set_name(dst, "s_%x" % (dst))
def getOpcodes(addr, size): md = capstone.Cs(capstone.CS_ARCH_X86, CAPSTONE_MODE) md.detail = True instr_bytes = ida_bytes.get_bytes(addr, size) opcodes_buf = b'' for i in md.disasm(instr_bytes, size): # get last opcode if (i.opcode[3] != 0): opcodes_buf += "%02x" % (i.opcode[3]) elif (i.opcode[2] != 0): opcodes_buf += "%02x" % (i.opcode[2]) elif (i.opcode[1] != 0): opcodes_buf += "%02x" % (i.opcode[1]) else: opcodes_buf += "%02x" % (i.opcode[0]) return opcodes_buf
def decode_at_ea(addr, offset, keyLen, dataLen): """ use for manually decoding with IDA and will put a comment at screen_ea """ data = ida_bytes.get_bytes(addr + offset, keyLen + dataLen) cmt = data_to_str(decode_string(data, keyLen, dataLen)) print("%s" % cmt) ea = idc.get_screen_ea() idaapi.set_cmt(ea, cmt, True) # disassembly comment cfunc = idaapi.decompile(ea) if cfunc is not None: # decompiled comment tl = idaapi.treeloc_t() tl.ea = ea tl.itp = idaapi.ITP_SEMI cfunc.set_user_cmt(tl, cmt) cfunc.save_user_cmts()
def main(): for segstart, segend, segname in enum_segments(): if segname not in ('.rdata', 'UPX1' ): continue for src, dst, psize in find_pointers(segstart, segend): if idc.get_segm_name(dst) not in (".rdata", "UPX0"): continue if psize == 8: size = ida_bytes.get_qword(src + 0x8) else: size = ida_bytes.get_dword(src + 0x4) if size > 0x100: continue if size <= 2: continue buf = ida_bytes.get_bytes(dst, size) if not buf: continue if b"\x00" in buf: continue try: s = buf.decode("ascii") except UnicodeDecodeError: continue print("string pointer: 0x%x -> 0x%x: %s" % (src, dst, s)) ida_bytes.del_items(src, 1) ida_bytes.set_cmt(dst, s, True) # pointer ida_bytes.del_items(src, psize) ida_bytes.create_data(src, idc.FF_QWORD if size == 8 else idc.FF_DWORD, psize, idc.BADADDR) # this doesn't seem to always work :-( idc.op_plain_offset(src, -1, 0) ida_name.set_name(src, "s_%x" % (src)) ida_bytes.set_cmt(src, s, True) # size ida_bytes.del_items(src + psize, psize) ida_bytes.create_data(src + psize, idc.FF_QWORD if size == 8 else idc.FF_DWORD, psize, idc.BADADDR)
def add_method_xref(self, xref): msg("Adding cross reference to method implementation for %s\n" % get_func_name(self.method_pointer)) add_dref(xref.frm, self.method_pointer, dr_I | XREF_USER) offset = self.method_pointer - xref.frm instruction_bytes = get_bytes(xref.frm, self.ARM64_INSTRUCTION_SIZE) #TODO: are there other instructions that could reference a method selector #and then move the selector reference into a register? arm64_ldr = AArch64LDRInstruction(instruction_bytes) arm64_ldr.patch_offset(offset) patch_dword(xref.frm, arm64_ldr.instruction_int) return ObjcMethodXref(xref.frm, self.method_pointer, xref.to)
def dump_binary(path): sections = [] current_offset = 0 with open(path, 'wb+') as f: # over all segments for n in range(ida_segment.get_segm_qty()): seg = ida_segment.getnseg(n) start_ea = seg.start_ea end_ea = seg.end_ea size = end_ea - start_ea dump_log.debug("Dumping 0x%x bytes from 0x%x", size, start_ea) f.write(ida_bytes.get_bytes(start_ea, size)) sections.append((ida_segment.get_segm_name(seg), start_ea, size, current_offset, size)) current_offset += size dump_log.debug(repr(sections)) return sections
def find_function_frame_references( self, function: Function, frame_pointer: int) -> dict[int, list[FrameReference]]: """ scan the given instruction for LOAD or STOREs to the function frame. args: function (dict[str, any]): function instance. frame_pointer (int): local variable index of the frame pointer. returns: dict[int, list[FrameReference]]: mapping from frame_offset to set of frame references """ buf = ida_bytes.get_bytes(function['offset'], function['size']) bc = list(wasm.decode.decode_bytecode(buf)) offset = function['offset'] SLICE_SIZE = 3 references: defaultdict[int, list[FrameReference]] = defaultdict(list) for i in range(len(bc) - SLICE_SIZE - 1): insns = bc[i:i + SLICE_SIZE] try: load = self.get_frame_load(function, frame_pointer, insns) except ValueError: pass else: load['offset'] += offset logger.debug('found function frame load at 0x%X', load['offset']) references[load['frame_offset']].append(load) try: store = self.get_frame_store(function, frame_pointer, insns) except ValueError: pass else: store['offset'] += offset logger.debug('found function frame store at 0x%X', store['offset']) references[store['frame_offset']].append(store) offset += bc[i].len return dict(references)
def main(argv=None): if argv is None: argv = sys.argv[:] try: seg_spec = prompt_for_segment() except BadInputError: logger.error('bad input, exiting...') return -1 seg = ida_segment.get_segm_by_name(seg_spec.name) if not seg: logger.error("bad segment, exiting...") buf = ida_bytes.get_bytes(seg.start_ea, seg.end_ea - seg.start_ea) with open(seg_spec.path, "wb") as f: f.write(buf) logger.info("wrote %x bytes", len(buf))
def xex_load_exports(li): global export_table_va export_table = HvImageExportTable() slen = ctypes.sizeof(export_table) bytes = ida_bytes.get_bytes(export_table_va, slen) fit = min(len(bytes), slen) ctypes.memmove(ctypes.addressof(export_table), bytes, fit) if export_table.Magic[0] != XEX_EXPORT_MAGIC_0 or export_table.Magic[ 1] != XEX_EXPORT_MAGIC_1 or export_table.Magic[ 2] != XEX_EXPORT_MAGIC_2: print("[+] Export table magic is invalid! (0x%X 0x%X 0x%X)" % (export_table.Magic[0], export_table.Magic[1], export_table.Magic[2])) return 0 print("[+] Loading module exports...") print(export_table) ordinal_addrs_va = export_table_va + slen for i in range(0, export_table.Count): func_ord = export_table.Base + i func_va = ida_bytes.get_dword(ordinal_addrs_va + (i * 4)) if func_va == 0: continue func_va = func_va + (export_table.ImageBaseAddress << 16) func_name = x360_imports.DoNameGen(idc.get_root_filename(), 0, func_ord) # Add to exports list & mark as func if inside a code section func_segmclass = ida_segment.get_segm_class( ida_segment.getseg(func_va)) idc.add_entry(func_ord, func_va, func_name, 1 if func_segmclass == "CODE" else 0) if func_segmclass == "CODE": idc.add_func(func_va) return 1
def emulate(startea, endea): global reg_overrides global stack_bottom global uc # Align on a 1024-byte boundry aligned_startea = startea & ~0x3ff aligned_endea = (endea + 0x400) & ~0x3ff len = aligned_endea - aligned_startea # print("startea {} endea {}".format(hex(startea), hex(endea))) # print("start {} end {} len {}".format(hex(aligned_startea), \ # hex(aligned_endea), hex(len))) uc.reg_write(UC_ARM64_REG_SP, stack_bottom + 0x400) uc.mem_write(aligned_startea, ida_bytes.get_bytes(aligned_startea, len)) uc.hook_add(UC_HOOK_CODE, hook_code, begin=startea, end=endea) uc.hook_add(UC_HOOK_INSN_INVALID, hook_invalid_insn) # uc.hook_add(UC_HOOK_INSN, hook_invalid_insn) uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED, hook_invalid) # try: uc.emu_start(startea, endea) # except UcError as e: # print("ERROR: %s" % e) # print("Done") x1 = uc.reg_read(UC_ARM64_REG_X1) x2 = uc.reg_read(UC_ARM64_REG_X2) w3 = uc.reg_read(UC_ARM64_REG_W3) if x1 == 0: x1 = reg_overrides[0] if x2 == 0: x2 = reg_overrides[1] if w3 == 0: w3 = reg_overrides[2] return [x1, x2, w3]
def get_bytes(address: int, size: int, default: int = 0) -> bytes: """ Obtains bytes from given address. Replaces non-loaded data with 0 byte. :param address: Address to pull bytes from. :param size: Number of bytes to pull. :param default: Default byte to provide if not loaded. :returns: obtained bytes """ if default == 0: data = bytearray(size) else: data = bytearray([default] * size) start = address for address, size in get_byte_chunks(address, size): offset = address - start data[offset:offset + size] = ida_bytes.get_bytes(address, size) return bytes(data)
def hint(self, ea, tag, val): val = val.strip() if not val: return None if tag == ida_lines.SCOLOR_REG: if val in self.regs: register = self.regs[val] return register["long_name"], register["purpose"] elif tag == ida_lines.SCOLOR_INSN: insn = ida_ua.insn_t() ida_ua.decode_insn(insn, ea) insn_bs = ida_bytes.get_bytes(insn.ea, insn.size) fmt = {2: "<H", 4: "<I", 8: "<Q"} insn_bs = struct.unpack(fmt.get(len(insn_bs)), insn_bs)[0] enc_type = "A64" if isinstance(self, AArch32): enc_type = "A32" if ida_segregs.get_sreg(ea, 20) > 0: enc_type = "T32" insn_enc = self.find_insn_enc( enc_type, insn_bs, insn.get_canon_mnem() ) if not insn_enc: return insn_name, tmpl_name = insn_enc insn = self.insns[insn_name] desc = insn["authored"] if tmpl_name in insn["templates"]: desc += "\n\n" + "\n".join(insn["templates"][tmpl_name]) return insn["heading"], desc elif tag == ida_lines.SCOLOR_KEYWORD: if val in self.data["keywords"]: return val, self.data["keywords"][val]
def data(self) -> bytes: """Returns all the bytes contained in the function.""" return ida_bytes.get_bytes(self.start_ea, self.end_ea - self.start_ea)
def __init__(self, base, rva, size): self.__base = base super().__init__(PE_Directory_Debug_CodeView.packinfo, ida_bytes.get_bytes(self.__base + rva, size))
def get_prologue_bc(self, offset: int) -> list[Instruction]: prologue = ida_bytes.get_bytes(offset, self.PROLOGUE_SIZE) try: return list(itertools.islice(wasm.decode_bytecode(prologue), 8)) except: # NOQA: E722 do not use bare 'except' return []
def decode_instruction(self, insn): buf = get_bytes(insn.ea, 2) hw1 = unpack("<H", buf)[0] op = (hw1 & 0x7E0) >> 5 # take bit5->bit10 # Format I if op == 2 and (hw1 >> 11) == 0 and (hw1 & 0x1F) != 0: # TODO add vector4 parsing insn.itype = NewInstructions.NN_fetrap insn.size = 2 return True # Format XIV elif op == 0x3D and ((hw1 & 0xFFE0) >> 5) == 0x3D: buf = get_bytes(insn.ea + 2, 2) hw2 = unpack("<H", buf)[0] subop = hw2 & 0x1F if subop == 0x07: # ld.hu insn.itype = NewInstructions.NN_ld_hu insn.Op1.type = o_displ insn.Op2.type = o_reg insn.Op1.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED insn.Op1.reg = self.parse_r1(hw1) buf = get_bytes(insn.ea + 4, 2) hw3 = unpack("<H", buf)[0] insn.Op1.addr = self.sign_extend( ((hw3 << 6) | ((hw2 & 0x7E0) >> 5)) << 1, 23) insn.Op1.dtyp = dt_dword insn.Op2.reg = self.parse_r2(hw2) insn.Op2.dtyp = dt_dword insn.size = 6 return True elif subop == 0xD: # st.h insn.itype = NewInstructions.NN_st_h insn.Op1.type = o_reg insn.Op2.type = o_displ insn.Op2.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED insn.Op2.reg = self.parse_r1(hw1) buf = get_bytes(insn.ea + 4, 2) hw3 = unpack("<H", buf)[0] insn.Op2.addr = self.sign_extend( ((hw3 << 6) | ((hw2 & 0x7E0) >> 5)) << 1, 23) insn.Op2.dtyp = dt_dword insn.Op1.reg = self.parse_r2(hw2) insn.Op1.dtyp = dt_dword insn.size = 6 return True # Format II elif op == 0x15: # sar imm5, reg2 insn.itype = NewInstructions.NN_sar insn.Op1.type = o_imm insn.Op2.type = o_reg insn.Op1.value = hw1 & 0x1F insn.Op2.reg = self.parse_r2(hw1) insn.size = 2 return True # Format IX, X, XI elif op == 0x3F: buf = get_bytes(insn.ea + 2, 2) hw2 = unpack("<H", buf)[0] subop = hw2 & 0x7FF if hw1 & 0x7FF == 0x7E0: if hw1 == 0x7E0: if hw2 == 0x14A: # feret insn.itype = NewInstructions.NN_feret insn.size = 4 return insn.size elif hw2 == 0x0148: # eiret insn.itype = NewInstructions.NN_eiret insn.size = 4 return True elif subop == 0x366: # sch1l reg2, reg3 insn.itype = NewInstructions.NN_sch1l insn.Op1.type = o_reg insn.Op2.type = o_reg insn.Op1.reg = self.parse_r2(hw1) insn.Op2.reg = self.parse_r3(hw2) insn.size = 4 return True elif subop == 0x362: # sch1r reg2, reg3 insn.itype = NewInstructions.NN_sch1r insn.Op1.type = o_reg insn.Op2.type = o_reg insn.Op1.reg = self.parse_r2(hw1) insn.Op2.reg = self.parse_r3(hw2) insn.size = 4 return True insn_handled = False if subop == hw2 == 0xA0: # sar reg1, reg2 insn.itype = NewInstructions.NN_sar insn.Op1.type = o_reg insn.Op2.type = o_reg insn.Op1.reg = self.parse_r1(hw1) insn.Op2.reg = self.parse_r2(hw1) insn.size = 4 return True elif subop == 0xEE: # caxi [reg1], reg2, reg3 insn.itype = NewInstructions.NN_caxi insn.Op1.type = o_displ insn.Op1.addr = 0 insn.Op2.type = o_reg insn.Op3.type = o_reg insn.Op1.reg = self.parse_r1(hw1) insn.Op2.reg = self.parse_r2(hw1) insn.Op2.reg = self.parse_r3(hw2) insn.size = 4 return True elif subop == 0x2FC: # divq reg1, reg2, reg3 insn.itype = NewInstructions.NN_divq insn.size = 4 insn_handled = True elif subop == 0x2FE: # divqu reg1, reg2, reg3 insn.itype = NewInstructions.NN_divqu insn.size = 4 insn_handled = True elif subop == 0xA2: # sar reg1, reg2, reg3 insn.itype = NewInstructions.NN_sar insn.size = 4 insn_handled = True elif subop == 0xC2: # shl reg1, reg2, reg3 insn.itype = NewInstructions.NN_shl insn.size = 4 insn_handled = True elif subop == 0x82: # shr reg1, reg2, reg3 insn.itype = NewInstructions.NN_shr insn.size = 4 insn_handled = True if insn_handled: insn.Op1.type = o_reg insn.Op2.type = o_reg insn.Op3.type = o_reg insn.Op1.reg = self.parse_r1(hw1) insn.Op2.reg = self.parse_r2(hw1) insn.Op3.reg = self.parse_r3(hw2) return True return False
def save_x(unique_name=None, start=None, size=None): ea = ida_kernwin.get_screen_ea() # signature if not unique_name: if not start: seg = ida_segment.getseg(ea) start = seg.start_ea sig_bytes = ida_bytes.get_bytes(start, SIGNATURE_SIZE) sig_hash = hashlib.md5(sig_bytes).hexdigest() unique_name = sig_hash if not start or not size: seg = ida_segment.getseg(ea) start = seg.start_ea size = seg.size() # (start_addr, end_addr, names, comms) saved_data = {} if MD5_hash_data_file and os.path.isfile(MD5_hash_data_file): with open(MD5_hash_data_file, "rb") as ifile: received_data = pickle.loads(ifile.read()) if received_data: saved_data = received_data # save names (func_names, labels, etc) # (addr, name, is_code) names_addr_name = [] names = idautils.Names() for addr, name in names: if start <= addr <= start + size: flags = ida_bytes.get_flags(addr) names_addr_name.append( (addr - start, name, ida_bytes.is_code(flags))) # save comments comms_addr_type_comm = [] # (addr, TYPE, comment) # type 0:comment 1:rpt_comment end = start + size for i in range(start, end + 1): if ida_bytes.get_cmt(i, 0): # 0 Comment comms_addr_type_comm.append((i - start, 0, ida_bytes.get_cmt(i, 0))) if ida_bytes.get_cmt(i, 1): # 1 RptCmt comms_addr_type_comm.append((i - start, 1, ida_bytes.get_cmt(i, 1))) # breakpoints bpts_addr_size_type = [] bpt = ida_dbg.bpt_t() global remove_on_exit_bpts for i in range(start, end + 1): if ida_dbg.get_bpt(i, bpt): bpts_addr_size_type.append((i - start, bpt.size, bpt.type)) remove_on_exit_bpts.append(i) # functions funcs_addr = [] flag = ida_bytes.get_flags(start) if ida_bytes.is_func(flag): funcs_addr.append(0) # start addr next_func = ida_funcs.get_next_func(start) while next_func: funcs_addr.append(next_func.start_ea - start) next_func = ida_funcs.get_next_func(next_func.start_ea) # SAVE saved_data[unique_name] = (start, start + end, names_addr_name, comms_addr_type_comm, bpts_addr_size_type, funcs_addr) if MD5_hash_data_file: with open(MD5_hash_data_file, "wb") as ifile: serial_data = pickle.dumps(saved_data) ifile.write(serial_data) print("dumpDyn::save:\n\ Name: {}\n\ Start address: {}".format(unique_name, hex(start)))
def run(self, arg): start_time = time.time() is_64bits = idaapi.get_inf_structure().is_64bit() if is_64bits: print("这个脚本只考虑了32位SO的反编译代码,64位未适配。") textStart, textEnd, end = getSegAddr() found = {} offsets = [] # 从二进制中搜索,这部分和findcrypt/signsrch中的处理类似 sig_list = load_signatures() bytes = ida_bytes.get_bytes(0, end) for sig in sig_list: oneInfo = {"describe": 0, "type": 0} ea = None idx = bytes.find(sig["data"]) if idx != -1: ea = idx ## 同一个魔数可能在so文件中出现多次 while ea != None: name = sig["name"] offset = hex(ea) oneInfo["type"] = 1 oneInfo["describe"] = name found[offset] = oneInfo offsets.append(offset) idx = bytes.find(sig["data"], ea + sig["size"]) if idx != -1: ea = idx else: ea = None # 正则匹配伪C中的魔数初始化,以及哈希运算部分 for func in idautils.Functions(textStart, textEnd): try: oneInfo = {'type': 0, "describe": 0, "funcName": 0, "init": 0, "round": 0, "hookOffset": 0} decompilerStr = str(idaapi.decompile(func)) Suspected_magic_num = [i[1] for i in re.findall( "(]|\+ \d{1,3}\)) = -?(0?x?[0-9A-FL]{8,20});", decompilerStr)] Suspected_transform_funcs = re.findall(" ([^ (*]{2,}?)\(", decompilerStr)[1:] funcs_count = list(Counter(Suspected_transform_funcs).values()) max_func_num = max(funcs_count) if funcs_count else 0 if len(Suspected_magic_num) >= 3: if hex(func) in offsets: found[hex(func)]["init"] = 1 else: functionName = demangle_str( str(idaapi.ida_funcs.get_func_name(func))) oneInfo["type"] = 2 oneInfo["funcName"] = functionName oneInfo["init"] = 1 found[hex(func)] = oneInfo if max_func_num > 60: if hex(func) in offsets: found[hex(func)]["round"] = 1 else: functionName = demangle_str( str(idaapi.ida_funcs.get_func_name(func))) oneInfo["type"] = 2 oneInfo["funcName"] = functionName oneInfo["round"] = 1 found[hex(func)] = oneInfo except: pass funclist, constlist = get_result(found) print("***************************在二进制文件中检索hash算法常量************************************") for i in constlist: print(i[1] + ":" + i[0]) for i in funclist: print(i[2] + ":" + i[1]) myscript = generate_script(funclist, constlist) script_name = so_name.split(".")[0] + "_findhash_" + str(int(time.time())) + ".js" save_path = os.path.join(so_path, script_name) with open(save_path, "w", encoding="utf-8")as F: F.write(myscript) # 对哈希相关的字符串和出现的路径进行搜索 IDAStrings = idautils.Strings() IDAStrings = [[str(i), i.ea] for i in IDAStrings] hashstring = ["md", "dgst", "digest", "final", "update", "sha"] SOURCE_FILES_REGEXP = r"([a-z_\/\\][a-z0-9_/\\:\-\.@]+\.(c|cc|cxx|c\+\+|cpp|h|hpp|m|rs|go|ml))($|:| )" Suspected_string = [] for s, ea in IDAStrings: s = demangle_str(s) if s and len(s) > 4: path = re.findall(SOURCE_FILES_REGEXP, s, re.IGNORECASE) if path: Suspected_string.append([ea, path[0][0]]) for h in hashstring: if (h in s) and ("__cxa_finalize" not in s): Suspected_string.append([ea, s]) print("***************************存在以下可疑的字符串************************************") for ea, i in Suspected_string: print(f"{hex(ea)}:{i}") print("生成对应的hook脚本如下:") print(f"frida -UF -l {save_path}") print("***********************************************************************************") print("花费 %s 秒,因为会对全部函数反编译,所以比较耗时间哈" % (time.time() - start_time))
def memcpy(src, dst, length): if length == 0: return data = ida_bytes.get_bytes(src, length) ida_bytes.put_bytes(dst, data)