def markSwitchTables(self, sc, aggressive=True): """Help IDA by marking all of the needed information from the observed switch tables. Args: sc (segment): (sark) code segment in which we are interested right now aggressive (bool, optional): True iff the marking operation should be aggressive, see notes. (True by default) Notes ----- 1. Make sure the switch case jump instruction is indeed a code line 2. Make sure the jump instruction has a code reference to all of the switch cases 3. (Aggressive) Make sure each switch table entry is a proper code pointer to it's matching case 4. (Aggressive) Enforce the correct code type over the entire gap between the minimal and maximal case """ for switch_instr, table_start, table_end in [ x for x in self._switch_case_entries if sc.start_ea <= x[0] and x[1] < sc.end_ea ]: cases = [] if not sark.Line(switch_instr).is_code: ida_bytes.del_items(switch_instr, 0, self._analyzer.addressSize()) idc.create_insn(switch_instr) for ea in range(table_start, table_end, self._analyzer.addressSize()): entry = self._analyzer.parseAdderss(ea) if aggressive: self._analyzer.markCodePtr(ea, entry) fixed_entry = self._analyzer.cleanPtr(entry) cases.append(fixed_entry) idc.add_cref(switch_instr, fixed_entry, idc.XREF_USER | idc.dr_O) if aggressive: self._analyzer.setCodeType(min(cases), max(cases), self._analyzer.ptrCodeType(entry))
def add_stub_func(va, sc, nm): idaapi.patch_bytes(va, binascii.unhexlify(sc)) idc.create_insn(va) idc.add_func(va) mykutils.makename_safe(va, self._stubname(nm)) cmt = ('%s implementation generated by FLARE Code Grafter' % (nm)) idc.set_cmt(va, cmt, 1)
def ida_main(): import idc filepath = ida_kernwin.ask_file(0, "*.map", "Load a Dolphin emulator symbol map") if filepath is None: return symbol_map = load_dolphin_map(filepath) for symbol in symbol_map: addr = int(symbol.vaddr, 16) size = int(symbol.size, 16) ida_bytes.del_items(addr, size, 0) if symbol.section in [".init", ".text"]: idc.create_insn(addr) success = ida_funcs.add_func( addr, idc.BADADDR if not size else (addr+size) ) else: success = ida_bytes.create_data(addr, idc.FF_BYTE, size, 0) if not success: ida_kernwin.msg("Can't apply properties for symbol:" " {0.vaddr} - {0.name}\n".format(symbol)) flags = idc.SN_NOCHECK | idc.SN_PUBLIC if symbol.name.startswith("zz_"): flags |= idc.SN_AUTO | idc.SN_WEAK else: flags |= idc.SN_NON_AUTO idc.set_name(addr, symbol.name, flags)
def resolve_loops(): PATTERNS = ["81 FB ?? ?? ?? ?? 75"] count_patched = 0 count_not_patched = 0 for pattern in PATTERNS: ea = 0 while ea != BADADDR: ''' pattern: 81 FB ?? ?? ?? ?? 75 .text:00406AA0 01 C7 add edi, eax .text:00406AA2 66 41 inc cx .text:00406AA4 43 inc ebx .text:00406AA5 81 FB A6 01 00 00 cmp ebx, 1A6h .text:00406AAB 75 F3 jnz short loc_406AA0 patched: .text:00406AA0 01 C7 add edi, eax .text:00406AA2 66 41 inc cx .text:00406AA4 43 inc ebx .text:00406AA5 90 nop .text:00406AA6 90 nop .text:00406AA7 90 nop .text:00406AA8 90 nop .text:00406AA9 90 nop .text:00406AAA 90 nop .text:00406AAB 90 nop .text:00406AAC 90 nop ''' ea = ida_search.find_binary( ea, BADADDR, pattern, 16, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE) if ea_in_bounds(ea): # Patch CMP and conditional jmp instructions in order to remove the loop ida_bytes.patch_byte(ea + 0, 0x90) ida_bytes.patch_byte(ea + 1, 0x90) ida_bytes.patch_byte(ea + 2, 0x90) ida_bytes.patch_byte(ea + 3, 0x90) ida_bytes.patch_byte(ea + 4, 0x90) ida_bytes.patch_byte(ea + 5, 0x90) ida_bytes.patch_byte(ea + 6, 0x90) ida_bytes.patch_byte(ea + 7, 0x90) idc.create_insn(ea) count_patched += 1 print("\tPatched resolve_loops: {0}".format(count_patched)) print("\tNot Patched resolve_loops: {0}".format(count_not_patched))
def deobfuscate_rets(ea): if idc.get_wide_byte(ea) == 0x83: fill_with_nops(ea, ea + 7) else: fill_with_nops(ea, ea + 8) idc.patch_byte(ea, 0xC3) idc.create_insn(ea) return True
def format_vfunc_name(self, ea, current_func_name, proposed_func_name, class_name, parent_class_names): if current_func_name.startswith("j_"): # jump current_func_name = current_func_name.lstrip("j_") if current_func_name.startswith("qword_"): idc.auto_mark_range(ea, ea + 1, idc.AU_CODE) idc.create_insn(ea) idc.add_func(ea) current_func_name = api.get_addr_name(ea) print( "Info: qword in vtbl of {1} at 0x{0:X}, it may be an offset to undefined code" .format(ea, class_name)) # Previously renamed as a vfunc if current_func_name.startswith(class_name): # return the proposed func name in case it was updated since last run current_class_name = current_func_name.rsplit(".", 1)[0] return "{0}.{1}".format(current_class_name, proposed_func_name) # This should have been handled in the parent class if any( current_func_name.startswith(name) for name in parent_class_names): return "" if current_func_name.startswith("sub_"): return "{0}.{1}".format(class_name, proposed_func_name) if current_func_name.startswith("nullsub_"): return "{0}.{1}_nullsub".format(class_name, proposed_func_name) if current_func_name.startswith("loc_"): return "{0}.{1}_loc".format(class_name, proposed_func_name) if current_func_name.startswith("locret_"): return "{0}.{1}_locret".format(class_name, proposed_func_name) # Name it later in a child class when it gets overridden if current_func_name == "_purecall": return "" # Mangled func names, thanks IDA if current_func_name.startswith( "?") or current_func_name.startswith("_"): return "{0}.{1}".format(class_name, proposed_func_name) return None
def _convert_address_to_function(func): """Convert an address that IDA has classified incorrectly into a proper function.""" # If everything goes wrong, we'll try to restore this function. orig = idc.first_func_chunk(func) # If the address is not code, let's undefine whatever it is. if not ida_bytes.is_code(ida_bytes.get_full_flags(func)): if not is_mapped(func): # Well, that's awkward. return False item = ida_bytes.get_item_head(func) itemend = ida_bytes.get_item_end(func) if item != idc.BADADDR: _log(1, 'Undefining item {:#x} - {:#x}', item, itemend) ida_bytes.del_items(item, ida_bytes.DELIT_EXPAND) idc.create_insn(func) # Give IDA a chance to analyze the new code or else we won't be able to create a # function. #ida_auto.auto_wait() autoanalyze() idc.plan_and_wait(item, itemend) else: # Just try removing the chunk from its current function. IDA can add it to another function # automatically, so make sure it's removed from all functions by doing it in loop until it # fails. for i in range(1024): if not idc.remove_fchunk(func, func): break # Now try making a function. if ida_funcs.add_func(func) != 0: return True # This is a stubborn chunk. Try recording the list of chunks, deleting the original function, # creating the new function, then re-creating the original function. if orig != idc.BADADDR: chunks = list(idautils.Chunks(orig)) if ida_funcs.del_func(orig) != 0: # Ok, now let's create the new function, and recreate the original. if ida_funcs.add_func(func) != 0: if ida_funcs.add_func(orig) != 0: # Ok, so we created the functions! Now, if any of the original chunks are not # contained in a function, we'll abort and undo. if all(idaapi.get_func(start) for start, end in chunks): return True # Try to undo the damage. for start, _ in chunks: ida_funcs.del_func(start) # Everything we've tried so far has failed. If there was originally a function, try to restore # it. if orig != idc.BADADDR: _log(0, 'Trying to restore original function {:#x}', orig) ida_funcs.add_func(orig) return False
def simplify_jumps(ea): # If we got long first conditional jump if idc.get_wide_byte(ea) == 0x0F: alternative_jmp_cmd = idc.get_wide_byte(ea + 1) ^ 1 interm_jmp_offt = 6 else: alternative_jmp_cmd = idc.get_wide_byte(ea) ^ 0xF1 interm_jmp_offt = 2 # Get intermediate jump's value if idc.get_wide_byte(ea + interm_jmp_offt) == 0x0F: interm_jmp_param = idc.get_wide_dword(ea + interm_jmp_offt + 2) final_jmp_addr = ea + interm_jmp_param + interm_jmp_offt + 6 else: interm_jmp_param = idc.get_wide_byte(ea + interm_jmp_offt + 1) final_jmp_addr = ea + interm_jmp_param + interm_jmp_offt + 2 # Check the last conditional jump # 75 ?? ... 0F 85 ?? ?? ?? ?? if idc.get_wide_byte(final_jmp_addr) == 0x0F and \ idc.get_wide_byte(final_jmp_addr + 1) == alternative_jmp_cmd: final_jmp_param = idc.get_wide_dword(final_jmp_addr + 2) final_jmp_target = (final_jmp_addr + final_jmp_param + 6) & 0xFFFFFFFF # 75 ?? ... 75 ?? elif idc.get_wide_byte(final_jmp_addr) ^ 0xF0 == alternative_jmp_cmd: final_jmp_param = idc.get_wide_byte(final_jmp_addr + 1) final_jmp_target = (final_jmp_addr + final_jmp_param + 2) & 0xFFFFFFFF # Make a little cleanup: remove garbage code elif interm_jmp_param < 0x10: fill_with_nops(ea + interm_jmp_offt, final_jmp_addr) return True else: return if final_jmp_target - ea < 0xFF: fill_with_nops(ea + interm_jmp_offt, final_jmp_target) else: fill_with_nops(ea + interm_jmp_offt, final_jmp_addr + 6) # Restore seconds jump idc.patch_byte(ea + interm_jmp_offt, 0x0F) idc.patch_byte(ea + interm_jmp_offt + 1, alternative_jmp_cmd) idc.patch_dword(ea + interm_jmp_offt + 2, final_jmp_target - (ea + interm_jmp_offt) - 6) idc.create_insn(ea + interm_jmp_offt) return True
def get_func_end(self, start): if (idc.add_func(start)): return idc.find_func_end(start) ea = start while (idc.get_wide_byte(ea) != 0xCC): idc.create_insn(ea) ea += idc.get_item_size(ea) if (ea - start > self.jit_max_size): return 0 return ea
def markCodePtr(self, src, dest, aggressive=True): """Mark a code pointer from src to dest. Args: src (int): effective address for the pointer's location dest (int): effective address for the pointed code address aggressive (bool, optional): True iff should redefine the src & dest (True by default) """ clean_dest = self.cleanPtr(dest) if aggressive: ida_bytes.del_items(src, 0, self.addressSize()) if self.makeAddress(src): idc.add_dref(src, clean_dest, idc.XREF_USER | idc.dr_O) idc.add_cref(src, clean_dest, idc.XREF_USER | idc.dr_O) ida_offset.op_offset(src, 0, idc.REF_OFF32) if aggressive: ida_bytes.del_items(dest, 0, self.addressSize()) idc.create_insn(self.cleanPtr(dest))
def convert_code_data_region_to_code(start_ea, end_ea): size = end_ea - start_ea score, count = code_score(start_ea, end_ea) if score >= 0.75: #print '{:016x} {:8x} {}'.format(start_ea, size, score) idc.del_items(start_ea, idc.DELIT_SIMPLE, size) for ea in range(start_ea, end_ea, 4): ilen = idc.create_insn(ea) if ilen == 0: idc.create_dword(ea)
def resolve_opaque_push_retn(): PATTERNS = ["68 ?? ?? ?? ?? C3"] count_patched = 0 for pattern in PATTERNS: ea = 0 while ea != BADADDR: ea = ida_search.find_binary( ea, BADADDR, pattern, 16, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE) ''' pattern: 68 ?? ?? ?? ?? C3 .text:00406922 68 B2 6A 00 10 push 0x10006ab2 .text:00406927 C3 retn patched: .text:00406922 E9 B2 6A 00 10 jmp 0x10006ab2 .text:00406927 90 nop ''' if ea_in_bounds(ea): j_pos = ea pos_jmp = idc.get_wide_dword(j_pos + 1) # absolute address offset = pos_jmp - 5 - j_pos # Patch the push and retn instructions with NOPs (6 bytes) for i in range(0, 6): ida_bytes.patch_byte(j_pos + i, 0x90) ida_bytes.patch_byte(j_pos, 0xE9) ida_bytes.patch_dword(j_pos + 0x1, offset) idc.create_insn(ea) count_patched += 1 print("\tPatched resolve_opaque_push_retn: {0}".format(count_patched))
def resolve_obf_calls(ea): next_instr_addr = idc.get_wide_dword(ea + 1) first_jmp_target = (ea + idc.get_wide_dword(ea + 0x7) + 0xB) & 0xFFFFFFFF second_jmp_target = (ea + idc.get_wide_dword(ea + 0xD) + 0x11) & 0xFFFFFFFF if first_jmp_target != second_jmp_target: return call_param = (first_jmp_target - ea - 5) & 0xFFFFFFFF # Now we can replace all code till next instruction's address with NOPs fill_with_nops(ea, next_instr_addr) # Insert CALL ida_bytes.patch_byte(ea, 0xE8) # CALL ida_bytes.patch_dword(ea + 1, call_param) idc.create_insn(ea) return True
def resolve_calls_through_register(ea): next_instr_addr = idc.get_wide_dword(ea + 1) # Check if that's just a parameter passing through stack if ea & 0xFFFF0000 != next_instr_addr & 0xFFFF0000: return if next_instr_addr - ea > 0x100: return if idc.get_wide_byte(ea + 6) & 0xF0 in [0x20, 0xE0]: call_param = 0x9000 + idc.get_wide_byte(ea + 6) ^ 0x30 else: call_param = idc.get_wide_word(ea + 6) ^ 0x30 fill_with_nops(ea, next_instr_addr) idc.patch_byte(ea, 0xFF) idc.patch_word(ea + 1, call_param) idc.create_insn(ea) return True
def check_previous_inst_is_call(return_addr, is_64bit): list_of_call_inst_lengths = [2, 3, 5, 6, 7] if is_64bit: list_of_call_inst_lengths.append(9) for call_length in list_of_call_inst_lengths: call_addr = return_addr - call_length try: if idaapi.is_call_insn(call_addr) and idc.create_insn( call_addr) and print_insn_mnem(call_addr) == "call": return (True, call_addr) except ValueError: continue return (False, None)
def cleanup_resolveapi_calls(ea): delta = 0 if idc.get_wide_byte(ea) == 0x6A else 3 intermediate_call_target = idc.get_wide_dword(ea + 8 + delta) if intermediate_call_target > 0x40: return # Check if we got library name if idc.get_wide_dword(ea + intermediate_call_target + 8 + delta) != 0x006c6c64: # "dll\x00" return # Check if we got enough space for patching if idc.get_wide_dword(ea + intermediate_call_target + 0x11 + delta) != 0x90909090 or \ idc.get_wide_byte(ea + intermediate_call_target + 0x15 + delta) != 0x90: return idc.patch_byte(ea + 7 + delta, 0xEB) idc.patch_byte(ea + 8 + delta, intermediate_call_target + 3) fill_with_nops(ea + 9 + delta, ea + 0xC + delta) idc.create_insn(ea + 7 + delta) # Now we need to pass module name parameter through stack resolve_api_call_param = idc.get_wide_dword(ea + intermediate_call_target + 0xD + delta) resolve_api_call_param = resolve_api_call_param - 5 # offset due to inserted PUSH command # Insert new PUSH command idc.patch_byte(ea + intermediate_call_target + 0xC + delta, 0x68) idc.patch_dword(ea + intermediate_call_target + 0xD + delta, ea + 0xC + delta) idc.create_insn(ea + intermediate_call_target + 0xC + delta) # Restore call ResolveApi idc.patch_byte(ea + intermediate_call_target + 0x11 + delta, 0xE8) idc.patch_dword(ea + intermediate_call_target + 0x12 + delta, resolve_api_call_param) idc.create_insn(ea + intermediate_call_target + 0x11 + delta) return True
def makeInsn(self, addr): if idc.create_insn(addr) == 0: idc.del_items(addr, idc.DELIT_EXPAND) idc.create_insn(addr) idc.auto_wait()
def dataScan(analyzer, scs): """Scan the code segments for orphan data blobs that represent analysis errors. Args: analyzer (instance): analyzer instance to be used scs (list): list of (sark) code segments """ # First Scan - unreffed data chunks inside functions ==> should be converted to code for sc in scs: first_line = None end_line = None for line in sc.lines: # After the first, the rest of the lines should have 0 crefs if first_line is not None and ((not line.is_data) or len(list(line.drefs_to)) > 0 or len(list(line.crefs_to)) > 0): end_line = line # we only care about data lines with a single cref from the previous line elif first_line is None and ( (not line.is_data) or len(list(line.drefs_to)) > 0 or len(list(line.crefs_to)) != 1 or sark.Line(list(line.crefs_to)[0]).next != line): end_line = line # don't mark switch entries elif analyzer.switch_identifier.isSwitchEntry(line.start_ea): end_line = line # Finally, check if it could be a function of some type elif first_line is None: first_line = line continue # Found an adjacent suitable line else: continue # Now check if we found something (end_line is always != None at this point) if first_line is not None and end_line is not None: chunk_start = first_line.start_ea chunk_end = end_line.start_ea # check that we can deduce anything on this current code type if not analyzer.supportedCodeType( analyzer.codeType(chunk_start)): continue # check that the chunk before us is not the end of a function if analyzer.func_classifier.predictFunctionEnd(chunk_start): # shouldn't really happen, do nothing in this case pass # data chunk in the middle of a function, and not at it's end - convert it to code else: analyzer.logger.debug( "In-Function data chunk at: 0x%x - 0x%x (%d)", chunk_start, chunk_end, chunk_end - chunk_start) ida_bytes.del_items(chunk_start, 0, chunk_end - chunk_start) idc.create_insn(chunk_start) # reset the vars first_line = None end_line = None # Second scan - unreffed data chunks outside of functions ==> new functions, possibly of different code type size_limit = analyzer.func_classifier.functionStartSize() analyzer.logger.debug("Size limit for data scan is: %d", size_limit) conversion_candidates = [] # recon pass for sc in scs: first_line = None end_line = None for line in sc.lines: # we only care about data lines without xrefs if (not line.is_data) or len(list(line.crefs_to)) > 0 or len( list(line.drefs_to)) > 0: end_line = line # check if it's big enough for the classifier elif line.size < size_limit: end_line = line # check if it looks like a string elif analyzer.str_identifier.isLocalAsciiString(line.start_ea, check_refs=False): analyzer.str_identifier.defineAsciiString(line.start_ea) end_line = line # make sure it isn't a switch entry elif analyzer.switch_identifier.isSwitchEntry(line.start_ea): end_line = line # Finally, check if it could be a function of some type elif first_line is None: first_line = line continue # Found an adjacent suitable line else: continue # Now check if we found something (end_line is always != None at this point) if first_line is not None and end_line is not None: chunk_start = first_line.start_ea chunk_end = end_line.start_ea guess_code_type = analyzer.func_classifier.predictFunctionStartType( chunk_start) original_code_type = analyzer.codeType(chunk_start) analyzer.logger.debug( "Found a data chunk at: 0x%x - 0x%x (%d), (Type %d, Local type %d)", chunk_start, chunk_end, chunk_end - chunk_start, guess_code_type, original_code_type) # Check if this is the beginning of a function if analyzer.func_classifier.predictFunctionStart( chunk_start, guess_code_type): conversion_candidates.append( (chunk_start, chunk_end, guess_code_type, original_code_type)) # reset the vars first_line = None end_line = None # conversion pass for chunk_start, chunk_end, guess_code_type, original_code_type in conversion_candidates: analyzer.logger.info( "Found an isolated data chunk at: 0x%x - 0x%x (%d), (Type %d, Local type %d)", chunk_start, chunk_end, chunk_end - chunk_start, guess_code_type, original_code_type) ida_bytes.del_items(chunk_start, 0, chunk_end - chunk_start) if original_code_type != guess_code_type: analyzer.setCodeType(chunk_start, chunk_end, guess_code_type) idc.plan_and_wait(chunk_start, chunk_end) ida_funcs.add_func(chunk_start)
def __call__(self): idc.create_insn(self.ea)
def try_mark_as_code(ea): if is_code(ea) and not is_code_by_flags(ea): idc.create_insn(ea) idaapi.auto_wait() return True return False
def apply(self): if self.data_at_address: ida_bytes.del_items(self.address, ida_bytes.DELIT_SIMPLE) idc.create_insn(self.address) return True
def cmd_make_code(self, a): return str(idc.create_insn(int(a, 0)))
def get_all_calls(): is_64bit = set_version_and_platform_specific_elements() call_list = [] call_addr_list = [] #check if stack segment is None. It's a problem if is_64bit: sp = cpu.Rsp ip = cpu.Rip else: sp = cpu.Esp ip = cpu.Eip if idaapi.getseg(cpu.Rsp) is None: warning("Stack segment is None") return False #information about current fun is_current_fun = True call_list.append(get_info_about_call(ip, "", sp, is_current_fun)) call_addr_list.append(ip) #iterate by stack if is_64bit: #at the begging return address is set as a current ip return_addr = cpu.Rip curr_sp = cpu.Rsp curr_bp = cpu.Rbp for i in range(0, MAX_NUMBER_FUNCTION_TO_SHOW): return_addr, curr_sp, curr_bp = calculate_caller_function_frame_info_x64( return_addr, curr_sp, curr_bp) if return_addr == idaapi.BADADDR or return_addr == 0: break curr_segment = idaapi.getseg(return_addr) #segmenst must exists and be executable if not curr_segment or (curr_segment.perm & idaapi.SEGPERM_EXEC) == 0: continue is_call, curr_call_addr = check_previous_inst_is_call( return_addr, is_64bit) if not is_call: continue #if bytes are not disassembled, then do it flags = ida_bytes.get_full_flags(curr_call_addr) if not ida_bytes.is_code(flags): idc.create_insn(curr_call_addr) #save a call argument call_list.append( get_info_about_call(curr_call_addr, print_operand(curr_call_addr, 0), return_addr)) call_addr_list.append(curr_call_addr) else: curr_ebp = cpu.Ebp ptr_size = 4 pfn_get_ptr = idc.get_wide_dword for i in range(0, MAX_NUMBER_FUNCTION_TO_SHOW): return_addr = pfn_get_ptr(curr_ebp + ptr_size) debug('ret: {:08X}'.format(return_addr)) curr_ebp = pfn_get_ptr(curr_ebp) debug('ebp: {:08X}'.format(curr_ebp)) if return_addr == idaapi.BADADDR or return_addr == 0: break curr_segment = idaapi.getseg(return_addr) #segmenst must exists and be executable if not curr_segment or (curr_segment.perm & idaapi.SEGPERM_EXEC) == 0: continue is_call, curr_call_addr = check_previous_inst_is_call( return_addr, is_64bit) if not is_call: continue #if bytes are not disassembled, then do it flags = ida_bytes.get_full_flags(curr_call_addr) if not ida_bytes.is_code(flags): idc.create_insn(curr_call_addr) #save a call argument call_list.append( get_info_about_call(curr_call_addr, print_operand(curr_call_addr, 0), curr_ebp)) call_addr_list.append(curr_call_addr) return True, call_list, call_addr_list
def locateDataPtrs(self, scs, sds): """Locate all data / code fptrs in the given set of segments. Args: scs (list): list of (sark) code segments sds (list): list of (sark) data segments """ local_ref_ptrs = defaultdict(set) seen_list = [] approved_ptrs = [] approved_eas = set() ptrs_mappings = defaultdict(set) marked_artifacts = [] for sd in sds: cur_ea = pad(sd.start_ea, self._analyzer.data_fptr_alignment) while cur_ea < sd.end_ea: line = sark.Line(cur_ea) if line.is_string: cur_ea += pad(line.size, self._analyzer.data_fptr_alignment) continue # check for a function ptr value = self._analyzer.parseAdderss(cur_ea) # make sure it is valid (enforces that the code_type is active) if self.isValidCodePtr(value, scs): func_value = self._analyzer.cleanPtr(value) code_type = self._analyzer.ptrCodeType(value) # is seen if func_value in local_ref_ptrs: local_ref_ptrs[func_value].add(code_type) ptrs_mappings[func_value].add(cur_ea) self._analyzer.logger.debug( "Located a fptr from 0x%x to 0x%x (type: %d) - Undeclared function", cur_ea, func_value, code_type) if self.isPrintableAddress(value): self._analyzer.logger.debug( "Looks like a printable FP: 0x%x", value) approved_ptrs.append((cur_ea, value)) approved_eas.add(cur_ea) seen_list.append((cur_ea, True)) marked_artifacts.append((cur_ea, True)) # is start of real function, from the correct type elif self._analyzer.codeType( func_value ) == code_type and self._analyzer.func_classifier.isFuncStart( func_value): local_ref_ptrs[func_value].add(code_type) ptrs_mappings[func_value].add(cur_ea) self._analyzer.logger.debug( "Located a fptr from 0x%x to 0x%x (type: %d) - Existing function", cur_ea, func_value, code_type) approved_ptrs.append((cur_ea, value)) approved_eas.add(cur_ea) seen_list.append((cur_ea, True)) marked_artifacts.append((cur_ea, True)) # is start of function elif self._analyzer.func_classifier.predictFunctionStartMixed( func_value, known_type=code_type): local_ref_ptrs[func_value].add(code_type) ptrs_mappings[func_value].add(cur_ea) self._analyzer.logger.debug( "Located a fptr from 0x%x to 0x%x (type: %d) - Undeclared function", cur_ea, func_value, code_type) if self.isPrintableAddress(value): self._analyzer.logger.debug( "Looks like a printable FP: 0x%x", value) approved_ptrs.append((cur_ea, value)) approved_eas.add(cur_ea) seen_list.append((cur_ea, True)) marked_artifacts.append((cur_ea, True)) # only a candidate - may be will be approved later else: seen_list.append((cur_ea, False)) # check for an analysis problem if len(list(line.drefs_from)) > 0: idc.del_dref(cur_ea, value) idc.del_dref(cur_ea, func_value) # Check for a valid data pointer elif self.isValidDataPtr(value, sds): # make it a data pointer self._analyzer.markDataPtr(cur_ea, value) self._analyzer.logger.debug( "Located a data ptr from 0x%x to 0x%x", cur_ea, value) marked_artifacts.append((cur_ea, False)) marked_artifacts.append((value, False)) # continue forward cur_ea += pad(self._analyzer.addressSize(), self._analyzer.data_fptr_alignment) # check if there is some pattern we can use to find more fptrs chosen_threshold = 7 cur_window = [] window_index = 0 # NOTE: this step is too risky if there are Read-Only data constants inside the text section while window_index < len( seen_list) and not self._analyzer.isCodeMixedWithData(): # If we didn't reach the end, and # 1. The window doesn't have enough "True" pointers # 2. The windows contains only "True" pointers # Slide the window onward while window_index < len(seen_list) and ( len(list(filter(lambda x: x[1], cur_window))) < chosen_threshold or len(list(filter(lambda x: not x[1], cur_window))) == 0): # If we are above the threshold (meaning that cond #2 applies), kick out the first ptr (which is a "True" ptr) if chosen_threshold < len( list(filter(lambda x: x[1], cur_window))): cur_window = cur_window[1:] # Add a new pointer at the end of our window cur_window.append(seen_list[window_index]) window_index += 1 # Sanity check: check if we have a candidate if window_index == len(seen_list) and len( list(filter(lambda x: not x[1], cur_window))) == 0: break # measure the deltas chosen_window = list(filter(lambda x: x[1], cur_window)) # deltas between the "True" pointers chosen_deltas = set() for i in range(len(chosen_window) - 1): chosen_deltas.add(chosen_window[i + 1][0] - chosen_window[i][0]) # All possible deltas between adjacent pointers seen_deltas = set() for i in range(len(cur_window) - 1): seen_deltas.add(cur_window[i + 1][0] - cur_window[i][0]) new_chosen = None # check for a pattern if len(seen_deltas) <= len(chosen_deltas): new_chosen = list(filter(lambda x: not x[1], cur_window))[0] # check if the window starts with a candidate, that is right near a "True" pointer elif not cur_window[0][1]: first_seen = cur_window[0] seen_addr = first_seen[0] for candidate in [ seen_addr - self._analyzer.data_fptr_alignment, seen_addr + self._analyzer.data_fptr_alignment ]: if candidate in approved_eas: new_chosen = first_seen break # check if found a match if new_chosen is not None: # re-insert ourselves with our new values our_index = cur_window.index(new_chosen) cur_window = cur_window[:our_index] + [ (new_chosen[0], True) ] + cur_window[our_index + 1:] # mark the pointer cur_ea = new_chosen[0] value = self._analyzer.parseAdderss(cur_ea) func_value = self._analyzer.cleanPtr(value) code_type = self._analyzer.ptrCodeType(value) local_ref_ptrs[func_value].add(code_type) ptrs_mappings[func_value].add(cur_ea) approved_ptrs.append((cur_ea, value)) marked_artifacts.append((cur_ea, True)) approved_eas.add(cur_ea) self._analyzer.logger.debug( "Located new fptr from 0x%x to 0x%x (type: %d)", cur_ea, func_value, code_type) # advance the window cur_window = cur_window[1:] # filter the pointers (we could have false positives) disqualified_addresses = set() for cur_ea, raw_address in approved_ptrs: fixed_address = self._analyzer.cleanPtr(raw_address) disqualified = False # check if already disqualified if fixed_address not in ptrs_mappings: continue # Several code types for the same address, we take no chances and remove them all if len(local_ref_ptrs[fixed_address]) != 1: disqualified = True # Check if the code type is even legal for that address else: wanted_code_type = list(local_ref_ptrs[fixed_address])[0] orig_code_type = self._analyzer.codeType(fixed_address) idc.ida_bytes.del_items(fixed_address, 0, self._analyzer.addressSize()) if orig_code_type != wanted_code_type: self._analyzer.setCodeType(fixed_address, fixed_address + 4, wanted_code_type) if idc.create_insn(fixed_address) == 0: disqualified = True # Always clean after ourselves ida_bytes.del_items(fixed_address, 0, self._analyzer.addressSize()) if orig_code_type != wanted_code_type: self._analyzer.setCodeType( fixed_address, fixed_address + self._analyzer.addressSize(), orig_code_type) # We are OK, can continue if not disqualified: continue # Found a false function pointer # Be cautious with the removals, we could have duplicates if fixed_address in self._ptrs_mappings: self._ptrs_mappings.pop(fixed_address) disqualified_addresses.add(raw_address) marked_artifacts.remove((cur_ea, True)) # no need to remove from local_ref_ptrs, as the global variable only gets the approved values # no need to remove from approved_eas, as this data set isn't used anymore self._analyzer.logger.debug( "Disqualified (code) pointer 0x%08x from 0x%08x (type %d, seen types %s)", fixed_address, cur_ea, wanted_code_type, local_ref_ptrs[fixed_address]) # Now filter them based on scoped range from other artifacts marked_artifacts.sort(key=lambda x: x[0]) cur_index = 0 prev_artifact = None while cur_index < len(marked_artifacts) - 1: cur_ea, is_fptr = marked_artifacts[cur_index] next_ea, _ = marked_artifacts[cur_index + 1] # Only check ourselves against the next in line if cur_ea + FPTR_LOCALITY_RANGE < next_ea: if prev_artifact is None and is_fptr: # we should be disqualified raw_address = self._analyzer.parseAdderss(cur_ea) wanted_code_type = self._analyzer.ptrCodeType(raw_address) fixed_address = self._analyzer.cleanPtr(raw_address) # Be cautious with the removals, we could have duplicates if fixed_address in self._ptrs_mappings: self._ptrs_mappings.pop(fixed_address) disqualified_addresses.add(raw_address) self._analyzer.logger.debug( "Disqualified (scope) pointer 0x%08x from 0x%08x (type %d))", fixed_address, cur_ea, wanted_code_type) # set the prev artifact prev_artifact = None # check the next element cur_index += 1 # We are linking to the next element, so he is legit too else: prev_artifact = next_ea cur_index += 1 # mark the pointers for cur_ea, raw_address in filter( lambda x: x[1] not in disqualified_addresses, approved_ptrs): self._ref_ptrs[self._analyzer.cleanPtr( raw_address)] = self._analyzer.ptrCodeType(raw_address) self._analyzer.markCodePtr(cur_ea, raw_address) # print some results self._analyzer.logger.info( "Found %d different potential function pointer destinations", len(self._ref_ptrs))
def create_function( fnc, overwrite_names, offset, add_names=True, ): def set_name(addr, name): if not add_names: return try: numericName = int(name, 16) return except ValueError: pass ok = False if not IDAtools.is_dummy_name(name): ok = idc.set_name(addr, name, idc.SN_CHECK) if not ok: data = (name, size, addr) name = fnc.get("name") addr = int(fnc.get("addr")) addr = addr + int(offset) size = int(fnc.get("size")) if type(addr) == int: idc.create_insn(addr) code = IDAtools.is_code(addr) fnc = IDAtools.is_func(addr) fnc_chnk = IDAtools.is_func_chunk(addr) start = IDAtools.get_func_start(addr) generic = fnc and IDAtools.func_is_generic(addr) if size >= 2 and size % 2 == 0: end = addr + size else: end = idaapi.BADADDR if (fnc or fnc_chnk): ok = idc.set_func_end(addr, addr) if not ok: pass #print("{0:#x}: cannot add fnc, there still exists code, {1}, {2:#x}".format(addr, size, end)) if IDAtools.is_dummy_name_by_addr(addr) and ( not name.startswith("sub_") and overwrite_names): set_name(addr, name) elif start == addr and overwrite_names: set_name(addr, name) elif start != addr: if fnc_chnk: idc.remove_fchunk(addr, addr) elif fnc: idc.set_func_end( addr, addr) # force previous function to end here. ok = idaapi.add_func(addr, end) if ok and add_names and overwrite_names: set_name(addr, name) if not ok: print("{0:#x}: cannot add fnc, {1}, {2:#x}".format( addr, size, end)) else: print( "{0:#x}: unknown problem - code: {1}, fnc: {2}, start {3:#x}, size {4}, end {5}" .format(addr, code, fnc, start, size, end)) IDAtools.ida_wait()
def load_file(li, neflags, format): # Set flags idaapi.set_processor_type( "arm", idaapi.SETPROC_LOADER_NON_FATAL | idaapi.SETPROC_LOADER) idc.set_inf_attr(idc.INF_LFLAGS, idc.get_inf_attr(idc.INF_LFLAGS) | idc.LFLG_64BIT) idc.set_inf_attr(idc.INF_DEMNAMES, idaapi.DEMNAM_GCC3) idaapi.set_compiler_id(idaapi.COMP_GNU) idaapi.add_til('gnulnx_arm64', 1) # Load IRAM memory li.seek(0) pk11 = li.read(PK1L_SIZE) idaapi.mem2base(pk11, PK1L_ADDRESS) # Add identity mappings add_segment(PK1L_ADDRESS, PK1L_SIZE, '.pk1_identity', 'RWX') add_segment(TZRAM_BASE, TZRAM_SIZE, '.tz_identity', 'RWX') # Emulate package1 to determine interesting extents emu = Emulator() emu.run_emulator(pk11) # Refresh IRAM contents to reflect any decompression that may have occurred. idaapi.mem2base(emu.read_mem(PK1L_ADDRESS, PK1L_SIZE), PK1L_ADDRESS) if not emu.found_smc_evp: # Set coldboot crt0 idc.create_insn(emu.entrypoint) idc.set_name(emu.entrypoint, 'coldboot_crt0') return 1 iram_mappings = [] dram_mappings = [] tzram_mappings = [] mmio_mappings = [] for m in emu.mappings: if is_tzram(m): tzram_mappings.append(m) elif is_dram(m): dram_mappings.append(m) elif is_iram(m): iram_mappings.append(m) else: assert is_mmio(m) mmio_mappings.append(m) for m in mmio_mappings: add_mmio_mapping(m) # Process IRAM mappings if len(iram_mappings) == 3: sorted_irams = sorted(iram_mappings, key=lambda m: m[2]) if [m[2] for m in sorted_irams] == [0x1000, 0x1000, 0x10000]: assert len(set([m[3] for m in sorted_irams])) == 2 if sorted_irams[1][3] == sorted_irams[2][3]: add_segment(sorted_irams[0][0], sorted_irams[0][2], '.bl_sync', 'IO') add_segment(sorted_irams[1][0], sorted_irams[1][2], '.sc7_fw', 'DATA') add_segment(sorted_irams[2][0], sorted_irams[2][2], '.sc7_tmp_save', 'DATA') else: assert sorted_irams[0][3] == sorted_irams[2][3] add_segment(sorted_irams[1][0], sorted_irams[1][2], '.bl_sync', 'IO') add_segment(sorted_irams[0][0], sorted_irams[0][2], '.sc7_fw', 'DATA') add_segment(sorted_irams[2][0], sorted_irams[2][2], '.sc7_tmp_save', 'DATA') else: print iram_mappings raise ValueError('Unknown IRAM mapping set') elif len(iram_mappings) == 2: sorted_irams = sorted(iram_mappings, key=lambda m: m[2]) if [m[2] for m in sorted_irams] == [0x1000, 0x10000]: assert len(set([m[3] for m in sorted_irams])) == 2 add_segment(sorted_irams[0][0], sorted_irams[0][2], '.bl_sync', 'IO') add_segment(sorted_irams[1][0], sorted_irams[1][2], '.sc7_tmp_save', 'DATA') else: print iram_mappings raise ValueError('Unknown IRAM mapping set') else: print iram_mappings raise ValueError('Unknown IRAM mapping set') # Process DRAM mappings if len(dram_mappings) == 2: sorted_drams = sorted(dram_mappings, key=lambda m: m[2]) if [m[2] for m in sorted_drams] == [0x1000, 0x10000]: add_segment(sorted_drams[0][0], sorted_drams[0][2], '.sc7_se_ctx', 'DATA') add_segment(sorted_drams[1][0], sorted_drams[1][2], '.sc7_sm_ctz', 'DATA') else: print dram_mappings raise ValueError('Unknown DRAM mapping set') else: print dram_mappings raise ValueError('Unknown DRAM mapping set') # Process TZRAM segments tzram_groups = [] for (vaddr, paddr, size, attr) in sorted(tzram_mappings, key=lambda m: m[0]): inserted = False for i in xrange(len(tzram_groups)): if vaddr == tzram_groups[i][-1][0] + tzram_groups[i][-1][ 2] and tzram_groups[i][-1][2] != 0x40000: tzram_groups[i].append((vaddr, paddr, size, attr)) inserted = True break if not inserted: tzram_groups.append([(vaddr, paddr, size, attr)]) for group in tzram_groups: print 'Group' for m in group: print ' %x %x %x %x' % m # Process groups for group in tzram_groups: if len(group) > 1: if is_executable(group[0]): # .text/.rodata/.rwdata :) if len(group) == 2: add_segment(group[0][0], group[0][2], '.text_rodata', 'CODE') add_segment(group[1][0], group[1][2], '.rwdata', 'DATA') load_mapping(emu, group[0]) load_mapping(emu, group[1]) else: assert len(group) == 3 add_segment(group[0][0], group[0][2], '.text', 'CODE') add_segment(group[1][0], group[1][2], '.rodata', 'CONST') add_segment(group[2][0], group[2][2], '.rwdata', 'DATA') load_mapping(emu, group[0]) load_mapping(emu, group[1]) load_mapping(emu, group[2]) elif is_executable(group[0]): assert len(group) == 1 vaddr, paddr, size, attr = group[0] if size > 0x8000: assert len( filter(lambda g: is_executable(g[0]) and g[0][2] > 0x8000, tzram_groups)) == 1 assert is_writable(group[0]) add_segment(vaddr, size, '.code', 'RWX') load_mapping(emu, group[0]) elif size == 0x2000: assert len( filter(lambda g: is_executable(g[0]) and g[0][2] == 0x2000, tzram_groups)) == 1 add_segment(vaddr, size, '.pk2ldr', 'RWX') load_mapping(emu, group[0]) else: assert size == 0x1000 assert len( filter(lambda g: is_executable(g[0]) and g[0][2] == 0x1000, tzram_groups)) == 1 add_segment(vaddr, size, '.vectors', 'CODE') load_mapping(emu, group[0]) elif len(group) == 1 and (group[0][2] == 0x10000 or group[0][2] == 0x40000): assert len( filter( lambda g: len(g) == 1 and not is_executable(g[0]) and (g[0][2] == 0x10000 or g[0][2] == 0x40000), tzram_groups)) == 1 assert not is_writable(group[0]) vaddr, paddr, size, attr = group[0] add_segment(vaddr, size, '.tzram_ro_view', 'CONST') elif len(group) == 1 and group[0][2] == 0x1000: vaddr, paddr, size, attr = group[0] pk2ldr_group = filter( lambda g: is_executable(g[0]) and g[0][2] == 0x2000, tzram_groups) assert len(pk2ldr_group) == 1 pk2ldr_group = pk2ldr_group[0][0] if paddr == emu.l3_table: add_segment(vaddr, size, '.l3_table', 'DATA') elif paddr == (emu.l3_table - 0x1000): add_segment(vaddr, size, '.l2_table', 'DATA') elif paddr == pk2ldr_group[1]: add_segment(vaddr, size, '.reused_stack0', 'DATA') elif paddr == (pk2ldr_group[1] + 0x1000): add_segment(vaddr, size, '.reused_stack1', 'DATA') elif vaddr == emu.phys_to_virt[ emu.ttbr & ~0xFFF][0] or vaddr == emu.core0_stack_page: add_segment(vaddr, size, '.shared_data', 'DATA') else: print 'Unknown Group' for m in group: print ' %x %x %x %x' % m assert False else: print 'Unknown Group' for m in group: print ' %x %x %x %x' % m assert False # Set vector types as code. for i, ctx in enumerate(['sp0', 'spx', 'a64', 'a32']): for j, kind in enumerate(['synch', 'irq', 'fiq', 'serror']): addr = emu.vbar + 0x200 * i + 0x80 * j name = '%s_%s_exception' % (ctx, kind) idc.create_insn(addr) idc.set_name(addr, name) # Set coldboot crt0 idc.create_insn(emu.entrypoint) idc.set_name(emu.entrypoint, 'coldboot_crt0') # Add SMC list entries assert len(emu.smc_lists) == 2 idc.set_name(emu.smc_lists_addr, 'g_smc_lists') for i, name in enumerate(['user', 'priv']): addr, num_entries, pad = emu.smc_lists[i] idc.set_name(addr, 'g_smc_list_%s' % name) for n in xrange(num_entries): id, access, func = up('<IIQ', emu.read_mem(addr + 0x10 * n, 0x10)) if func == 0: continue smc_name = get_smc_name(name == 'user', id) process_smc(func, smc_name) # We're done return 1
def import_functions(fncs, add_names=True, always_thumb=True): name_counter = defaultdict(int) imported_functions = defaultdict(set) cfg_conflicts = defaultdict(set) symbol_defects = list() failed_fncs = list() countername_Fail = 0 counterfct_fail = 0 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) for name, size, addr, thumb in fncs: name_counter[name] += 1 if not always_thumb: idc.split_sreg_range(addr, "T", int(thumb), idc.SR_user) idc.create_insn(addr) code = is_code(addr) fnc = is_func(addr) fnc_chnk = is_func_chunk(addr) data = (name, size, addr) start = get_func_start(addr) generic = fnc and func_is_generic(addr) # BADADDR automatically finds end of fnc. if size >= 2 and size % 2 == 0: end = addr + size else: end = idaapi.BADADDR if not code and not (fnc or fnc_chnk): symbol_defects.append(data) elif (fnc or fnc_chnk) and not code: cfg_conflicts[idc.get_func_name(addr)] = (data, start) elif start == addr and not generic: set_name(addr, name) # duplicate symbol elif start == addr and generic: set_name(addr, name) elif start != addr: if fnc_chnk: idc.remove_fchunk(addr, addr) elif fnc: idc.set_func_end(addr, addr) # force previous function to end here. ok = idaapi.add_func(addr, end) if not ok: failed_fncs.append((data, "fnc")) print("{0:#x}: cannot add fnc, {1}, {2:#x}".format( addr, size, end)) counterfct_fail = counterfct_fail + 1 else: set_name(addr, name) else: failed_fncs.append((data, "unknown")) print( "{0:#x}: unknown problem - code: {1}, fnc: {2}, start {3:#x}, size {4}, end {5}" .format(addr, code, fnc, start, size, end)) ida_auto.auto_wait() print("not added functions: {1} , not added names: {0}".format( countername_Fail, counterfct_fail)) return name_counter, imported_functions, failed_fncs, cfg_conflicts, symbol_defects
def process_smc(func, smc_name): idc.create_insn(func) idc.set_name(func, 'smc_%s' % smc_name) disasm = get_disasm(func) if disasm[:2] == ['adrp', 'x1']: d = [ get_disasm(ea) for ea in [func, func + 4, func + 8, func + 12, func + 16] ] if not d[1][:2] == ['adrp', 'x2']: return if not d[2][:3] == ['add', 'x1', 'x1']: return if not d[3][:3] == ['add', 'x2', 'x2']: return if not d[4][0] == 'b': return func_impl = parse_adr_op(d[0][2]) + parse_adr_op(d[2][3]) gr_impl = parse_adr_op(d[1][2]) + parse_adr_op(d[3][3]) gr_name = '%s_get_result' % smc_name if smc_name == 'unwrap_titlekey': gr_name += '_data' idc.create_insn(func_impl) idc.set_name(func_impl, smc_name) idc.create_insn(gr_impl) idc.set_name(gr_impl, gr_name) if d[4][1].startswith('unk_'): async_smc = parse_adr_op(d[4][1]) idc.create_insn(async_smc) idc.set_name(async_smc, 'handle_asynchronous_smc') elif disasm[:2] == ['adrl', 'x1']: branch_d = get_disasm(func + 8) if branch_d[0] == 'b': func_impl = parse_adr_op(disasm[2]) idc.create_insn(func_impl) idc.set_name(func_impl, smc_name) if branch_d[1].startswith('unk_'): sync_smc = parse_adr_op(branch_d[1]) idc.create_insn(sync_smc) idc.set_name(sync_smc, 'handle_synchronous_smc')
def make_code(addresses): for ea in addresses: idc.create_insn(ea) return addresses
def activate(self, ctx): if self.action in ACTION_MENU_CONVERT: sel, start, end = lazy_read_selection() if not sel: idc.msg("[LazyIDA] Nothing to convert.") return False size = end - start data = idc.get_bytes(start, size) if isinstance(data, str): # python2 compatibility data = bytearray(data) assert size == len(data) name = idc.get_name(start, idc.GN_VISIBLE) if not name: name = "data" if data: output = None plg_print("Dump from 0x%X to 0x%X (%u bytes):" % (start, end - 1, size)) if self.action == ACTION_MENU_CONVERT[0]: # escaped string output = '"%s"' % "".join("\\x%02X" % b for b in data) elif self.action == ACTION_MENU_CONVERT[1]: # hex string, space output = " ".join("%02X" % b for b in data) elif self.action == ACTION_MENU_CONVERT[2]: # C array output = "unsigned char %s[%d] = {" % (name, size) for i in range(size): if i % 16 == 0: output += "\n " output += "0x%02X, " % data[i] output = output[:-2] + "\n};" elif self.action == ACTION_MENU_CONVERT[3]: # C array word data += b"\x00" array_size = (size + 1) // 2 output = "unsigned short %s[%d] = {" % (name, array_size) for i in range(0, size, 2): if i % 16 == 0: output += "\n " output += "0x%04X, " % u16(data[i:i+2]) output = output[:-2] + "\n};" elif self.action == ACTION_MENU_CONVERT[4]: # C array dword data += b"\x00" * 3 array_size = (size + 3) // 4 output = "unsigned int %s[%d] = {" % (name, array_size) for i in range(0, size, 4): if i % 32 == 0: output += "\n " output += "0x%08X, " % u32(data[i:i+4]) output = output[:-2] + "\n};" elif self.action == ACTION_MENU_CONVERT[5]: # C array qword data += b"\x00" * 7 array_size = (size + 7) // 8 output = "unsigned long %s[%d] = {" % (name, array_size) for i in range(0, size, 8): if i % 32 == 0: output += "\n " output += "%#018X, " % u64(data[i:i+8]) output = output[:-2] + "\n};" output = output.replace("0X", "0x") elif self.action == ACTION_MENU_CONVERT[6]: # python list output = "[%s]" % ", ".join("0x%02X" % b for b in data) elif self.action == ACTION_MENU_CONVERT[7]: # python list word data += b"\x00" output = "[%s]" % ", ".join("0x%04X" % u16(data[i:i+2]) for i in range(0, size, 2)) elif self.action == ACTION_MENU_CONVERT[8]: # python list dword data += b"\x00" * 3 output = "[%s]" % ", ".join("0x%08X" % u32(data[i:i+4]) for i in range(0, size, 4)) elif self.action == ACTION_MENU_CONVERT[9]: # python list qword data += b"\x00" * 7 output = "[%s]" % ", ".join("%#018X" % u64(data[i:i+8]) for i in range(0, size, 8)).replace("0X", "0x") elif self.action == ACTION_MENU_CONVERT[10]: # MASM byte array header = "%s db " % name output = header for i in range(size): if i and i % 16 == 0: output += "\n" output += " " * len(header) output += "0%02Xh, " % data[i] output = output[:-2] elif self.action == ACTION_MENU_CONVERT[11]: # GNU ASM byte array header = "%s: .byte " % name output = header for i in range(size): if i and i % 16 == 0: output += "\n" output += " " * len(header) output += "0x%02X, " % data[i] output = output[:-2] if output: print(output) copy_to_clip(output) output = None elif self.action == ACTION_MENU_COPY_DATA: # added by merc, modified by HTC sel, start, end = lazy_read_selection() if not sel: return 0 data = idaapi.get_bytes(start, end - start) if isinstance(data, str): data = bytearray(data) output = "".join("%02X" % b for b in data) copy_to_clip(output) plg_print("Hex string '%s' copied" % output) elif self.action == ACTION_MENU_DUMP_DATA: # add by HTC sel, start, end = lazy_read_selection() if not sel: return 0 size = end - start data = idaapi.get_bytes(start, size) assert len(data) == size if data and len(data) == size: dump_data_to_file("Dump_At_%X_Size_%d.dump" % (start, size), data) else: plg_print("0x%X: unable to get %d bytes" % (start, size)) elif self.action == ACTION_MENU_XOR_DATA: sel, start, end = lazy_read_selection() if not sel: return 0 size = end - start key = idaapi.ask_str("AA BB CC DD", 0, "Xor with hex values (or a string begin and end with\" or ')...") if not key: return 0 bytes_key = bytearray() if is_str(key): bytes_key = str_to_bytes(key) else: bytes_key = hex_to_bytes(key) if not bytes_key: return 0 data = idc.get_bytes(start, end - start) if isinstance(data, str): # python2 compatibility data = bytearray(data) output = xor_data(data, bytes_key) if not output: plg_print("Sorry, error occurred. My bug :( Please report.") return 0 assert size == len(output) plg_print("Xor result from 0x%X to 0x%X (%d bytes) with %s:" % (start, end, end - start, key)) process_data_result(start, output) elif self.action == ACTION_MENU_FILL_NOP: sel, start, end = lazy_read_selection() if not sel: return 0 idaapi.patch_bytes(start, b"\x90" * (end - start)) idc.create_insn(start) plg_print("Fill 0x%X to 0x%X (%u bytes) with NOPs" % (start, end, end - start)) elif self.action == ACTION_MENU_B64STD: base64_decode(True) elif self.action == ACTION_MENU_B64URL: base64_decode(False) elif self.action == ACTION_MENU_SCAN_VUL: plg_print("Finding Format String Vulnerability...") found = [] for addr in idautils.Functions(): name = idc.get_func_name(addr) if "printf" in name and "v" not in name and idc.get_segm_name(addr) in (".text", ".plt", ".idata"): xrefs = idautils.CodeRefsTo(addr, False) for xref in xrefs: vul = self.check_fmt_function(name, xref) if vul: found.append(vul) if found: plg_print("Done! %d possible vulnerabilities found." % len(found)) ch = VulnChoose("Vulnerability", found, None, False) ch.Show() else: plg_print("No format string vulnerabilities found.") else: return 0 return 1