def disassemble(self): """ Dump .text, .rodata, .data, .eh_frame, .got to file """ print colored('1: DISASSEMBLE', 'green') ret = os.system(config.objdump + ' -Dr -j .text ' + self.file + ' > ' + self.file + '.temp') self.checkret(ret, self.file + '.temp') if not ELF_utils.elf_arm(): if ELF_utils.elf_32(): pic_process.picprocess32(self.file) else: extern_symbol_process.globalvar(self.file) pic_process.picprocess64(self.file) ret = os.system( config.objdump + " -s -j .rodata " + self.file + " | grep \"^ \" | cut -d \" \" -f3,4,5,6 > rodata.info") self.checkret(ret, 'rodata.info') ret = os.system(config.objdump + " -s -j .data " + self.file + " | grep \"^ \" | cut -d \" \" -f3,4,5,6 > data.info") self.checkret(ret, 'data.info') os.system(config.objdump + " -s -j .eh_frame " + self.file + " | grep \"^ \" | cut -d \" \" -f3,4,5,6 > eh_frame.info") if not ELF_utils.elf_arm(): os.system( config.objdump + " -s -j .eh_frame_hdr " + self.file + " | grep \"^ \" | cut -d \" \" -f3,4,5,6 > eh_frame_hdr.info") os.system(config.objdump + " -s -j .got " + self.file + " | grep \"^ \" | cut -d \" \" -f3,4,5,6 > got.info")
def traverse32(self, l, startaddr): """ Traverse data section, find pointers using 32bit alignment and substitute them with labels :param l: .byte declaration list :param startaddr: section start address """ i = 0 holei = 0 while holei < len(self.exclude) and startaddr > self.exclude[holei][1]: holei += 1 while i < len(l) - 3: val = int(''.join(map(lambda e: e[1][8:10], reversed(l[i:i + 4]))), 16) s = self.check_sec(val) if s is not None: if self.assumption_two: self.in_jmptable = False elif not ELF_utils.elf_arm() or self.checkifprobd2dARM(val): if s.sec_name == '.plt' and val in self.plt_symbols: l[i] = (l[i][0], '.long ' + self.plt_symbols[val]) l[i + 1:i + 4] = [('', '')] * 3 else: self.data_labels.insert(0, (s.sec_name, val)) l[i] = (l[i][0], '.long S_0x%X' % val) l[i + 1:i + 4] = [('', '')] * 3 else: if ELF_utils.elf_arm(): val = val & (-2) if self.check_text(val): c = bbn_byloc( val, self.begin_addrs) if self.assumption_three else True if c or (not c and self.check_jmptable(l[i][0], val)): if c and self.check_jmptable_1(l[i][0]): self.in_jmptable = True self.cur_func_name = self.fn_byloc(val) else: self.in_jmptable = False self.text_labels.insert(0, val) l[i] = (l[i][0], '.long S_0x%X' % val) l[i + 1:i + 4] = [('', '')] * 3 else: self.in_jmptable = False else: self.in_jmptable = False if holei < len(self.exclude) and self.exclude[holei][ 0] <= startaddr + i + 4 <= self.exclude[holei][1]: i = self.exclude[holei][1] - startaddr i += 4 - i % 4 holei += 1 else: i += 4
def data_output(self): """ Save data sections to files """ self.process(self.locations) self.process(self.data_labels, True) self.gotexternals() if len(self.rodata_list) != 0: l, s = self.rodata_list[0] self.rodata_list[0] = ('s_dummy:\n' + l, s) dataalign = '\n.align 16' if ELF_utils.elf_64() else ( '\n.align 2' if ELF_utils.elf_arm() else '') self.rodata_list.insert(0, ('.section .rodata' + dataalign, '')) self.got_list.insert(0, ('.section .got', '')) self.data_list.insert(0, ('.section .data' + dataalign, '')) self.bss_list.insert(0, ('.section .bss' + dataalign, '')) def createout(l): l = filter(lambda e: len(e[0]) + len(e[1]) > 0, l) return '\n'.join(map(lambda e: e[0] + e[1], l)) with open('final_data.s', 'a') as f: f.write(createout(self.rodata_list) + '\n') f.write('\n' + createout(self.data_list) + '\n') f.write('\n' + createout(self.got_list) + '\n') f.write('\n' + createout(self.bss_list) + '\n')
def add_func_label(self, ufuncs, instrs): """ Insert function labels :param ufuncs: function list :param instrs: instruction list :return: instruction list with function declarations """ i = 0 j = 0 while True: if i == len(ufuncs) or j == len(instrs): break hf = ufuncs[i] hi = instrs[j] iloc = get_loc(hi) if hf.func_begin_addr == iloc.loc_addr and hf.func_name not in iloc.loc_label: lab = '\n' + hf.func_name + ' : ' if ELF_utils.elf_arm(): lab = '\n.thumb_func' + lab iloc.loc_label = lab + iloc.loc_label instrs[j] = set_loc(hi, iloc) i += 1 j -= 1 elif hf.func_begin_addr < iloc.loc_addr: i += 1 j += 1 return instrs
def process(self): """ Traverse instruction list and insert generated S_ labels in the correct positions """ do_update = lambda s, n: s if n in s else s + '\n' + n des1 = self.clean_sort(self.des) i = 0 j = 0 while True: if j == len(des1) or i == len(self.locs): break # if i == len(self.locs)-1 and j == len(des1)-1: # raise Exception("undefined des list") lh = self.locs[i] dh = des1[j] if dh == lh.loc_addr: lhs = 'S_' + dec_hex(lh.loc_addr) if ELF_utils.elf_arm() and not isinstance( self.instr_list[i][0], Types.InlineData): lhs = '.thumb_func\n' + lhs label = do_update(lh.loc_label, lhs + ' : ') self.locs[i].loc_label = label j += 1 elif dh < lh.loc_addr: i -= 1 j += 1 i += 1
def traverse(self): """ Analyze and modify instructions :return: list of generated labels """ if ELF_utils.elf_32() and not ELF_utils.elf_arm(): self.scan() return unify_int_list(self.label)
def perform(instrs, funcs): # Do stuff to the instruction list if not ELF_utils.elf_arm(): instrs.append( Types.TripleInstr(('mov', Types.RegClass('eax'), Types.RegClass('eax'), Types.Loc('', 0, True), False))) return instrs
def init_array_dump(self): return # This seems creating problems rather than solving them if len(self.init_array_list) != 0 and not ELF_utils.elf_arm(): with open('final_data.s', 'a') as f: f.write('\n\n.section .ctors,"aw",@progbits\n') f.write('.align 4\n') f.write('\n'.join( map(lambda s: '.long ' + s.strip(), self.init_array_list))) f.write('\n')
def pp_print_file(ilist): """ Write instruction string list to file :param ilist: string list """ with open('final.s', 'w') as f: f.write('.section .text\n') if ELF_utils.elf_arm(): f.write('.syntax unified\n.align 2\n.thumb\n') f.write('\n'.join(ilist)) f.write('\n\n')
def textProcess(self): """ Code disassembly dump """ # useless_func_del.main(self.file) if ELF_utils.elf_arm(): arm_process.arm_process(self.file) else: extern_symbol_process.pltgot(self.file) os.system("cat " + self.file + ".temp | grep \"^ \" | cut -f1,3 > instrs.info") os.system("cut -f 1 instrs.info > text_mem.info")
def post_analyze(il, re): """ Make final adjustments and write code to file :param il: instruction list :param re: symbol reconstruction object """ il = re.unify_loc(il) if ELF_utils.elf_arm(): il = re.alignvldrARM(il) ils = pp_print_list(il) ils = re.adjust_globallabel(Analysis.global_bss(), ils) pp_print_file(ils)
def picprocess32(filepath): """ PC relative operation in x86 32 bit code such as: call 804c452 <__x86.get_pc_thunk.bx> add $0x2b8e,%ebx mov $0x10, (%ebx) This operation usually loads into %ebx the address of the _GLOBAL_OFFSET_TABLE_ Further adjustments are operated in the analysis phase :param filepath: path to target executable """ if ELF_utils.elf_32() and ELF_utils.elf_exe() and not ELF_utils.elf_arm(): text_process_strip(filepath)
def lib32_processing(self, instrs, funcs): """ Process PC relative code for x86 32 binaries :param instrs: instruction list :param funcs: function list """ if ELF_utils.elf_32() and not ELF_utils.elf_arm(): helper = lib32_helper(instrs, funcs) self.label += map( lambda addr: (self.check_sec(addr).sec_name, addr), helper.traverse()) return helper.get_instrs() return instrs
def perform(instrs, funcs): """ Perform gfree instrumentation :param instrs: list of program's instruction :param funcs: list of function objects :return: instrumented list of instructions """ gfree = GfreeInstrumentation(instrs, funcs) gfree.findfreebranches() gfree.indirectprotection() gfree.returnprotection() if not ELF_utils.elf_arm(): gfree.rewrite_instr() elif config.gfree_ARMITdelete: gfree.remove_its() return gfree.instrs
def generatefuncID(self): """ Generate unique function identifier :return: integer for x86, integer tuple for ARM """ while True: fid = pack('<I', random.getrandbits(32)) if not fid in self.fIDset: if next((b for b in fid if b in alignmentenforce.badbytes), None) is not None: continue self.fIDset.add(fid) return unpack('<HH', fid) if ELF_utils.elf_arm() \ else unpack('<i', fid)[0]
def p_exp(exp): """ String from expression :param exp: expression :return: expression string """ if isinstance(exp, Types.Const): return p_const(exp) elif isinstance(exp, Types.Symbol): return p_symbol(exp) elif isinstance(exp, Types.AssistOpClass): return p_assist(exp) elif isinstance(exp, Types.Ptr): return p_ptraddr(exp) elif isinstance(exp, Types.RegClass): return p_reg(exp) elif isinstance(exp, Types.Label): return str(exp) elif ELF_utils.elf_arm(): if isinstance(exp, Types.ShiftExp): return p_shift(exp) elif isinstance(exp, Types.RegList): return p_reglist(exp) elif isinstance(exp, Types.TBExp): return p_tbexp(exp)
def visit_heuristic_analysis(self, instrs): """ Reconstruct symbolic information :param instrs: instruction list :return: instruction list with labels """ func = lambda i: self.check_text(get_loc(i).loc_addr) self.instr_list = instrs if ELF_utils.elf_arm(): self.pcreloffARM(instrs) instrs = map(self.vinst2ARM, enumerate(instrs)) self.doublemovARM(instrs) else: instrs = map(lambda i: self.vinst2(func, i), instrs) self.symbol_list = map(lambda l: int(l.split('x')[1], 16), self.deslist) + self.symbol_list return instrs
def pp_print_instr(i): """ Get instruction string in assembler syntax :param i: instruction :return: instruction string """ loc = get_loc(i) if not loc.loc_visible: return p_location(loc) res = p_location(loc) + p_prefix(i[-1]) if isinstance(i, Types.SingleInstr): res += p_single(i[0]) elif isinstance(i, Types.DoubleInstr): res += p_double(i[0], i[1]) elif isinstance(i, Types.TripleInstr): res += p_triple(i[0], i[1], i[2]) elif isinstance(i, Types.FourInstr): res += p_four(i[0], i[1], i[2], i[3]) elif isinstance(i, Types.FiveInstr): res += p_five(i[0], i[1], i[2], i[3], i[4]) elif ELF_utils.elf_arm() and isinstance(i, Types.CoproInstr): res += p_copro(i) return res
def addxorcanary(self, i, func): """ Apply return address encryption :param i: starting instruction index :param func: current funtion :return: instruction index after last inserted block """ if func.func_begin_addr in self.avoid: return i + 1 if len(self.indcalls[func.func_begin_addr]) == 0: header = inlining.get_returnenc(self.instrs[i]) self.instrs[i:i + 1] = header i += len(header) - 1 popcookie = False else: popcookie = True for t in self.rets[func.func_begin_addr]: while get_loc(self.instrs[i]).loc_addr != t: i += 1 if ELF_utils.elf_arm( ) and self.instrs[i][0][-2:] in Types.CondSuff: # Handle somehow IT blocks itlen = 0 while not self.instrs[i - itlen][0].upper().startswith( 'IT') and itlen < 5: itlen += 1 if itlen < 5: i -= itlen j = len(self.instrs[i][0].strip()) + 1 self.instrs[i:i + j] = inlining.translate_it_block( self.instrs[i:i + j]) while get_loc(self.instrs[i]).loc_addr != t: i += 1 footer = inlining.get_returnenc(self.instrs[i], popcookie) self.instrs[i:i + 1] = footer i += len(footer) return i
cmpb $0,{1} jne .{0}.L1 movl $.LC1,%edi movl $49,%edx jmp .{0}.L2 .{0}.L1: movl $.LC0,%edi movl $18,%edx .{0}.L2: movl $1,%esi call fwrite movl $-1,%edi call exit ''' elif ELF_utils.elf_arm(): # ARM keygenfunction = ''' push {{r0,r1,r2,r4,r12,lr}} movw r0,#:lower16:.LC2 movt r0,#:upper16:.LC2 movs r1,#0 bl open subs r4,r0,#0 blt {0} movs r2,#4 movw r1,#:lower16:{1} movt r1,#:upper16:{1} bl read cmp r0,#4 mov r2,r0
:param assist: assist operator :return: lowercase assist operator """ return str(assist).lower() def p_loc(loc): """ String of location address :param loc: location address :return: lowercase hexdecimal string """ return '0x%x' % loc if ELF_utils.elf_arm(): ## ARM def p_reg(reg): """ String of register :param reg: register :return: lowercase register string """ return str(reg).lower() def p_shift(shift): """ String of shift operand :param shift: shift value :return: shift operand string
def v_exp2(self, exp, instr, f, chk): """ Analyze expression and determine if it represent an address. If so substitute if with a corresponding symbolic expression using labels :param exp: expression :param instr: instruction tuple to which the expression belongs to :param f: unused :param chk: True if instruction is TEST (x86) :return: modified expression if matching, original one otherwise """ if isinstance(exp, Types.Const): if isinstance(exp, Types.Normal) and chk: return exp s = self.check_sec(exp) if s is not None: self.insert_data(s.sec_name, exp) return Types.Label(self.build_symbol(exp)) if self.check_text(exp): if ELF_utils.elf_arm(): exp = type(exp)(exp & (-2)) s_label = self.build_symbol(exp) self.insert_text(s_label, exp) return Types.Label(s_label) if self.check_plt(exp): return Types.Label(self.build_plt_symbol(exp)) elif isinstance(exp, Types.Symbol): if isinstance(exp, Types.JumpDes): if self.check_text(exp): s_label = 'S_' + dec_hex(exp) self.insert_text(s_label, exp) return Types.Label(s_label) elif self.check_plt(exp) and exp in self.plt_hash: return Types.Label(self.plt_hash[exp]) elif isinstance(exp, Types.StarDes): return Types.StarDes(self.v_exp2(exp.content, instr, f, chk)) elif isinstance(exp, Types.CallDes): if exp.func_name.startswith('S_0'): addr = int(exp.func_name[2:], 16) if self.check_text(addr): s_label = 'S_' + dec_hex(addr) self.insert_text(s_label, addr) return Types.Label(s_label) elif self.check_plt(addr): off = 0 while not self.plt_hash.get(addr - off, None): off += 2 return Types.Label(self.plt_hash[addr - off]) else: self.symbol_list.insert(0, exp.func_begin_addr) elif isinstance(exp, Types.Ptr): if isinstance(exp, (Types.BinOP_PLUS, Types.BinOP_MINUS)): r, addr = exp s = self.check_sec(addr) if s is not None: s_label = 'S_' + dec_hex(addr) self.insert_data(s.sec_name, addr) return Types.BinOP_PLUS_S((r, s_label)) \ if isinstance(exp, Types.BinOP_PLUS) \ else Types.BinOP_MINUS_S((r, s_label)) elif isinstance(exp, (Types.FourOP_PLUS, Types.FourOP_MINUS)): r1, r2, off, addr = exp s = self.check_sec(addr) if s is not None: s_label = 'S_' + dec_hex(addr) self.insert_data(s.sec_name, addr) return Types.FourOP_PLUS_S((r1,r2,off,s_label)) \ if isinstance(exp, Types.FourOP_PLUS) \ else Types.FourOP_MINUS_S((r1,r2,off,s_label)) elif isinstance(exp, Types.JmpTable_PLUS): addr, r, off = exp s = self.check_sec(addr) if s is not None: s_label = 'S_' + dec_hex(addr) self.insert_data(s.sec_name, addr) return Types.JmpTable_PLUS_S((s_label, r, off)) if self.check_text(addr): s_label = 'S_' + dec_hex(addr) self.insert_text(s_label, addr) return Types.JmpTable_PLUS_S((s_label, r, off)) elif isinstance(exp, Types.JmpTable_MINUS): addr, r, off = exp s = self.check_sec(addr) if s is not None: s_label = '-S_' + dec_hex(addr) self.insert_data(s.sec_name, addr) return Types.JmpTable_MINUS_S((s_label, r, off)) if self.check_text(addr): s_label = '-S_' + dec_hex(addr) self.insert_text(s_label, addr) return Types.JmpTable_MINUS_S((s_label, r, off)) return exp