def print_section_list(): for s in idautils.Segments(): seg = idaapi.getseg(s) print(" {name:\"%s\", begin:0x%x, end:0x%x}," % (idc.SegName(s), idc.SegStart(s), idc.SegEnd(s)))
def segName(self, selector): return idc.SegName(selector)
def is_tls_segment(ea): try: seg_name = idc.SegName(ea) return seg_name in (".tbss", ".tdata", ".tls") except: return False
def chunk_info(self, chunk_addr, chunk): line = idaapi.COLSTR("Chunk ", idaapi.SCOLOR_NUMBER) line += idaapi.COLSTR("0x%x\n\n" % (chunk_addr), idaapi.SCOLOR_INSN) line += idaapi.COLSTR("size: 0x%x\nfd: 0x%x - %s" % \ (chunk.size, chunk.fd, idc.SegName(chunk.fd)), SCOLOR_DEFAULT) return line
def slave_handler(task_id, f_eas_to_export, yatools, hash_provider): m = YaToolIDAModel(yatools, hash_provider) m.set_slave_skip(True) # v = MakeFileXmlExporter("database/database_%08i.xml" % (task_id)) v = ya.MakeFlatBufferExporter() logger.info("Computing port with %s" % os.path.abspath(DATABASE_PATH)) logger.info("slave connecting to %r" % (IPC_ADDRESS,)) client = Client(IPC_ADDRESS) client.send("READY") v.visit_start() try: loop_count = 0 data = client.recv() while data != "FINISHED": job_name = data if job_name == "ACCEPT_SEGMENT": (parent_id, seg_ea_start, seg_ea_end) = client.recv() logger.info("Received ACCEPT_SEGMENT job : parent:%X 0x%08X->0x%08X:%s" % (parent_id, seg_ea_start, seg_ea_end, idc.SegName(seg_ea_start))) m.set_skip_accept_segment(False) m.accept_segment(v, parent_id, seg_ea_start, seg_ea_end) m.set_skip_accept_segment(True) elif job_name == "ACCEPT_SEGMENT_CHUNK": (seg_ea_start, seg_ea_end, chunk_start, chunk_end) = client.recv() logger.info("Received ACCEPT_SEGMENT_CHUNK job : 0x%08X->0x%08X in seg 0x%08X->0x%08X:%s" % (chunk_start, chunk_end, seg_ea_start, seg_ea_end, idc.SegName(seg_ea_start))) m.set_skip_accept_segment(False) m.accept_segment_chunk( v, chunk_start, chunk_end, seg_start=seg_ea_start, seg_end=seg_ea_end, export_eas=True) m.set_skip_accept_segment(True) elif job_name != "NOP": logger.error("Received bad job : %r" % job_name) loop_count += 1 mem = get_mem_usage() out_of_memory = False if mem > MAX_SLAVE_MEMORY_USAGE_MB: logger.warning("Using %d Mb : limit reached" % mem) out_of_memory = True elif mem == 0 and loop_count % 4 == 3: objs = muppy.get_objects() obj_count = 0 for o in objs: if isinstance(o, idaapi.member_t): obj_count += 1 out_of_memory = obj_count > MAX_IDAMEMBER_T_COUNT if out_of_memory: client.send("OUTOFMEMORY") else: logger.warning("Memory limit not reached") client.send("READY") data = client.recv() except EOFError: logger.warning("Received EOF") traceback.print_exc() v.visit_end() with open(os.path.join(DATABASE_PATH, "database_%08i.yadb" % task_id), 'wb') as fh: fh.write(v.GetBuffer())
import sys import struct vftable_section_names = [ ".rodata", ".data.rel.ro", ".data.rel.ro.local", ".rdata" ] rtti_list = [] class_list = {} hasdrr = 0 hasgot = 0 hasplt = 0 for seg in idautils.Segments(): if idc.SegName(seg) == '.text': text_start = idc.SegStart(seg) text_end = idc.SegEnd(seg) if idc.SegName(seg) == '.pdata': pdata_start = idc.SegStart(seg) pdata_end = idc.SegEnd(seg) if idc.SegName(seg) == '.idata': idata_start = idc.SegStart(seg) idata_end = idc.SegEnd(seg) if idc.SegName(seg) == '.rdata': rdata_start = idc.SegStart(seg) rdata_end = idc.SegEnd(seg) if idc.SegName(seg) == '.data.rel.ro': drrdata_start = idc.SegStart(seg) drrdata_end = idc.SegEnd(seg) hasdrr = 1
def get_seg_range(seg): for s in idautils.Segments(): if idc.SegName(s) == seg: start_ea = idc.SegStart(s) end_ea = idc.SegEnd(s) return start_ea, end_ea
def find_and_rename_memcpy_function(): ea = 0 pattern = "57 56 8B 74 24 10 8B 4C 24 14 8B 7C 24 0C 8B C1 8B D1 03 C6 3B FE 76 08" memcpy_func_addr = None # Find memcpy func while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea and segment_name == idc.SegName(ea): memcpy_func_addr = ea idaapi.set_name(ea, "_memcpy_maze", idaapi.SN_FORCE) print "Pattern: {0}".format(pattern) print "\tPatched find_and_rename_memcpy_function: {0}".format( hex(memcpy_func_addr).split("L")[0]) # Find all memcpy refs and resolve them. # This is possible to be resolved with xref IDA functions # But because the code is obfuscated, it will be done this way if memcpy_func_addr: ''' .text:1001DEE1 0F 84 69 95 01 00 jz sub_10037450 .text:1001DEE7 0F 85 63 95 01 00 jnz sub_10037450 ''' patterns = [ "68 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 0F 85", "68 ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 0F 84" ] for pattern in patterns: count_patched = 0 count_not_patched = 0 ea = 0 while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) ''' Identify the ones that pushes the return .text:1001E0EC 68 01 E1 01 10 push offset byte_1001E101 .text:1001E0F1 0F 84 59 93 01 00 jz _memcpy_maze .text:1001E0F7 0F 85 53 93 01 00 jnz _memcpy_maze ''' # relative offset from first jz # push_instr_addr + push_instr_size + 2 (0F 84) relative_offset_addr = ea + 5 + 2 # push_inst_addr + push_instr_size + relative offset + jz_instr_size absolute_addr = ea + 0x5 + Dword(relative_offset_addr) + 0x6 # If points to the memcpy func if absolute_addr == memcpy_func_addr: # Copy instruction values before patching values = GetManyBytes(ea, 5 + 6 + 6) ''' Original .text:10001574 50 push eax .text:10001575 55 push ebp .text:10001576 53 push ebx .text:10001577 68 94 15 00 10 push offset dword_10001594 .text:1000157C 0F 84 CE 5E 03 00 jz _memcpy_maze .text:10001582 0F 85 C8 5E 03 00 jnz _memcpy_maze The idea is to patch in order to create: .text:10021905 52 push edx .text:10021906 56 push esi .text:10021907 50 push eax .text:10021908 FF 15 50 74 03 10 call dword ptr ds:_memcpy_maze .text:1002190E 68 21 19 02 10 push offset loc_10021921 .text:10021913 C3 retn .text:10021914 90 nop .text:10021915 90 nop .text:10021916 90 nop .text:10021917 90 nop .text:10021918 90 nop ''' # Patch push with the call _memcpy_maze idc.PatchByte(ea, 0xFF) idc.PatchByte(ea + 1, 0x15) idc.PatchDword(ea + 2, memcpy_func_addr) # Below patch with the original push # push offset dword_714522A8 idc.PatchByte(ea + 6, ord(values[0])) idc.PatchByte(ea + 7, ord(values[1])) idc.PatchByte(ea + 8, ord(values[2])) idc.PatchByte(ea + 9, ord(values[3])) idc.PatchByte(ea + 10, ord(values[4])) # Add ret idc.PatchByte(ea + 11, 0xC3) # 8 pos ret # Nop left part of last instruction idc.PatchByte(ea + 12, 0x90) idc.PatchByte(ea + 13, 0x90) idc.PatchByte(ea + 13, 0x90) idc.PatchByte(ea + 14, 0x90) idc.PatchByte(ea + 15, 0x90) idc.PatchByte(ea + 16, 0x90) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "Pattern: {0}".format(pattern) print "\tPatched find_and_rename_memcpy_function: {0}".format( count_patched) print "\tNot Patched find_and_rename_memcpy_function: {0}".format( count_not_patched)
def delete_fake_calls_before_jz_jnz(): ''' .text:714517B9 74 2C jz short loc_714517E7 .text:714517BB 75 0A jnz short loc_714517C7 .text:714517BD FF 15 0C 70 48 71 call ds:LsaClose .text:71463763 75 5F jnz short near ptr dword_714637C0+4 .text:71463765 74 0A jz short near ptr dword_71463770+1 .text:71463767 FF 15 EC 71 48 71 call ds:LsaConnectUntrusted 74 ?? 75 ?? FF 15 75 ?? 74 ?? FF 15 ''' patterns = ["74 ?? 75 ?? FF 15", "75 ?? 74 ?? FF 15"] for pattern in patterns: count_patched = 0 count_not_patched = 0 ea = 0 while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea and segment_name == idc.SegName(ea): ''' 74 2C jz short loc_714517E7 75 0A jnz short loc_714517C7 FF 15 0C 70 48 71 call ds:LsaClose Result: 74 2C jz short loc_714517E7 75 0A jnz short loc_714517C7 90 90 90 90 90 90 nops ''' # Patch from pos, and delete fake calls pos = ea + 0x4 patch_loop(pos, 6, 0x90) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "Pattern: {0}".format(pattern) print "\tPatched delete_fake_calls_before_jz_jnz: {0}".format( count_patched) print "\tNot Patched delete_fake_calls_before_jz_jnz: {0}".format( count_not_patched) ''' .text:714713E4 0F 84 36 01 FE FF jz loc_71451520 .text:714713EA 75 0A jnz short near ptr loc_714713F5+1 0F ?? ?? ?? ?? ?? 75 ?? FF 15 0F ?? ?? ?? ?? ?? 74 ?? FF 15 ''' patterns = [ "0F ?? ?? ?? ?? ?? 75 ?? FF 15", "0F ?? ?? ?? ?? ?? 74 ?? FF 15" ] for pattern in patterns: count_patched = 0 count_not_patched = 0 ea = 0 while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea and segment_name == idc.SegName(ea): ''' 0F 84 36 01 FE FF jz loc_71451520 75 0A jnz short near ptr loc_714713F5+1 FF 15 00 70 48 71 call ds:EqualDomainSid Result: 0F 84 36 01 FE FF jz loc_71451520 75 0A jnz short near ptr loc_714713F5+1 90 90 90 90 90 90 nops ''' # Patch from pos, and delete fake calls pos = ea + 0x8 patch_loop(pos, 6, 0x90) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "Pattern: {0}".format(pattern) print "\tPatched delete_fake_calls_before_jz_jnz: {0}".format( count_patched) print "\tNot Patched delete_fake_calls_before_jz_jnz: {0}".format( count_not_patched) ''' .text:10021C11 0F 84 09 F9 FD FF jz loc_10001520 .text:10021C17 0F 85 03 F9 FD FF jnz loc_10001520 .text:10021C1D FF 15 00 91 03 10 call ds:CreateFileW ''' patterns = [ "0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? FF 15", "0F 85 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? FF 15" ] for pattern in patterns: count_patched = 0 count_not_patched = 0 ea = 0 while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea and segment_name == idc.SegName(ea): ''' .text:10021C11 0F 84 09 F9 FD FF jz loc_10001520 .text:10021C17 0F 85 03 F9 FD FF jnz loc_10001520 .text:10021C1D FF 15 00 91 03 10 call ds:CreateFileW Result: .text:10021C11 0F 84 09 F9 FD FF jz loc_10001520 .text:10021C17 0F 85 03 F9 FD FF jnz loc_10001520 .text:10021C1D 90 nop .text:10021C1E 90 nop .text:10021C1F 90 nop .text:10021C20 90 nop .text:10021C21 90 nop .text:10021C22 90 nop ''' # Patch from pos, and delete fake calls pos = ea + 12 patch_loop(pos, 6, 0x90) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "Pattern: {0}".format(pattern) print "\tPatched delete_fake_calls_before_jz_jnz: {0}".format( count_patched) print "\tNot Patched delete_fake_calls_before_jz_jnz: {0}".format( count_not_patched)
def loadWorkspaceFromIdb(): ''' from IDA Pro, load the currently loaded module into a vivisect workspace. currently only supports windows PE files. Returns: vivisect.Workspace: the loaded and analyzed vivisect workspace. ''' vw = vivisect.VivWorkspace() if is_x86(): vw.setMeta('Architecture', 'i386') elif is_x64(): vw.setMeta('Architecture', 'amd64') else: raise NotImplementedError('unsupported architecture') if not is_exe(): raise NotImplementedError('unsupported file format') vw.setMeta('Platform', 'windows') vw.setMeta('Format', 'pe') vw._snapInAnalysisModules() filename = idc.GetInputFile() for segstart in idautils.Segments(): segname = idc.SegName(segstart) segbuf = get_segment_data(segstart) if segbuf is None: raise RuntimeError('failed to read segment data') logger.debug('mapping section %s with %x bytes', segname, len(segbuf)) vw.addMemoryMap(segstart, envi.memory.MM_RWX, segname, segbuf) vw.addSegment(segstart, len(segbuf), segname, filename) for ea, ordinal, name in get_exports(): logger.debug('marking export %s at %x', name, ea) vw.addEntryPoint(ea) vw.addExport(ea, vivisect.const.EXP_FUNCTION, name, filename) for ea, dllname, name, ordinal in get_imports(): logger.debug('marking import %s!%s at %x', dllname, name, ea) vw.makeImport(ea, dllname, name) logger.debug('running vivisect auto-analysis') vw.analyze() for fva in get_functions(): logger.debug('marking function %s at %x', idc.GetFunctionName(fva), fva) vw.makeFunction(fva) vw.makeName(fva, idc.GetFunctionName(fva)) # can only set thunk-ness after a function is defined. for ea, dllname, name, ordinal in get_imports(): try: thunk = get_import_thunk(ea) except ValueError: pass else: logger.debug('found thunk for %s.%s at %x', dllname, name, thunk) vw.makeFunction(thunk) vw.makeFunctionThunk(thunk, '%s.%s' % (dllname, name)) return vw
def _initialize_page(self, n, new_page): if n in self._initialized: return False self._initialized.add(n) new_page_addr = n * self._page_size initialized = False if self.state is not None: self.state.scratch.push_priv(True) if self._memory_backer is None: pass project = load_project() #print "LOADING 0x%x" % new_page_addr if get_memory_type() == TEXT_SIMPROCS_FROM_LOADER or ( get_memory_type() == ONLY_SIMPROCS_FROM_LOADER and idc.SegName(new_page_addr) == project.arch.got_section_name): #yes this is weird if isinstance(self._memory_backer, cle.Clemory): # first, find the right clemory backer for addr, backer in self._memory_backer.cbackers if self.byte_width == 8 else ( (x, y) for x, _, y in self._memory_backer.stride_repr): start_backer = new_page_addr - addr if isinstance(start_backer, BV): continue if start_backer < 0 and abs( start_backer) >= self._page_size: continue if start_backer >= len(backer): continue # find permission backer associated with the address # fall back to read-write if we can't find any... flags = IdaPage.PROT_READ | IdaPage.PROT_WRITE for start, end in self._permission_map: if start <= new_page_addr < end: flags = self._permission_map[(start, end)] break snip_start = max(0, start_backer) write_start = max(new_page_addr, addr + snip_start) write_size = self._page_size - write_start % self._page_size if self.byte_width == 8: snip = _ffi.buffer(backer)[snip_start:snip_start + write_size] mo = SimMemoryObject(claripy.BVV(snip), write_start, byte_width=self.byte_width) self._apply_object_to_page(n * self._page_size, mo, page=new_page) else: for i, byte in enumerate(backer): mo = SimMemoryObject(claripy.BVV( byte, self.byte_width), write_start + i, byte_width=self.byte_width) self._apply_object_to_page(n * self._page_size, mo, page=new_page) new_page.permissions = claripy.BVV(flags, 3) initialized = True elif len(self._memory_backer) <= self._page_size: for i in self._memory_backer: if new_page_addr <= i and i <= new_page_addr + self._page_size: if isinstance(self._memory_backer[i], claripy.ast.Base): backer = self._memory_backer[i] elif isinstance(self._memory_backer[i], bytes): backer = claripy.BVV(self._memory_backer[i]) else: backer = claripy.BVV(self._memory_backer[i], self.byte_width) mo = SimMemoryObject(backer, i, byte_width=self.byte_width) self._apply_object_to_page(n * self._page_size, mo, page=new_page) initialized = True elif len(self._memory_backer) > self._page_size: for i in range(self._page_size): try: if isinstance(self._memory_backer[i], claripy.ast.Base): backer = self._memory_backer[i] elif isinstance(self._memory_backer[i], bytes): backer = claripy.BVV(self._memory_backer[i]) else: backer = claripy.BVV(self._memory_backer[i], self.byte_width) mo = SimMemoryObject(backer, new_page_addr + i, byte_width=self.byte_width) self._apply_object_to_page(n * self._page_size, mo, page=new_page) initialized = True except KeyError: pass # page from debugger try: seg = idaapi.getseg( new_page_addr) ### CHANGE TO SUPPORT OTHER DEBUGGERS if seg is not None: perms = 0 if seg.perm & idaapi.SEGPERM_EXEC: perms += IdaPage.PROT_EXEC if seg.perm & idaapi.SEGPERM_WRITE: perms += IdaPage.PROT_WRITE if seg.perm & idaapi.SEGPERM_READ: perms += IdaPage.PROT_READ new_page.permissions = claripy.BVV(perms, 3) #print "permissions setted %x %d" % (new_page_addr, perms) initialized = True setattr(new_page, "from_ida_dbg", True) except Exception as ee: import traceback traceback.print_exc() if self.state is not None: self.state.scratch.pop_priv() return initialized
def getSectionInfo(root_path): with open(root_path + 'section.csv', 'w') as f: wr = csv.writer(f) wr.writerow(['name', 'align', 'combination', 'flags', 'selector', 'type', 'r', 'w', 'x', 'size']) # segment objects for seg in idautils.Segments(): # Segment Name try: name = idc.SegName(seg) except: name = "error" # Segment Align try: align = idc.GetSegmentAttr(seg, SEGATTR_ALIGN) except: align = "error" # Segment Combination try: combination = idc.GetSegmentAttr(seg, SEGATTR_COMB) except: combination = "error" # Segment Flags try: flags = idc.GetSegmentAttr(seg, SEGATTR_FLAGS) except: flags = "error" # Segment Selector try: selector = idc.GetSegmentAttr(seg, SEGATTR_SEL) except: selector = "error" # Segment Type try: seg_type = idc.GetSegmentAttr(seg, SEGATTR_TYPE) except: seg_type = "error" # Segment Permission try: perm_r = 0 perm_w = 0 perm_x = 0 perm = idc.GetSegmentAttr(seg, SEGATTR_PERM) if perm == 1: perm_x = 1 elif perm == 2: perm_w = 1 elif perm == 4: perm_r = 1 elif perm == 3: perm_x = 1 perm_w = 1 elif perm == 6: perm_w = 1 perm_r = 1 elif perm == 5: perm_x = 1 perm_r = 1 elif perm == 7: perm_r = 1 perm_w = 1 perm_x = 1 except: perm_r = "error" perm_w = "error" perm_x = "error" # Segment Size try: size = idc.GetSegmentAttr(seg, SEGATTR_END) - idc.GetSegmentAttr(seg, SEGATTR_START) except: size = "error" wr.writerow([name, align, combination, flags, selector, seg_type, perm_r, perm_w, perm_x, size])
print "0x%x %s" % (ea, ea) #获取当前光标的地址 ea = here() print "0x%x %s" % (ea, ea) #获取当前IDB中最小地址 print hex(MinEA()) #获取当前IDB中最大地址 print hex(MaxEA()) ea = here() #获取指定地址区段名称 print idc.SegName(ea) #获取指定地址反汇编代码 print idc.GetDisasm(ea) #获取指定地址助记符 print idc.GetMnem(ea) #获取指定地址第一个操作数 print idc.GetOpnd(ea, 0) #获取指定地址第二个操作数 print idc.GetOpnd(ea, 1) #标识无效地址 print hex(idaapi.BADADDR)
def main(fileName): if fileName is None: return jsonValue = {} jsonValue["names"] = {} jsonValue["functions"] = {} jsonValue["segments"] = [] jsonValue["strings"] = {} for addr, name in idautils.Names(): jsonValue["names"][addr] = name # Record segment details for ea in idautils.Segments(): cur_seg = {} cur_seg["start"] = idc.SegStart(ea) cur_seg["end"] = idc.SegEnd(ea) cur_seg["name"] = idc.SegName(ea) seg = idaapi.getseg(ea) cur_seg["r"] = (seg.perm & idaapi.SEGPERM_READ) != 0 cur_seg["w"] = (seg.perm & idaapi.SEGPERM_WRITE) != 0 cur_seg["x"] = (seg.perm & idaapi.SEGPERM_EXEC) != 0 cur_seg["semantics"] = DefaultSectionSemantics if seg.type == idaapi.SEG_CODE: cur_seg["semantics"] = ReadOnlyCodeSectionSemantics elif seg.type == idaapi.SEG_DATA or seg.type == idaapi.SEG_BSS: if cur_seg["w"]: cur_seg["semantics"] = ReadWriteDataSectionSemantics else: cur_seg["semantics"] = ReadOnlyDataSectionSemantics # Record function details for ea in idautils.Functions(): cur_func = {} cur_func["start"] = ea cur_func["end"] = idc.GetFunctionAttr(ea, idc.FUNCATTR_END) cur_func["comment"] = linearize_comment(ea, True) cur_func["comments"] = {} for line_ea in idautils.Heads(ea, cur_func["end"]): line_comment = linearize_comment(line_ea) if line_comment is not None: cur_func["comments"][line_comment] = line_ea flags = idc.GetFunctionFlags(ea) cur_func["can_return"] = (flags & idc.FUNC_NORET) != idc.FUNC_NORET cur_func["thunk"] = False f = idaapi.get_func(ea) blocks = [] for block in idaapi.FlowChart(f): blocks.append([block.startEA, block.endEA]) # IDA treats thunks as being part of the function they are tunking to # Binary Ninja doesn't so only add the first basic block for all thunks if flags & idc.FUNC_THUNK != 0: cur_func["thunk"] = True break cur_func["basic_blocks"] = blocks jsonValue["functions"][idc.GetFunctionName(ea)] = cur_func # Record string details for string in idautils.Strings(): name = "" if string.ea in jsonValue["names"]: name = jsonValue["names"][string.ea] xrefs = list(idautils.DataRefsTo(string.ea)) if idaapi.IDA_SDK_VERSION < 700: jsonValue["strings"][string.ea] = (name, string.length, string.type, xrefs) else: jsonValue["strings"][string.ea] = (name, string.length, string.strtype, xrefs) # TODO: global variable names and types # TODO: stack local variable names and types # TODO: types and enumerations # TODO: non-function comments with open(fileName, "wb") as f: f.write(json.dumps(jsonValue, indent=4)) print("Exported idb to {}".format(fileName))
def enum_segments(): for segstart in idautils.Segments(): segend = idc.SegEnd(segstart) segname = idc.SegName(segstart) yield segstart, segend, segname
def obfuscated_jz_jnz(): ''' Resolve opaque predicates, in case that opaque predicate points to FF 25 the call will be patched: Example: .text:10001540 68 71 15 00 10 push offset byte_10001571 .text:10001545 0F 84 57 5C 03 00 jz loc_100371A2 .text:1000154B 0F 85 51 5C 03 00 jnz loc_100371A2 Pattern 68 ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 68 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? Operation -> DWORD( 9C 9F 01 00 ) - DWORD( 96 9F 01 00 ) == 6 That means that jumps to the same relative address 0x19f9C - 0x19F96 = 0x6 If that address points to FF 25 ?? ?? ?? ?? .text:100371A2 FF 25 9C 90 03 10 jmp ds:lstrlenA <---------- .text:100371A8 ----------------------------- .text:100371A8 FF 25 A0 90 03 10 jmp ds:GetModuleHandleA .text:100371AE ----------------------------- .text:100371AE FF 25 A4 90 03 10 jmp ds:LoadLibraryA .text:100371B4 ----------------------------- .text:100371B4 .text:100371B4 .text:100371B4 .text:100371B4 FF 25 A8 90 03 10 jmp ds:GetLastError .text:100371BA ----------------------------- .text:100371BA FF 25 AC 90 03 10 jmp ds:lstrcpyA Patch with CALL PUSH RET formula .text:10001540 90 nop ; Return addr - 0x10001571 .text:10001541 90 nop .text:10001542 90 nop .text:10001543 90 nop .text:10001544 90 nop .text:10001545 FF 15 9C 90 03 10 call ds:lstrlenA .text:1000154B 68 71 15 00 10 push 10001571h .text:10001550 C3 retn ''' count_patched = 0 count_not_patched = 0 ea = 0 while ea != BADADDR: ea = idc.FindBinary( ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, "68 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ??") if ea and segment_name == idc.SegName(ea): idc.MakeComm( ea, "Return addr - {0}".format(hex(Dword(ea + 0x1)).split("L")[0])) jz_pos = ea + 0x5 jz_value = Dword(jz_pos + 0x2) jnz_pos = jz_pos + 0x6 jnz_value = Dword(jnz_pos + 0x2) # Check same jmp addr if jz_value - jnz_value == 0x6: pos_jmp = jz_pos + jz_value + 0x6 # If the jmp points to a FF 25 instruction (absolute jmp) if Word(pos_jmp) == 0x25FF: ''' .text:1000153F 55 push ebp .text:10001540 68 71 15 00 10 push offset byte_10001571 .text:10001545 0F 84 57 5C 03 00 jz loc_100371A2 .text:1000154B 0F 85 51 5C 03 00 jnz loc_100371A2 .text:10001551 56 push esi ''' # Patch the conditional jmp, copying the absolute jmp into it idc.PatchWord(jz_pos, Word(pos_jmp)) idc.PatchDword(jz_pos + 0x2, Dword(pos_jmp + 0x2)) # At this point the FF 25 instruction have been copied into the conditional jmp position # Ex: FF 25 3C 70 48 71 jmp ds:CryptGenRandom # But the absolute jmp will be patched with a call FF 15 # FF 15 call idc.PatchByte(jz_pos, 0xFF) idc.PatchByte(jz_pos + 0x1, 0x15) # Copy the push instruction where conditonal jmp was idc.PatchWord(jnz_pos, Word(ea)) idc.PatchWord(jnz_pos + 0x1, Word(ea + 0x1)) idc.PatchWord(jnz_pos + 0x3, Word(ea + 0x3)) idc.PatchByte(jnz_pos + 0x5, 0xC3) # Nop the first 5 bytes. patch_loop(ea, 0x5, 0x90) ''' .text:10001540 90 nop ; Return addr - 0x10001571 .text:10001541 90 nop .text:10001542 90 nop .text:10001543 90 nop .text:10001544 90 nop .text:10001545 FF 15 9C 90 03 10 call ds:lstrlenA .text:1000154B 68 71 15 00 10 push 10001571h .text:10001550 C3 retn ''' idc.MakeCode(ea) else: ''' .text:10021C0C 68 27 1C 02 10 push offset loc_10021C27 .text:10021C11 0F 84 09 F9 FD FF jz loc_10001520 .text:10021C17 0F 85 03 F9 FD FF jnz loc_10001520 .text:10021C0C 90 nop .text:10021C0D 90 nop .text:10021C0E 90 nop .text:10021C0F 90 nop .text:10021C10 90 nop .text:10021C11 90 nop .text:10021C12 90 nop .text:10021C13 90 nop .text:10021C14 90 nop .text:10021C15 90 nop .text:10021C16 90 nop .text:10021C17 E9 04 F9 FD FF jmp loc_10001520 .text:10021C1C 90 nop <- last byte of old jnz instr ''' # The first 11 bytes to nop for i in range(0, 11): idc.PatchByte(ea + i, 0x90) # Add 1 to the address, because the size of the conditional jmp is 6, and the size of the unconditional jmp is 5 addr_to_jmp = Dword(jnz_pos + 0x2) + 0x1 # Patch first jz_value with unconditional jmp idc.PatchByte(jnz_pos, 0xE9) # Set the relative address idc.PatchDword(jnz_pos + 0x1, addr_to_jmp) # Last byte of the jnz to NOP (0x90) idc.PatchByte(jnz_pos + 0x5, 0x90) idc.MakeCode(ea) ''' .text:10021C0C 90 nop .text:10021C0D 90 nop .text:10021C0E 90 nop .text:10021C0F 90 nop .text:10021C10 90 nop .text:10021C11 90 nop .text:10021C12 90 nop .text:10021C13 90 nop .text:10021C14 90 nop .text:10021C15 90 nop .text:10021C16 90 nop .text:10021C17 E9 04 F9 FD FF jmp loc_10001520 .text:10021C1C 90 nop <- last byte of old jnz instr ''' count_patched += 1 else: count_not_patched += 1 print "\tPatched obfuscated_jz_jnz: {0}".format(count_patched) print "\tNot Patched obfuscated_jz_jnz: {0}".format(count_not_patched)
def is_in_got(self, addr): """ Checks whether or not an address is in the GOT section. """ return idc.SegName(addr) == '.got.plt'
def patch_jmp_eax(): ''' This type of push + jmp eax can be found in the maze code .text:71452297 68 A8 22 45 71 push offset dword_714522A8 .text:7145229C FF E0 jmp eax With this patter the script is able to find this type of code: 68 ?? ?? ?? ?? FF E0 ''' count_patched = 0 count_not_patched = 0 ea = 0 while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, "68 ?? ?? ?? ?? FF E0") if ea and segment_name == idc.SegName(ea): ''' 68 A8 22 45 71 push offset dword_714522A8 FF E0 jmp eax in some cases we found this after the jmp fake api calls FF 15 08 90 03 10 call ds:LsaAddAccountRights ''' # push + jmp = 7 bytes # call Fake api call = 6 bytes # Store the byte values in "values" list values = GetManyBytes(ea, 7 + 6) ''' FF D0 call eax 68 A8 22 45 71 push offset dword_714522A8 C3 ret ''' # Patch with call eax idc.PatchByte(ea, 0xFF) idc.PatchByte(ea + 0x1, 0xD0) # Patch push idc.PatchByte(ea + 0x2, ord(values[0])) idc.PatchByte(ea + 0x3, ord(values[1])) idc.PatchByte(ea + 0x4, ord(values[2])) idc.PatchByte(ea + 0x5, ord(values[3])) idc.PatchByte(ea + 0x6, ord(values[4])) # Patch ret idc.PatchByte(ea + 0x7, 0xC3) ''' If the script found in the position 7 and 8 FF 15 that means that it has found a fake api call 57 push edi A1 D4 90 03 10 mov eax, ds:lstrcatW 89 C6 mov esi, eax 68 C9 28 00 10 push offset loc_100028C9 FF E0 jmp eax FF 15 08 90 03 10 call ds:LsaAddAccountRights 91 xchg eax, ecx 23 00 and eax, [eax] ''' # NOP the fake call if ord(values[7]) == 0xFF and ord(values[8]) == 0x15: for i in range(0, 5): idc.PatchByte(ea + 0x8 + i, 0x90) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "\tPatched patch_jmp_eax: {0}".format(count_patched) print "\tNot Patched patch_jmp_eax: {0}".format(count_not_patched)
def search_vftable_list_gcc(): for seg in idautils.Segments(): if idc.SegName(seg) in vftable_section_names: find_vftable_gcc(seg)
def obfuscated_jz_jnz_2(): patterns = [ "68 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 75 ??", "68 ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 74 ??" ] for pattern in patterns: ea = 0 count_patched = 0 count_not_patched = 0 ''' It is assumed, after analysis, that the following conditions are met. - The first jz or jnz instruction is the one that contains the final address. - The second jz or jnz instruction contains the intermediate address, this intermediate address contains another conditional jmp instruction that points to the same final address as the first conditional jmp instruction. .text:1002AFE3 68 1D B0 02 10 push offset loc_1002B01D .text:1002AFE8 0F 84 12 6E 00 00 jz loc_10031E00 <- jmp to Final Addr .text:1002AFEE 75 04 jnz short loc_1002AFF4 <- jmp to Intermediate Addr .text:1002AFF0 E2 1B loop loc_1002B00D .text:1002AFF2 00 db 0 .text:1002AFF3 00 db 0 .text:1002AFF4 .text:1002AFF4 loc_1002AFF4: .text:1002AFF4 0F 85 06 6E 00 00 jnz loc_10031E00 <- jmp to Final Addr .text:1002AFFA 74 04 jz short loc_1002B000 <- Junk Code .text:1002AFFC 13 1A adc ebx, [edx] ''' while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea and segment_name == idc.SegName(ea): # First push push_addr = ea # First conditional jmp (jz or jnz) first_j = ea + 0x5 # Second conditional jmp (jz or jnz) second_j = first_j + 0x6 # pos contains the last position pos = 0xFFFFFFFF & first_j + Dword(first_j + 0x2) + 0x6 # pos_2 contains the intermediate position before jmp to the final pos pos_2 = 0xFFFFFFFF & first_j + 0x6 + 0x2 + Byte(first_j + 0x6 + 0x1) # First opcode intermediate instruction value_pos_2 = Byte(pos_2) # Final address pos_3 = pos_2 + Dword(pos_2 + 0x2) + 0x6 # If the OPCODE value of the intermediate jmp instruction begins with 0x0F must be patched if value_pos_2 == 0xF: value_pos_final = Word(pos_3) # If the final address first two bytes values are FF 25 (absolute jmp) # Patch with call push ret formula if value_pos_final == 0x25FF: ''' .text:1002D25D 68 9C D2 02 10 push offset dword_1002D29C .text:1002D262 0F 84 A6 9F 00 00 jz loc_1003720E <------ jmp ds:GetTickCount .text:1002D268 75 04 jnz short loc_1002D26E .text:1002D26A 86 01 xchg al, [ecx] .text:1002D26C 00 db 0 .text:1002D26D 00 db 0 .text:1002D26E .text:1002D26E loc_1002D26E: .text:1002D26E 0F 85 9A 9F 00 00 jnz loc_1003720E <------ jmp ds:GetTickCount .text:1002D274 74 0A jz short loc_1002D280 .text:1002D276 FF 15 10 92 03 10 call ds:EnumChildWindows The first conditional jmp and the intermediate points to WinAPI function: .text:1003720E FF 25 E4 90 03 10 jmp ds:GetTickCount Patched: .text:1002D25D 90 nop .text:1002D25E 90 nop .text:1002D25F 90 nop .text:1002D260 90 nop .text:1002D261 90 nop .text:1002D262 FF 15 E4 90 03 10 call ds:GetTickCount .text:1002D268 68 9C D2 02 10 push 1002D29Ch .text:1002D26D C3 retn .text:1002D26E 90 nop .text:1002D26F 90 nop .text:1002D270 90 nop .text:1002D271 90 nop .text:1002D272 90 nop .text:1002D273 90 nop .text:1002D274 90 nop .text:1002D275 90 nop .text:1002D276 90 nop .text:1002D277 90 nop .text:1002D278 90 nop .text:1002D279 90 nop .text:1002D27A 90 nop .text:1002D27B 90 nop ''' idc.PatchByte(first_j, 0xFF) idc.PatchByte(first_j + 0x1, 0x15) idc.PatchDword(first_j + 0x2, Dword(pos_3 + 0x2)) # patch 0x90 patch_loop(first_j + 0x6, 2, 0x90) # Patch from pos_2 8 bytes NOPS patch_loop(pos_2, 8, 0x90) # Copy push addr idc.PatchByte(second_j, Byte(push_addr)) idc.PatchByte(second_j + 0x1, Byte(push_addr + 0x1)) idc.PatchByte(second_j + 0x2, Byte(push_addr + 0x2)) idc.PatchByte(second_j + 0x3, Byte(push_addr + 0x3)) idc.PatchByte(second_j + 0x4, Byte(push_addr + 0x4)) # Patch with ret idc.PatchByte(second_j + 0x5, 0xC3) # Patch push_addr patch_loop(push_addr, 5, 0x90) idc.MakeCode(ea) count_patched += 1 #If the final address first two bytes values are not FF 25 (jmp) else: ''' .text:1002C85C 68 99 C8 02 10 push offset loc_1002C899 .text:1002C861 0F 84 B9 4C FD FF jz loc_10001520 .text:1002C867 75 04 jnz short loc_1002C86D .text:1002C869 FD std .text:1002C86A 0A 00 or al, [eax] .text:1002C86C 00 db 0 .text:1002C86D .text:1002C86D loc_1002C86D: .text:1002C86D 0F 85 AD 4C FD FF jnz loc_10001520 .text:1002C873 74 04 jz short loc_1002C879 .text:1002C875 CF iret Patched: .text:1002C85C 68 99 C8 02 10 push offset loc_1002C899 .text:1002C861 E9 BA 4C FD FF jmp loc_10001520 .text:1002C861 ; --------------------------------------------------------------------------- .text:1002C866 FF db 0FFh ; ÿ .text:1002C867 75 04 jnz short loc_1002C86D .text:1002C869 FD std .text:1002C86A 0A 00 or al, [eax] .text:1002C86A ; --------------------------------------------------------------------------- .text:1002C86C 00 db 0 .text:1002C86D ; --------------------------------------------------------------------------- .text:1002C86D .text:1002C86D loc_1002C86D: ; CODE XREF: .text:1002C867↑j .text:1002C86D 90 nop .text:1002C86E 90 nop .text:1002C86F 90 nop .text:1002C870 90 nop .text:1002C871 90 nop .text:1002C872 90 nop .text:1002C873 90 nop .text:1002C874 90 nop ''' idc.PatchByte(first_j, 0xE9) idc.PatchDword(first_j + 0x1, Dword(first_j + 0x2) + 0x1) # Patch from pos_2 8 bytes NOPS patch_loop(pos_2, 8, 0x90) idc.MakeCode(ea) count_patched += 1 print "Pattern: {0}".format(pattern) print "\tPatched obfuscated_jz_jnz_2: {0}".format(count_patched) print "\tNot Patched obfuscated_jz_jnz_2: {0}".format( count_not_patched)
def main(): global functions program_ver_arch_opt = idc.ARGV[2] program = idc.ARGV[3] PROGRAM = idc.ARGV[1] parent_dir = os.path.join(config.Normalize_INST_DIR, config.Normalize_type, PROGRAM, program_ver_arch_opt) print(parent_dir) if not os.path.exists(parent_dir): os.makedirs(parent_dir) norm_inst_dir = parent_dir + os.sep + 'function_norm_inst/' if not os.path.exists(norm_inst_dir): os.makedirs(norm_inst_dir) textStartEA = 0 textEndEA = 0 res = [] for seg in idautils.Segments(): if (idc.SegName(seg)[:5] == ".text"): textStartEA = idc.SegStart(seg) textEndEA = idc.SegEnd(seg) print(str(hex(textStartEA))) print(str(hex(textEndEA))) print('\n') for func in idautils.Functions(textStartEA, textEndEA): flags = idc.GetFunctionFlags(func) if flags & idc.FUNC_LIB: print hex(func), "FUNC_LIB", idc.GetFunctionName(func) continue func_name = idc.GetFunctionName(func) # replace '.' in program to '_' program = '_'.join(program.split('.')) cur_function_name = program + '_' + func_name if len(cur_function_name) > 130: cur_function_name = cur_function_name[:130] if ':' in cur_function_name: cur_function_name = '_'.join(cur_function_name.split(':')) if cur_function_name.lower() in functions: pass else: functions.append(cur_function_name.lower()) norm_inst_file = norm_inst_dir + os.sep + str( cur_function_name) + "_norm.csv" norm_fp = open(norm_inst_file, 'w') tmp = [] allblock = idaapi.FlowChart(idaapi.get_func(func)) for block in allblock: one_block_norms = [] curEA = block.startEA while curEA <= block.endEA: #print config.Normalize_type cur_norm_inst = eval(config.Normalize_type)(curEA) tmp.append(cur_norm_inst) one_block_norms.append(cur_norm_inst) curEA = idc.NextHead(curEA, block.endEA) one_block_norm_str = '\t'.join(one_block_norms) one_block_norm_str = str(hex( block.startEA)) + '\t' + one_block_norm_str + '\n' norm_fp.write(one_block_norm_str) norm_fp.close() res.append(tmp) file_name = os.path.join(parent_dir, program + '.csv')
import idc import idautils from idaapi import * f = open('kernelStrings.pl','w') #why does this say False? s = idautils.Strings(False) s.setup(strtypes=Strings.STR_C) for i, v in enumerate(s): if v is None: f.write("Failed to retrieve string index %d" % i) else: #f.write("%x: len=%d type=%d index=%d-> '%s'\n" % (v.ea, v.length, v.type, i, str(v))) seg=idc.SegName(v.ea) #I'm probably replacing more things than I should, but prolog is getting confused by random backslashes. currentString=str(v).replace("\n","").replace('"',"'").replace("\\","") #f.write("%s\n" % str(seg)) f.write("kernelString(segment(\"%s\"),stringFromProgram(\"%s\")).\n" % (str(seg), currentString)) f.close() idc.Exit(0)
''' File name: dump_sections.py ''' import idautils import idc import idaapi SECTION_NAME = '.e1t1_fw' FOUND_FLAG = False for sec in idautils.Segments(): sec_name = idc.SegName(sec) if sec_name == SECTION_NAME: FOUND_FLAG = True start = idc.GetSegmentAttr(sec, idc.SEGATTR_START) end = idc.GetSegmentAttr(sec, idc.SEGATTR_END) with open(SECTION_NAME+".bin", 'wb') as f: while start < end: if not idc.hasValue(idc.GetFlags(start)): continue data = idc.Byte(start) f.write(chr(data)) start += 1 if not FOUND_FLAG: print "[+] Could not found %s section " %(SECTION_NAME) else:
def getSegName(self, segName): for seg in idautils.Segments(): if idc.SegName(seg) == segName: return idc.SegStart(seg), idc.SegEnd( seg), idc.SegEnd(seg) - idc.SegStart(seg)
def _collect_vtables(metaclass_info): """Use OSMetaClass information to search for virtual method tables.""" # Build a mapping from OSMetaClass instances to virtual method tables. metaclass_to_vtable_builder = _OneToOneMapFactory() vtable_lengths = {} # Define a callback for when we find a vtable. def found_vtable(metaclass, vtable, length): # Add our vtable length. vtable_lengths[vtable] = length # If our classname has a defined vtable symbol and that symbol's address isn't this vtable, # don't add the link. classname = metaclass_info[metaclass].classname proper_vtable_symbol = symbol.vtable_symbol_for_class(classname) proper_vtable_symbol_ea = idau.get_name_ea(proper_vtable_symbol) if proper_vtable_symbol_ea not in (idc.BADADDR, vtable): return # If our vtable has a symbol and it doesn't match the metaclass, skip adding a link. vtable_symbol = idau.get_ea_name(vtable, user=True) if vtable_symbol: vtable_classname = symbol.vtable_symbol_get_class(vtable_symbol) if vtable_classname != classname: _log( 2, 'Declining association between metaclass {:x} ({}) and vtable {:x} ({})', metaclass, classname, vtable, vtable_classname) return # Add a link if they are in the same kext. if segment.kernelcache_kext(metaclass) == segment.kernelcache_kext( vtable): metaclass_to_vtable_builder.add_link(metaclass, vtable) # Process all the segments with found_vtable(). for ea in idautils.Segments(): segname = idc.SegName(ea) if not segname.endswith('__DATA_CONST.__const'): continue _log(2, 'Processing segment {}', segname) _process_const_section_for_vtables(ea, metaclass_info, found_vtable) # If a metaclass has multiple vtables, that's really weird, unless the metaclass is # OSMetaClass's metaclass. In that case all OSMetaClass subclasses will have their vtables # refer back to OSMetaClass's metaclass. def bad_metaclass(metaclass, vtables): metaclass_name = metaclass_info[metaclass].classname if metaclass_name != 'OSMetaClass': vtinfo = ['{:#x}'.format(vt) for vt in vtables] _log(0, 'Metaclass {:#x} ({}) has multiple vtables: {}', metaclass, metaclass_name, ', '.join(vtinfo)) # If a vtable has multiple metaclasses, that's really weird. def bad_vtable(vtable, metaclasses): mcinfo = [ '{:#x} ({})'.format(mc, metaclass_info[mc].classname) for mc in metaclasses ] _log(0, 'Vtable {:#x} has multiple metaclasses: {}', vtable, ', '.join(mcinfo)) metaclass_to_vtable = metaclass_to_vtable_builder.build( bad_metaclass, bad_vtable) # The resulting mapping may have fewer metaclasses than metaclass_info. class_info = dict() for metaclass, classinfo in metaclass_info.items(): # Add the vtable and its length, which we didn't have earlier. If the current class doesn't # have a vtable, take it from the superclass (recursing if necessary). metaclass_with_vtable = metaclass while metaclass_with_vtable: vtable = metaclass_to_vtable.get(metaclass_with_vtable, None) if vtable: classinfo.vtable = vtable classinfo.vtable_length = vtable_lengths[vtable] break classinfo_with_vtable = metaclass_info.get(metaclass_with_vtable, None) if not classinfo_with_vtable: break metaclass_with_vtable = classinfo_with_vtable.meta_superclass # Set the superclass field and add the current classinfo to the superclass's children. This # is safe since this is the last filtering operation. superclass = metaclass_info.get(classinfo.meta_superclass, None) if superclass: classinfo.superclass = metaclass_info[classinfo.meta_superclass] classinfo.superclass.subclasses.add(classinfo) # Add the classinfo to the final dictionary. class_info[classinfo.classname] = classinfo return class_info, vtable_lengths
def objc_msgsend_xref(self, call_ea, objc_self, objc_selector, create_xref=True): ''' This function will create a code xref to an objc method call_ea : location of call/jmp objc_msgsend (regardless of direct/indirect) objc_self: ea where RDI is set to static value (or that we find it's from a previous call or the RDI of the current function) objc_selector: ea where RSI is set to static value This ignores the RDI register, which is the `self` argument to objc_msgsend() id objc_msgSend(id self, SEL op, ...); So far, this seems to be fine as far as the cross-references are concerned. ''' # get instruction mnemonic at address - I guess to check and make sure # it's mov rsi, blah instruction = idc.GetDisasm(objc_selector) if self.debugflag: print ">>> objc_msgsend_xref 0x%08x %s" % (objc_selector, instruction) # get outbound references in the appropriate segment # implicit assumption is there is exacltly one target_selref = None for _ref in idautils.DataRefsFrom(objc_selector): if idc.SegName(_ref) == "__objc_selrefs": target_selref = _ref if not target_selref: return False # get outbound references in the appropriate segment # implicit assumption is there is exacltly one target_methname = None for _ref in idautils.DataRefsFrom(target_selref): if idc.SegName(_ref) == "__objc_methname": target_methname = _ref if not target_methname: return False # get inbound references # __objc_const # must be a __objc2_meth # I hope this method is correct to find __objc2_meth structs # BUG: when the binary has mutiple objc methods by the same name, this logic fails # Track RDI register. have to figure out what instance/class is referenced objc2_meth_struct_id = ida_struct.get_struc_id("__objc2_meth") meth_struct_found = False target_method = None for _ref in idautils.DataRefsTo(target_methname): # multiple may match # we care about the __obj2_meth struct found in references if idc.SegName(_ref) == "__objc_const": # check the outbound references for _meth_ref in idautils.DataRefsFrom(_ref): if _meth_ref == objc2_meth_struct_id: meth_struct_found = True if meth_struct_found: # only do this once # TODO: check against RDI here to make sure it's the proper class # meth_struct_found = False for _meth_ref in idautils.DataRefsFrom(_ref): # assumption made on function always being in text segment if idc.SegName(_meth_ref) == "__text": # save the method implementation -- this is the function ptr if self.debugflag: print "0x%08x checking for the proper method -- %s" % ( _meth_ref, idc.get_name( idc.get_func_attr( _meth_ref, idc.FUNCATTR_START))) target_method = _meth_ref if not target_method: return False # After dereferencing across the IDB file, we finally have a target function. # In other words, if there isn't a method **in this binary** no xref is made (IDA only loads one binary?) # that is referenced from the mov rsi, <selector> instruction if self.debugflag: print "Found target method 0x%08x" % target_method if create_xref: idc.AddCodeXref(objc_selector, target_method, idc.fl_CF) return True
def FindChildNode2(func, bbl_heads, parent, cur, stack, num_call, write_head): ''' Handle function which was instrumented by ratio. Find 1st layer instrumented child. Recusion when encounter jump instruction. Stop when found a child node. @func function address @bbl_heads all bbl heads of function. @parent parent node address @cur the address to start the search. This vuale is typically the starting address of BBL. @stack stack of search address, avoid recusion loops @num_call indicates how many call is contained in one edge. @write_head list ''' global g_f global g_off_set_random global g_size_ins_block global g_off_random # num_call = 0 # don't count call in parent bbl num_mem = 0 # don't count call mem function in parent bbl # current pos must in func end = idc.GetFunctionAttr(func, idc.FUNCATTR_END) if cur < func: return # avoid recusion loops if cur in stack: return stack.append(cur) ea = cur while ea < end and idaapi.BADADDR != ea: #idaapi.BADADDR #print(hex(ea), idc.GetDisasm(ea)) flag = idc.GetFlags(ea) if idc.isData(flag): ea += idc.ItemSize(ea) continue if not idc.isCode(flag): ea += 1 continue # code # found a child node, stop if ea < end - g_size_ins_block and IsInstrumentIns(ea): parent_id = idc.Dword( parent + g_off_random) #(int)(idc.GetOpnd(parent, 1).strip('h'), 16) child_id = idc.Dword( ea + g_off_set_random + g_off_random) #(int)(idc.GetOpnd(ea+0x13, 1).strip('h'), 16) #if len(g_dict_func_edge): if 1 == write_head[0]: # found a child node, stop g_f.write(("%d %d\n") % (num_call, num_mem)) write_head[0] = 0 return if parent != ea + g_off_set_random: #g_f.write(("%d %d %d %d %d %d %d\n") % ( parent, ea+0x13, parent_id, child_id, (parent_id >> 1) ^ child_id, num_call, num_mem )) g_f.write(("%d %d %d %d %d ") % (parent, ea + g_off_set_random, parent_id, child_id, (parent_id >> 1) ^ child_id)) write_head[0] = 1 #return if ea in bbl_heads: pass mnem = idc.GetMnem(ea) #asm = idc.GetDisasm(ea) if mnem[:3] == 'ret': #asm[:3] == 'ret': if write_head[0]: # found a child node, stop g_f.write(("%d %d\n") % (num_call, num_mem)) write_head[0] = 0 return # jmp jz jnz ja ...... elif 'j' == mnem[ 0]: # and 'm' != mnem[1]: jmp dst addr has been instumented. if write_head[0]: # found a child node, stop g_f.write(("%d %d\n") % (num_call, num_mem)) write_head[0] = 0 return for xref in idautils.XrefsFrom(ea): # idautils.ida_xref.XREF_ALL) if xref.type == 18 or xref.type == 19: # 18 :'Code_Far_Jump', 19 : 'Code_Near_Jump', please see XrefTypeName FindChildNode2(func, bbl_heads, parent, xref.to, stack, num_call, write_head) elif xref.type == 20: # 20 : 'Code_User' print('******************************************') print('Code_User', hex(parent), hex(ea), idc.GetDisasm(ea)) elif xref.type == 21: # 21 : 'Ordinary_Flow' FindChildNode2(func, bbl_heads, parent, xref.to, stack, num_call, write_head) return # call. count call ins. elif mnem == 'call': #asm.startswith('call'): to = 0 for to in idautils.CodeRefsFrom(ea, False): fun_name = idc.GetFunctionName(to).lower() if IsSanFunc(fun_name): continue if fun_name.find('alloc') >= 0 or fun_name.find('free') >= 0 \ or fun_name.find('create') >= 0 or fun_name.find('delete') >= 0 \ or fun_name.find('destroy') >= 0: num_mem += 1 break # only count instrumented function if idc.SegName(ea) == idc.SegName(to): if not IsSanFunc(fun_name) and IsInstrumentIns(to): #if len(g_dict_func_edge): # num_call += g_dict_func_edge[to] #else: num_call += 1 ea += idc.ItemSize(ea) continue else: ea += idc.ItemSize(ea) #ea += idc.DecodeInstruction(ea) #ea = idc.NextNotTail(ea) if write_head[0]: g_f.write(("%d %d\n") % (num_call, num_mem)) write_head[0] = 0 return
def get_segm_name(seg_ea): if idaapi.IDA_SDK_VERSION <= 699: retval = idc.SegName(seg_ea) else: retval = idc.get_segm_name(seg_ea) return retval
def get_destructor_segment(): """Returns the start address of the global destructor section""" for seg_ea in idautils.Segments(): seg_name = idc.SegName(seg_ea).lower() if seg_name in [".fini_array", ".dtor"]: return seg_ea
def get_name(self): return idc.SegName(self.addr)