def decomp(src, dst, length): # print("decomp 0x%X 0x%X (0x%x)"%(src, dst, length)) end = dst + length while True: meta = ida_bytes.get_byte(src) src += 1 l = meta & 7 if l == 0: l = ida_bytes.get_byte(src) src += 1 l2 = meta >> 4 if l2 == 0: l2 = ida_bytes.get_byte(src) src += 1 # print("meta: 0x%x l: 0x%X l2: 0x%x"%(meta,l,l2)) # copy l byte memcpy(src, dst, l - 1) src += l - 1 dst += l - 1 if meta & 8: off = ida_bytes.get_byte(src) src += 1 for i in range(l2 + 2): memcpy(dst - off, dst, 1) dst += 1 else: memclr(dst, l2) dst += l2 if dst >= end: assert dst == end, "Decompress failed" # print('decomp end %0x %0x'%(dst, end)) break
def readPASSTR(ea): out = "" len = ida_bytes.get_byte(ea) ea += 1 for i in range(len): out += chr(ida_bytes.get_byte(ea + i)) return out
def apply(self): """ Apply patches to found opaque branch :return: None """ for src, dst in self._flow_patches_map.items(): ir_block: IRBlock = self._ir_cfg.get_block(src) asm_instr = ir_block.assignblks[ir_block.dst_linenb].instr if asm_instr.name not in conditional_branch: log(f"Unsupported asm pattern at {hex(src)}", code='!') continue patch_addr = asm_instr.offset opcode1 = ida_bytes.get_byte(patch_addr) # Fast and Furious if opcode1 == 0x0F: ida_bytes.patch_bytes( patch_addr, b"\xe9" + pack("<I", (dst - (patch_addr + 5)) & (2**32 - 1))) elif ((opcode1 & 0xe0) == 0xe0) or ((opcode1 & 0x70) == 0x70): ida_bytes.patch_byte(patch_addr, 0xeb) ida_bytes.patch_byte(patch_addr + 1, (dst - (patch_addr + 2)) & 0xFF) else: log(f"Unknown first part of opcode at {hex(patch_addr)}", code='!') continue log(f"Apply patch JCC -> JMP at {hex(patch_addr)}")
def __add_bytes(self, bytes_ea, bytes_count): sginature_str = '' for i in range(0, bytes_count): sginature_str = sginature_str + '%02X ' % ida_bytes.get_byte( bytes_ea + i) return sginature_str
def on_mb_click(self, event, addr, size, mouse_offs): button = event.button() if button == Qt.MiddleButton: mouse = addr + mouse_offs c = get_byte(mouse) head, name, size = self._get_item_info(mouse) funcname = self._get_func_name(mouse) self.ann = [(mouse, qRgb(c, 0xFF, self.hl_color), "Address: %X" % (mouse), qRgb(c, 0xFF, self.hl_color)), (None, None, " Item: %s" % (name), qRgb(c, 0xFF, self.hl_color)), (None, None, " Head: %X" % (head), qRgb(c, 0xFF, self.hl_color)), (None, None, " Size: %d" % (size), qRgb(c, 0xFF, self.hl_color))] if funcname: self.ann.append((None, None, " Function: %s" % (funcname), qRgb(c, 0xFF, self.hl_color))) self.last_sel = (head, size) elif button == Qt.MiddleButton: pass elif button == Qt.RightButton: self.switch ^= 1 msg('Highlighting %s\n' % self.mode[self.switch])
def on_mb_click(self, event, addr, size, mouse_offs): button = event.button() if button == Qt.MiddleButton: self._set_xor_key() elif button == Qt.RightButton: key = get_byte(addr + mouse_offs) self._set_xor_key(key) return
def get_bytes_str(start_ea, end_ea): size = end_ea - start_ea bytes = [] for ea in range(start_ea, end_ea): b = '{:02x}'.format(ida_bytes.get_byte(ea)) bytes.append(b) return ' '.join(bytes)
def GetName(Ad): name=bytes() while(1): ascii=ida_bytes.get_byte(Ad) if(ascii==0): break Ad=Ad+1 name=name+chr(ascii) return name
def calc_item_size(self, ea, maxsize): if ida_struct.is_member_id(ea): return 1 # get the opcode and see if it has an imm n = 5 if (ida_bytes.get_byte(ea) & 3) == 0 else 1 # string too big? if n > maxsize: return 0 # ok, accept return n
def main(): print('[*] start debfuscation') for s in get_code_segments(): print('[*] try to deobfuscate {} section'.format( ida_segment.get_segm_name(s))) if s.use32(): junk_patterns = junk_patterns_x86 elif s.use64(): junk_patterns = junk_patterns_x64 else: print('[!] unsupported arch') print('[*] replace junk code to nop') for pattern, pattern_len in junk_patterns: addr_from = idc.find_binary(s.start_ea, ida_search.SEARCH_DOWN, pattern) while addr_from != idaapi.BADADDR and addr_from < s.end_ea: ida_bytes.patch_bytes(addr_from, '\x90' * pattern_len) addr_from = idc.find_binary(addr_from + pattern_len, ida_search.SEARCH_DOWN, pattern) print('[*] hide nop code') addr_from = ida_search.find_text( s.start_ea, 0, 0, 'nop', ida_search.SEARCH_CASE | ida_search.SEARCH_DOWN) while addr_from != idaapi.BADADDR and addr_from < s.end_ea: func_offset = idc.get_func_off_str(addr_from) if type(func_offset) == str and func_offset.find('+') == -1: addr_from = ida_search.find_text( idc.next_head(addr_from), 0, 0, 'nop', ida_search.SEARCH_CASE | ida_search.SEARCH_DOWN) else: i = 0 while True: if ida_bytes.get_byte(addr_from + i) == 0x90: i += 1 else: break if i >= 3: idc.add_hidden_range(addr_from, addr_from + i, 'nop', None, None, 0xFFFFFFFF) print("%08X" % addr_from) addr_from = ida_search.find_text( idc.next_head(addr_from + i), 0, 0, 'nop', ida_search.SEARCH_CASE | ida_search.SEARCH_DOWN) #print('[*] renanlyze') #idc.del_items(s.start_ea, size=s.size()) #time.sleep(1) #idc.plan_and_wait(s.start_ea, s.end_ea) print('[*] done')
def callback2(ea): print("Found ref to cmd string at 0x%08X" % ea) ref=ida_bytes.get_dword(ea)&0xFFFF0000 for i in range(0,0x1000*4,0x10): value=ida_bytes.get_dword(ea+i) if value&0xFFFF0000 != ref: valbyte=ida_bytes.get_byte(ea+i) if valbyte==ord('$') or valbyte==ord('!') or valbyte==ord('+') or valbyte==ord('^') or valbyte==ord('*'): return ea+i break logger.debug("cmd_ref:%x" % (ea+i)) references.append(ea+i) return 0
def calc_item_size(self, ea, maxsize): # Custom data types may be used in structure definitions. If this case # ea is a member id. Check for this situation and return 1 if ida_struct.is_member_id(ea): return 1 # get the length byte n = ida_bytes.get_byte(ea) # string too big? if n > maxsize: return 0 # ok, accept the string return n + 1
def get_string(ea, length=idc.BADADDR): end_ea = ea + length ret = [] while ea < end_ea: # break if current ea is already assigned if not idc.is_loaded(ea): break byte = ida_bytes.get_byte(ea) if byte == 0: # NULL terminate break ret.append(byte) ea += 1 return bytes(ret)
def main(): print("[*] Start patching to XOR encoded blocks") ea = ida_kernwin.ask_addr(BADADDR, "What address is encoded block by xor?") xor_key = ida_kernwin.ask_long(0x00, "Waht is key for xor?(0-255)") valid_check(ea, xor_key) print hex(ea) print hex(xor_key) while True: b = ida_bytes.get_byte(ea) if b == 0: break ida_bytes.patch_byte(ea, b ^ xor_key) ea += 1 print("[*] Finished patching to XOR encoded blocks")
def on_get_annotations(self, address, size, mouse_offs): item_ea = get_item_head(address + mouse_offs) cursor_ea = address + mouse_offs name = get_name(item_ea) if len(name): name = "(%s)" % name else: name = "" ann = [ (item_ea, self.red[0], "Item: %X" % (item_ea), self.colormap[-1]), (None, None, " Size: %d %s" % (get_item_size(get_item_head(cursor_ea)), name), self.colormap[-3]), (cursor_ea, self.colormap[-1], "Cursor: %X" % (cursor_ea), self.colormap[-1]), (None, None, " %s" % generate_disasm_line( cursor_ea, GENDSM_FORCE_CODE | GENDSM_REMOVE_TAGS), self.colormap[-3]), (None, None, " Value: %02X" % get_byte(cursor_ea), self.colormap[-3]), ] return ann
def decomp2(src, dst, length): # print("decomp 0x%X 0x%X (0x%x)"%(src, dst, length)) meta = ida_bytes.get_byte(src) src += 1 end = dst + length while True: l = meta & 3 if l == 0: l = ida_bytes.get_byte(src) src += 1 l2 = meta >> 4 if l2 == 0: l2 = ida_bytes.get_byte(src) src += 1 # print("meta: 0x%x l: 0x%X l2: 0x%x"%(meta,l,l2)) # copy l byte memcpy(src, dst, l - 1) src += l - 1 dst += l - 1 if l2: off = ida_bytes.get_byte(src) src += 1 meta_val = meta & 0xC src_ptr = dst - off if meta_val == 12: meta_val = ida_bytes.get_byte(src) src += 1 src_ptr -= 256 * meta_val else: src_ptr -= 64 * meta_val l2 += 2 memcpy(src_ptr, dst, l2) dst += l2 meta = ida_bytes.get_byte(src) src += 1 if dst >= end: assert dst == end, "Decompress failed" # print('decomp end %0x %0x'%(dst, end)) break
secondOp = SecondOperand(popcntSize) inst = DecodeInstruction(popcntAddr) if inst.Op2.dtype != idaapi.dt_qword: if inst.Op2.dtype == idaapi.dt_dword: secondOp.is64bit = False else: raise RuntimeError("Parsing error: unsupported data type") if inst.Op2.type == o_reg: secondOp.isPtr = False secondOp.base = inst.Op2.reg elif inst.Op2.type == o_phrase or inst.Op2.type == o_displ: if inst.Op2.specflag1 and inst.Op2.phrase != 4: extendedBase = 0 extendedIndex = 0 rex = ida_bytes.get_byte(popcntAddr + 1) if rex != 0x0f: if rex >> 4 == 4: extendedBase = rex & 1 extendedIndex = (rex >> 1) & 1 else: raise RuntimeError("Parsing error: bad rex prefix") sib = inst.Op2.specflag2 #print(rex) #print extendedBase, extendedIndex #print(sib) base = (sib & 7) | (extendedBase << 3) if base == 12 and not extendedIndex: secondOp.base = base else:
def get_byte(self, offset): """Read byte from IDB""" self.ret = ida_bytes.get_byte(offset) return self.ret
def on_mb_click(self, button, addr, mouse_offs): if button == Qt.MiddleButton: self._set_xor_key() elif button == Qt.RightButton: key = get_byte(addr + mouse_offs) self._set_xor_key(key)
def get_int8(ea): return struct.unpack("b", struct.pack("B", ida_bytes.get_byte(ea)))[0]
def get_tooltip(self, addr, mouse_offs): return "%X:\nCursor 0x%02X\nKey: 0x%02X" % ( addr + mouse_offs, get_byte(addr + mouse_offs), self.key)
def get_decryption_data(xref_address): encrypted_buffer_address = "" little_endian_iv = 0 buffer_size = 0 push_counter = 0 instructions = reversed(list(Heads(xref_address - LIMIT, xref_address))) for instr_address in instructions: # If the values have not been found if not encrypted_buffer_address or little_endian_iv == 0 or buffer_size == 0: # If the instruction is a "mov" plus the register in the destination operand is "ecx" # and the buffer address has not been found yet, we get this value if GetMnem(instr_address) == "mov" and GetOpnd(instr_address, 0) == "ecx" and not encrypted_buffer_address: # If the operand of the source instruction is an offset, it is the address if "offset" in GetOpnd(instr_address, 1): encrypted_buffer_address = get_value_from_instruction(instr_address) # If not, we look for the value in a recirsive way else: encrypted_buffer_address = get_value_from_register(instr_address, GetOpnd(instr_address, 1)) # If the instruction is a "pop" we decrement the "push" counter elif GetMnem(instr_address) == "pop": push_counter = push_counter - 1 # If the instruction is a "push" we increment the "push" counter and gets the values of the IV and the buffer_size elif GetMnem(instr_address) == "push": push_counter = push_counter + 1 # If the push counter is equal 0 (it must be a pop prior this instruction), it is the size of the buffer if push_counter == 0 and buffer_size == 0: operand = GetOpnd(instr_address, 0) # If the operand of the instruction is a digit or an hex value, it is the size (if this value has not been found yet) if check_if_hex(operand) or operand.isdigit: buffer_size = get_value_from_instruction(instr_address) # If not, we look for the value in a recirsive way else: buffer_size = get_value_from_register(instr_address, GetOpnd(instr_address, 0)) # If the push counter is equal 1, it is the IV (if this value has not been found yet) elif push_counter == 1 and little_endian_iv == 0: operand = GetOpnd(instr_address, 0) # If the operand of the instruction is a an hex value, it is the IV if check_if_hex(operand): little_endian_iv = get_value_from_instruction(instr_address) # If not, we look for the value in a recirsive way else: little_endian_iv = get_value_from_register(instr_address, GetOpnd(instr_address, 0)) elif GetMnem(instr_address) == "lea" and GetOpnd(instr_address, 0) == "edx" and buffer_size == 0: buffer_size = ida_bytes.get_byte(instr_address + 2) + 1 # If the instruction is a "mov" plus the register in the destination operand is "edx" # and the buffer size has not been found yet, we get this value elif GetMnem(instr_address) == "mov" and GetOpnd(instr_address, 0) == "edx" and buffer_size == 0: operand = GetOpnd(instr_address, 0) # If the operand of the instruction is a digit or an hex value, it is the size if check_if_hex(operand) or operand.isdigit: buffer_size = get_value_from_instruction(instr_address) # If not, we look for the value in a recirsive way else: buffer_size = get_value_from_register(instr_address, GetOpnd(instr_address, 1)) # If the buffer size could not be obtained, we set it to 1, since it is real value in the binary elif GetMnem(instr_address) == "inc" and GetOpnd(instr_address, 0) == "edx" and buffer_size == 0: buffer_size = 1 # If all values has been found, we stop the search else: break decryption_data = { "encrypted_buffer_address": str(encrypted_buffer_address), "little_endian_iv": str(little_endian_iv), "buffer_size": str(buffer_size) } return decryption_data
def makePASSTR(ea): len = ida_bytes.get_byte(ea) ida_bytes.create_strlit(ea, len + 1, ida_bytes.STRTYPE_PASCAL) return len
def load_file(f, neflags, format): f.seek(0) ida_idp.set_processor_type("metapc", ida_idp.SETPROC_LOADER) MGROUPStart = 0 magic = f.read(2) if magic == MZ_HEADER_MAGIC: f.seek(0x22) MGROUPStart = DW(f) * 16 f.seek(MGROUPStart) magic = f.read(2) headerSize = DW(f) segmentDataAlignment = DW(f) nextExeOff = DD(f) SegDataOff = DD(f) f.file2base(MGROUPStart, 0, SegDataOff, True) ida_segment.add_segm(0, 0, 0x50, "HEADER", "MODULE") f.seek(MGROUPStart + 2) headerSize = rnDW(f, "headerSize", MGROUPStart) segmentDataAlignment = rnDW(f, "segmentDataAlignment", MGROUPStart) nextExeOff = rnDD(f, "nextExeOff", MGROUPStart) SegDataOff = rnDD(f, "SegDataOff", MGROUPStart) ResDataOff = rnDD(f, "ResDataOff", MGROUPStart) flags = rnDW(f, "flags", MGROUPStart) version = rnDB(f, "version", MGROUPStart) revision = rnDB(f, "revision", MGROUPStart) AutoDataSegNo = rnDW(f, "AutoDataSegNo", MGROUPStart) HeapSize = rnDW(f, "HeapSize", MGROUPStart) StackSize = rnDW(f, "StackSize", MGROUPStart) StartProc = rnDD(f, "StartProc", MGROUPStart) LoadProc = rnDD(f, "LoadProc", MGROUPStart) FreeProc = rnDD(f, "FreeProc", MGROUPStart) nSegments = rnDW(f, "nSegments", MGROUPStart) pSegTable = rnDW(f, "pSegTable", MGROUPStart) cbResTab = rnDW(f, "cbResTab", MGROUPStart) pResTab = rnDW(f, "pResTab", MGROUPStart) cbEntTab = rnDW(f, "cbEntTab", MGROUPStart) pEntTab = rnDW(f, "pEntTab", MGROUPStart) cbNamTab = rnDW(f, "cbNamTab", MGROUPStart) pNamTab = rnDW(f, "pNamTab", MGROUPStart) cbStrTab = rnDW(f, "cbStrTab", MGROUPStart) pStrTab = rnDW(f, "pStrTab", MGROUPStart) cbNRNamTab = rnDW(f, "cbNRNamTab", MGROUPStart) pNRNamTab = rnDW(f, "pNRNamTab", MGROUPStart) ida_segment.add_segm(0, pSegTable, pSegTable + (nSegments * SEG_STRUCT_SIZE), "SEGTABLE", "MODULE") ida_segment.add_segm(0, pResTab, pResTab + cbResTab, "RESOURCES", "MODULE") ida_segment.add_segm(0, pEntTab, pEntTab + cbEntTab, "ENTTABLE", "MODULE") ida_segment.add_segm(0, pNamTab, pNamTab + cbNamTab, "ENTNAME", "MODULE") ida_segment.add_segm(0, pStrTab, pStrTab + cbStrTab, "IMPORTS", "MODULE") ida_segment.add_segm(0, pNRNamTab, pNRNamTab + cbNRNamTab, "NRENTNAME", "MODULE") #parse segtable segentsid = defSEGENT() base = SegDataOff // 16 importCount = 0 for i in range(nSegments): segEntStart = pSegTable + i * SEG_STRUCT_SIZE ida_bytes.create_struct(segEntStart, SEG_STRUCT_SIZE, segentsid) segStart = ida_bytes.get_word(segEntStart + 2) segLen = ida_bytes.get_word(segEntStart + 4) segImports = ida_bytes.get_word(segEntStart + 6) importCount += segImports f.file2base(MGROUPStart + SegDataOff + segStart * 16, SegDataOff + segStart * 16, SegDataOff + (segStart + segLen) * 16, True) segBase = (base + segStart) * 16 #segmentDef = ida_segment.segment_t() #segmentDef.start_ea = segBase #segmentDef.end_ea = (base+segStart+segLen)*16 #ida_segment.set_selector() print(base + segStart) ida_segment.add_segm(base + segStart, segBase, (base + segStart + segLen) * 16, "", "", 0) sel = ida_segment.find_selector(base + segStart) seg = ida_segment.getseg(segBase) ida_segment.set_segm_addressing(seg, 0) segtable[i] = seg segimportstable[i] = segImports if i + 1 == AutoDataSegNo: ida_segment.set_segm_name(seg, "DATA", 0) ida_segment.set_segm_class(seg, "DATA", 0) dataSel = sel else: ida_segment.set_segm_name(seg, "TEXT", 0) ida_segment.set_segm_class(seg, "CODE", 0) if AutoDataSegNo == 0: dataSel = sel ida_segregs.set_default_dataseg(dataSel) #parse enttable pENT = pEntTab currord = 1 while pENT < pEntTab + cbEntTab: bundleCount = ida_bytes.get_byte(pENT) bundleFlags = ida_bytes.get_byte(pENT + 1) if bundleCount == 0 and bundleFlags == 0: break pENT += 2 for i in range(bundleCount): if bundleFlags == 0xFF: ordFlags = ida_bytes.get_byte(pENT) if ordFlags & 0x80: toexport.append(currord) segNo = ida_bytes.get_byte(pENT + 3) segOff = ida_bytes.get_word(pENT + 4) enttable[currord] = (segtable[segNo - 1].start_ea // 16, segOff) pENT += 6 else: ordFlags = ida_bytes.get_byte(pENT) if ordFlags & 0x80: toexport.append(currord) segOff = ida_bytes.get_word(pENT + 1) enttable[currord] = (segtable[bundleFlags - 1].start_ea // 16, segOff) pENT += 3 currord += 1 modulename = readPASSTR(pNamTab) make_entry(StartProc, modulename + "_start") make_entry(LoadProc, modulename + "_load") make_entry(FreeProc, modulename + "_free") #export named ordinals namedordtable = loadExportsF(f) for i in toexport: if i in namedordtable: name = namedordtable[i] else: name = "Ordinal" + str(i) (base, off) = enttable[i] addr = base * 16 + off ida_entry.add_entry(i, addr, name, 1) #process imports ida_segment.add_segm(0xF000, 0xF0000, 0xF0000 + importCount * 2, "IMPORTS", "XTRN", 0) import_ea = 0xF0000 for seg in segtable: segend = segtable[seg].end_ea f.seek(MGROUPStart + segend) for i in range(segimportstable[seg]): count = DB(f) mode = DB(f) relocStart = DW(f) module = DW(f) proc = DW(f) if (module == 0xFFFF): (base, off) = enttable[proc] else: modulestr = readPASSTR(pStrTab + module) if (proc & 0x8000) != 0: # read by ord ordinal = proc & 0x7FFF procname = modulestr + "_Ordinal" + str(ordinal) if not modulestr in importedmodules: if os.path.isfile(modulestr + ".EXE"): importedmodules[modulestr] = loadExports( modulestr + ".EXE") else: filename = ida_kernwin.ask_file( 0, modulestr + ".EXE", "Select file to name exports") if filename is not None and os.path.isfile( filename): importedmodules[modulestr] = loadExports( filename) else: importedmodules[modulestr] = None if modulestr in importedmodules and ( importedmodules[modulestr] is not None ) and ordinal in importedmodules[modulestr]: procname = importedmodules[modulestr][ordinal] else: procname = readPASSTR(pStrTab + proc) ida_bytes.create_data(import_ea, ida_bytes.FF_WORD, 2, ida_idaapi.BADADDR) ida_name.force_name(import_ea, procname) ida_bytes.set_cmt(import_ea, "Imported from " + modulestr, 1) base = 0xF000 off = import_ea - 0xF0000 import_ea += 2 for xx in range(count): next = ida_bytes.get_word(segtable[seg].start_ea + relocStart) if mode == 0x20: ida_bytes.put_word(segtable[seg].start_ea + relocStart + 2, base) ida_bytes.put_word(segtable[seg].start_ea + relocStart, off) elif mode == 0x10: ida_bytes.put_word(segtable[seg].start_ea + relocStart, off) elif mode == 0x0: ida_bytes.put_word(segtable[seg].start_ea + relocStart, base) relocStart = next #print "import %d: seg %d mode %s count %d relocStart %s module %s proc %s" % (i, seg, hex(mode), count, hex(relocStart), modulestr, hex(proc)) return 1
def on_get_tooltip(self, addr, size, mouse_offs): return "0x%02X" % get_byte(addr + mouse_offs)
def MemCopy( dest, src, length ) : for i in xrange(0, length): #if (i < 20): # print "set byte at %#x to %#x" % (dest+i, idc.Byte(src+i)) ida_bytes.patch_byte(dest+i,ida_bytes.get_byte(src+i))
def range_xor(start_addr, end_addr, xor_num): for the_addr in range(start_addr, end_addr + 1): the_byte = get_byte(the_addr) patch_byte(the_addr, the_byte ^ xor_num)