def main(): tid = get_guid_tid() for type_name, type_prefix, filepath in GUID_LIST: print('[*] scanning {}'.format(type_name)) fp = open(filepath, 'r') for line in fp.readlines(): line = line.strip() if line == "": continue guid, guid_name = line.split(' ') guid_name = type_prefix + guid_name binary_pattern = make_binary_pattern(guid) ea = 0 while True: ea = idc.find_binary( ea, ida_search.SEARCH_DOWN | ida_search.SEARCH_NEXT | ida_search.SEARCH_NOSHOW, binary_pattern) if ea == idaapi.BADADDR: break idc.del_items(ea, 16, 0) ida_bytes.create_struct(ea, ida_struct.get_struc_size(tid), tid) if idc.set_name(ea, guid_name, ida_name.SN_NOWARN) != 1: for i in range(0, 100): if idc.set_name(ea, guid_name + "_" + str(i), ida_name.SN_NOWARN) == 1: break else: print("[!] 0x{:X}: failed to apply {}".format( ea, guid_name)) print("[*] 0x{:X}: {}".format(ea, guid_name)) print("[*] finished")
def make_array(ea, size): if ea != idc.BADADDR and ea != 0: flags = idc.get_full_flags(ea) if not idc.isByte(flags) or idc.get_item_size(ea) != 1: idc.del_items(ea, idc.DOUNK_SIMPLE, 1) ida_bytes.create_data(ea, ida_bytes.FF_BYTE, 1, ida_idaapi.BADADDR) idc.make_array(ea, size)
def make_array(ea, size): if ea != idc.BADADDR and ea != 0: flags = idc.get_full_flags(ea) if not idc.isByte(flags) or idc.get_item_size(ea) != 1: idc.del_items(ea, idc.DOUNK_SIMPLE, 1) idc.MakeByte(ea) idc.MakeArray(ea, size)
def type(self, value): if value is None or value == "": raise ValueError("value: %s" % value) if SetType(self.__ea, "%s %s" % (value, self.name)) == 0: del_items(self.__ea, 0) if SetType(self.__ea, "%s %s" % (value, self.name)) == 0: print('SetType(0x{:x}, "{} {}") has failed'.format(self.__ea, value, self.name))
def create_func_by_prefix(func_name, prefix, force=False): addrs = [] start_addr = 0 func_addr = 0 while func_addr != idc.BADADDR: func_addr = ida_search.find_binary(start_addr, idc.BADADDR, prefix, 16, idc.SEARCH_DOWN) if func_addr == idc.BADADDR: break # already existing function but it is not the right prefix addr = idc.get_func_attr(func_addr, idc.FUNCATTR_START) if addr != idc.BADADDR and func_addr != addr: if not force: start_addr = func_addr + 4 continue idc.del_func(addr) idc.del_items(func_addr) # add_func is not applied to the existing function idc.add_func(func_addr) func_name = set_entry_name(func_addr, func_name) print("%s: 0x%x" % (func_name, func_addr)) addrs.append(func_addr) start_addr = func_addr + 4 return addrs
def make_array(ea, size): if ea != idc.BADADDR and ea != 0: flags = idc.get_full_flags(ea) if not idc.isByte(flags) or idc.get_item_size(ea) != 1: idc.del_items(ea, idc.DOUNK_SIMPLE, 1) idc.MakeByte(ea) idc.MakeArray(ea, size)
def resolve_symbols(self, dll): tmpdir = tempfile.gettempdir().replace('\\', '/') + '/' # File who will first contain ordinals then symbols tmpfile = tmpdir + "dotNIETsymbols.txt" tmplog = tmpdir + "dotNIETlogs.txt" # Destination path for a copy of SharedLibrary.dll tmpdll = tmpdir + os.path.basename(dll) # Path to the idat binary, needed to parse a remote idb idat = idc.idadir().replace('\\','/') + '/' + 'idat' + \ ['64' if utils.is_x64(dll) else ''][0] if os.getenv("windir") is not None: idat += '.exe' # script called along with idat in order to parse SharedLibrary.idb parsing_script_path = '"' + os.path.dirname(os.path.realpath(__file__)).replace('\\','/')\ + '/dotNIET/resolve.py"' # we first copy SharedLibrary.dll to tmp to be sure we are in a # writable location shutil.copyfile(dll, tmpdll) # we have to use temporary files because of the ida headless stuff... # pretty dirty ordinals = [] with open(tmpfile, 'w') as f: for i in range(self.nb_symbols): ordinals.append((idaapi.get_qword(self.ordinals + i * 8) & ~0x8000000000000000) * 4) f.write(",".join([str(x) for x in ordinals])) # be prepared to wait as this will load the binary + pdb idaapi.msg("Starting parsing of %d symbols...\n" % self.nb_symbols) subprocess.run([idat, "-L" + tmplog, "-c", "-A", "-S" + \ parsing_script_path + " " + tmpfile, tmpdll], creationflags=subprocess.CREATE_NO_WINDOW, check=True) # we read back tmpfile which now contains symbols symbols = [] with open(tmpfile, 'r') as f: symbolsArray = f.read() symbols = symbolsArray.split(',') # we first have to undef structure as it is an array and ida will # refuse to set names within it idc.del_items(self.ordinals) idc.set_cmt(self.ordinals - 8, "Custom imports from SharedLibrary.dll", 0) # we then apply the symbols at this very same location (start of .idata) for i in range(self.nb_symbols): idc.set_name(self.ordinals + i * 8, symbols[i], SN_NOCHECK | SN_FORCE | SN_NODUMMY) # finally we remove our temp files os.remove(tmpfile) os.remove(tmpdll) # ida generate a db os.remove(tmpdll + '.i64') # if everything went smoothly, we should not need this anymore os.remove(tmplog)
def check_address(address): # Checks if given address contains virtual table. Returns True if more than 2 function pointers found # Also if table's addresses point to code in executable section, than tries to make functions at that addresses if helper.is_code_ea(address): return False if not idaapi.get_name(address): return False functions_count = 0 while True: func_address = helper.get_ptr(address) # print "[INFO] Address 0x{0:08X}".format(func_address) if helper.is_code_ea(func_address) or helper.is_imported_ea( func_address): functions_count += 1 address += const.EA_SIZE else: segment = idaapi.getseg(func_address) if segment and segment.perm & idaapi.SEGPERM_EXEC: idc.del_items(func_address, 1, idaapi.DELIT_SIMPLE) if idc.add_func(func_address): functions_count += 1 address += const.EA_SIZE continue break idaapi.auto_wait() return functions_count
def unkTillName(ea): d = Data.Data(ea) while True: size = d.getSize() idc.del_items(d.ea, d.getSize()) d = Data.Data(d.ea + size) if d.getName(): break return d.ea
def assign_struct_to_address(address, struct_name): idc.apply_type(address, get_Tinfo_from_name(struct_name)) struct_id = idaapi.get_struc_id(struct_name) if struct_id != 0xffffffffffffffff: struct_size = idaapi.get_struc_size(struct_id) for i in range(struct_size): idc.del_items(address + i, 0) return idaapi.create_struct(address, struct_size, struct_id) return False
def read_string(ea): s = idc.get_strlit_contents(ea, -1, idc.ASCSTR_C) if s: slen = len(s) + 1 idc.del_items(ea, idc.DOUNK_SIMPLE, slen) idaapi.make_ascii_string(ea, slen, idc.ASCSTR_C) return s, ea + slen else: return s, ea
def read_string(ea): s = idc.get_strlit_contents(ea, -1, idc.ASCSTR_C) if s: slen = len(s)+1 idc.del_items(ea, idc.DOUNK_SIMPLE, slen) idaapi.make_ascii_string(ea, slen, idc.ASCSTR_C) return s, ea + slen else: return s, ea
def define(self): """ Defines the string in the IDB. """ try: idc.del_items(self.start_ea, idc.DELIT_SIMPLE, len(self.decoded_data)) idaapi.create_strlit(self.start_ea, len(self.decoded_data), self.string_type) except Exception as e: logger.warning('Unable to define string at 0x{:0X}: {}'.format(self.start_ea, e))
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 delShiftedContent(ea): d = Data.Data(ea) content = d.getContent() if type(content) == list or not d.getXRefsFrom( )[1] or content < 0x8000000 or not d.isPointer(content): return False dContent = Data.Data(content) if content == dContent.ea or dContent.isCode(): return False if not idc.del_items(dContent.ea, dContent.getSize()): for i in range(dContent.ea, dContent.ea + dContent.getSize()): idc.del_items(i, 1) return True
def forceItemOp(ea, op, *args, **kwargs): """ This forces a change to an item, like MakeWord, in the database :param ea: linear address of item :param op: the operation to apply. Expected to return a success status :param args: arguments to op :param kwargs: kwargs to op :return: success of op """ success = op(*args, **kwargs) if not success: idc.del_items(ea) success = op(*args, **kwargs) return success
def delete_and_make_function(ea, func_size, func_name, is_thumb): if ea == 0x8001030: raise Exception('target found!') print('{:X} <{}>: make function'.format(ea, func_name)) # delete items and make sure that the area is thumb or arm for item_ea in next_item_ea(ea, func_size): idc.del_items(item_ea) idc.SetRegEx(item_ea, 'T', is_thumb, idc.SR_user) idc.MakeFunction(ea) # check that the function was created import IDAItems.Function f = IDAItems.Function.Function(ea) if f.getSize(withPool=True) != func_size: raise Exception('Function {:07X} could not be successfully created'.format(ea))
def registerUncompFile(ea, force=True): # type: (int) -> bool d = Data.Data(ea) compPtr = d.getContent() if not idc.is_dword(d.getFlags()) or type(compPtr) == list: if not force: return False print('[%07X] -> dword' % (ea)) forceItemOp(ea, idc.create_dword, ea) d = Data.Data(ea) compPtr = d.getContent() # compressed pointers have the 31th bit set if not compPtr & (1 << 31): return False compPtr = compPtr - (1 << 31) # make its content an array, and set a name for it, and a size compData = Data.Data(compPtr) if compData.ea != compPtr: idc.del_items(compData.ea) compData = Data.Data(compPtr) compSize = getLZ77CompressedSize(compPtr) # size must have been identified if compSize == -1: return False if compSize % 4 != 0: compSize += 4 - (compSize % 4) # must be word aligned if compData.getSize() != compSize: if not idc.del_items(compPtr, compSize): for i in range(compPtr, compPtr + compSize): idc.del_items(i, 1) idc.make_array(compPtr, compSize) if not compData.getName(): compData.setName('comp_%07X' % compData.ea) idc.op_man(ea, 0, '%s + 1<<31' % compData.getName()) # now register the compressed data as its own file filename = 'data/compressed/%s.lz77' % compData.getName() print('[%07X] addFile %s' % (ea, filename)) dis = Terminal.DisTerminal() dis.addFile(filename, compPtr, compPtr + compSize) return True
def removeRedCode(start_ea, end_ea): """ unconditionally removes all red code within a specified region :param start_ea: start of the region :param end_ea: end of the region :return: """ srchNext = next redStart_ea = redEnd_ea = srchNext.red(start_ea, end_ea, ui=False) while redEnd_ea < end_ea: d = Data.Data(redEnd_ea) while d.isCode() and not Function.isFunction(d.ea): redEnd_ea += 2 d = Data.Data(redEnd_ea) # change to bytes print("%07X: del red code (%07X, %07X)" % (redStart_ea, redStart_ea, redEnd_ea)) idc.del_items(redStart_ea, 0, redEnd_ea - redStart_ea) redStart_ea = redEnd_ea = srchNext.red(redEnd_ea, end_ea, ui=False)
def removeFakeRedCode(start_ea, end_ea): """ Removes instances of code recognized by IDA to be code, but are unlikely not to be by making them bytes. :param start_ea: start of the range to fix :param end_ea: end of the range to fix :return: """ srchNext = next redStart_ea, redEnd_ea = srchNext.fakered(start_ea, end_ea, ui=False, hexOut=False) while redStart_ea < end_ea: # change to bytes print("%07X: del fake red code (%07X, %07X)" % (redStart_ea, redStart_ea, redEnd_ea)) idc.del_items(redStart_ea, 0, redEnd_ea - redStart_ea) redStart_ea, redEnd_ea = srchNext.fakered(redEnd_ea, end_ea, ui=False, hexOut=False)
def expandUnkArrays(start_ea, end_ea, verbose=True): """ Finds all named byte_xxx and dword_xxx arrays, and turns them to unknowns. If an array is unnamed, and it's a byte array, it's also turned into unknowns. :param start_ea: start of the range :param end_ea: end of the range :param verbose: if True, print all changes :return: status of the expansion """ d = Data.Data(start_ea) while d.ea < end_ea: if (not idc.isAlign(d.getFlags()) and ( # known dummy array (d.getName() and (d.getName().startswith('byte_') or d.getName().startswith('dword_'))) # byte/dword array or (not d.getName() and type(d.getContent()) == list and (d.getSize() / len(d.getContent()) == 1 or d.getSize() / len(d.getContent()) == 4)))): if verbose: print('%07X: delete unk arr' % d.ea) idc.del_items(d.ea, d.getSize()) d = Data.Data(d.ea + d.getSize())
def arr2dword(pointerRange): d = Data.Data(pointerRange[0]) outputStatus = True while (d.ea < pointerRange[1]): content = d.getContent() # case: byte array that's 4 elements. Likely a word if type(content) == list and len(content) == 4 and (d.getSize() / len(content) == 1): # transform to dword status = idc.del_items(d.ea) status = status and idc.MakeDword(d.ea) outputStatus = outputStatus and status if status: print('[OK] %07X: u8[4] -> u32 %07X' % (d.ea, d.getContent())) else: print('[FAIL] %07X: u8[4] -> u32' % d.ea) # advance 4 bytes d = Data.Data(d.ea + 4) else: d = Data.Data(d.ea + d.getSize()) return outputStatus
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 reset_db(): idc.del_items(min_ea, size=max_ea - min_ea) idc.delete_all_segments() clear_names() ida_auto.auto_wait()
def delete_items(ea, size): for item_ea in next_item_ea(ea, size): idc.del_items(item_ea)
def DoDescentParser(self, Prologue): ''' @brief Walk the function leveraging a recursive descent parser. @detail Starting with a prologue walk each instruction until the associated epilogue is reached. For functions with multiple epilogues, iterate over each one. As each instruction is traversed, do the following three things: - Undefine the instruction - Mark the instruction as code - Check to see if the instruction is already a member of another function If an instruction is a member of another function, undefine that function and place it in a queue. At the end of traversing each function, a new function is going to be created with the new prologue and the new epilogue. In addition, the undefined function queue is going to be iterated over and each function will be redefined. This should clean up messy function much thanks to the author of "Practical Malware Analysis" for the break down of the algorithm in Chapter 8. ''' # # jmps = [eval("idaapi."+name) for name in dir(idaapi) if "NN_j" in name] # jcc_terminators = [ 'jnz', 'jz', 'jo', 'jno', 'js', 'jns', 'je', 'jne', 'jb', 'jnae', 'jc', 'jnb', 'jae', 'jnc', 'jbe', 'jna', 'ja', 'jnbe', 'jl', 'jnge', 'jge', 'jnl', 'jle', 'jng', 'jg', 'jnle', 'jp', 'jpe', 'jnp', 'jpo', 'jcxz', 'jecxz' ] #print EndAddresses while len(self.deferred_targets) > 0: curr_insn_ea = self.deferred_targets.pop() if curr_insn_ea in self.instructions_walked: # # skip instructions that were already walked # continue #for target in self.deferred_targets: # print "deferred target: %08x" % target while curr_insn_ea not in Prologue.possible_epilogues: # # walk only to a known epilogue # #print "Current EA: %08x" % (curr_insn_ea) self.instructions_walked.append(curr_insn_ea) # # Verify current instruction information # curr_insn = ida_ua.insn_t() decode_result = ida_ua.decode_insn(curr_insn, curr_insn_ea) if decode_result < 1: # # break if instruction invalid # break represented_insn_dism = idc.generate_disasm_line( curr_insn_ea, 0) curr_insn_dism = idc.generate_disasm_line(curr_insn_ea, 1) if curr_insn_dism != represented_insn_dism: # # If the item shown at this address in IDA does not match # what should be shown (due to obfuscation), fix it # #print "Instructions don't match: %08x" % (curr_insn_ea) idc.del_items(curr_insn_ea, 1) #idc.plan_and_wait(curr_insn_ea, curr_insn_ea+curr_insn.size) idc.create_insn(curr_insn_ea) #idc.plan_and_wait(curr_insn_ea, curr_insn_ea+curr_insn.size) curr_func_name = idc.get_func_name(curr_insn_ea) if curr_func_name: # # check if in function, undefine function, add to list to redefine later # #print "Part of another function: %08x" % (curr_insn_ea) curr_func_ea = idc.get_name_ea_simple(curr_func_name) func_end_ea = idc.find_func_end(curr_func_ea) idc.del_func(curr_func_ea) for curr_function in self.wrong_functions: if curr_function not in curr_function: self.wrong_functions.append( [curr_func_ea, func_end_ea]) if curr_insn_dism.startswith(tuple(jcc_terminators)): # # JCC conditionals, recursion control case # #print "Adding jcc target: %08x" % (curr_insn_ea) jmp_target_ea = self.GetInstuctionTargetAddress(curr_insn) if jmp_target_ea not in self.deferred_targets: self.deferred_targets.append(jmp_target_ea) curr_insn_ea = curr_insn_ea + curr_insn.size elif curr_insn_dism.startswith("jmp"): jmp_target_ea = self.GetInstuctionTargetAddress(curr_insn) #print "Adding jump target: %08x" % (curr_insn_ea) if jmp_target_ea not in self.deferred_targets: self.deferred_targets.append(jmp_target_ea) break elif curr_insn_dism.startswith("retn"): break else: curr_insn_ea = curr_insn_ea + curr_insn.size if curr_insn_ea in Prologue.possible_epilogues: Prologue.connected_epilogues.append(curr_insn_ea) continue
def cmd_undefine(self, a): return str(idc.del_items(int(a, 0), 1))
def __call__(self): idc.del_items(self.ea)
def delRange(start_ea, end_ea): status = True for ea in range(start_ea, end_ea): idc.del_items(ea, 1) return status