def flatten_cfg(self, cfg): fname, _ = cfg bbl = self.fb_tbl[fname] name_eloc_dict = {} for b in bbl: name_eloc_dict[b.bblock_name] = b.bblock_end_loc f = None for _f in self.funcs: if _f.func_name == fname: f = _f break assert f is not None func_instrs = self.func_instrs(f) for i in func_instrs: op = get_op(i) if op in JumpOp: des = get_cf_des(i) if isinstance(des, Label): i0_loc = get_loc(i) i1_loc = self._get_loc(i) i1_loc.loc_label = '' i0 = TripleInstr((self._ops['mov'], Label('global_des'), Label('$' + des), i0_loc, None)) junk = get_junk_codes(i1_loc) i1 = DoubleInstr((op, Label('switch_bb'), i1_loc, None)) self.replace_instrs(i0, get_loc(i), i) for _i in junk: self.append_instrs(_i, get_loc(i)) self.append_instrs(i1, get_loc(i)) self.update_process()
def split_bb(self, b_name, b_instrs, split_pos): instr = b_instrs[split_pos] split_symbol = b_name + '_split' loc = get_loc(instr) loc_without_label = self._get_loc(instr) loc_without_label.loc_label = '' loc_with_label = self._get_loc(instr) loc_with_label.loc_label = split_symbol + ':' i1 = DoubleInstr((self._ops['jmp'], Label(split_symbol), loc_without_label, None)) # i2 should be replaced with a list of garbage code # i2 = SingleInstr((self._ops['nop'], loc_with_label, None)) self.insert_instrs(i1, loc) # self.insert_instrs(i2, loc) # we insert some codes after the jmp, which will never execute useless = [] useless.append(DoubleInstr((self._ops['push'], self._regs[1], loc_without_label, None))) useless.append(TripleInstr((self._ops['mov'], self._regs[0], self._regs[1], loc_without_label, None))) useless.append(TripleInstr(('xor', self._regs[0], self._regs[0], loc_without_label, None))) useless.append(DoubleInstr((self._ops['jmp'], Label('printf'), loc_without_label, None))) for u in useless: self.insert_instrs(u, loc) junk = get_junk_codes(loc_with_label) if len(junk) == 0: junk.append(SingleInstr((self._ops['nop'], loc_with_label, None))) junk[0][-2].loc_label = loc_with_label.loc_label for j in junk: self.insert_instrs(j, loc)
def get_opaque_header2(self, b, spt_pos=0): opaque_symbol = b.bblock_name + '_opaque_next' bil = self.bb_instrs(b) i = bil[spt_pos] # print 'basic block opaque transformation: ' + b.bblock_name + ' ' + dec_hex(b.bblock_begin_loc.loc_addr) \ # + '->' + dec_hex(b.bblock_end_loc.loc_addr) # print 'instruction be added opaque block is: %s' % pp_print_instr(i) iloc = self._get_loc(i) tmp_iloc = copy.deepcopy(iloc) tmp_iloc.loc_label = '' tmp_iloc2 = copy.deepcopy(iloc) tmp_iloc2.loc_label = opaque_symbol + ': ' # store the %eax to stack i1 = DoubleInstr(('push', self._regs[0], iloc, None)) save_flag = SingleInstr((self._ops['pushf'], tmp_iloc, None)) junk1 = get_junk_codes(tmp_iloc) i2 = DoubleInstr(('call', Types.Label('opaque_func'), tmp_iloc, None)) # in attach_opaque_routines, we set the value of %eax=0x0 # cmp is supposed to be true, and the code will always jump to opaque_symbol i3 = TripleInstr(('cmp', self._regs[0], Types.Normal(self.routine_constant), tmp_iloc, None)) i4 = DoubleInstr(('je', Types.Label(opaque_symbol), tmp_iloc, None)) # this junk should never be executed junk2 = get_junk_codes(tmp_iloc) i5 = DoubleInstr(('call', Types.Label('halt_func'), tmp_iloc, None)) # recover the value of %eax, and here is the start of opaque_symbol recover_flag = SingleInstr((self._ops['popf'], tmp_iloc2, None)) i6 = DoubleInstr(('pop', self._regs[0], tmp_iloc, None)) i0 = set_loc(i, tmp_iloc) res = list() res.append((instr_update.INSERT, i1, iloc)) res.append((instr_update.INSERT, save_flag, iloc)) res.extend([(instr_update.INSERT, j, iloc) for j in junk1]) res.append((instr_update.INSERT, i2, iloc)) res.append((instr_update.INSERT, i3, iloc)) res.append((instr_update.INSERT, i4, iloc)) res.extend([(instr_update.INSERT, j, iloc) for j in junk2]) res.append((instr_update.INSERT, i5, iloc)) res.append((instr_update.INSERT, recover_flag, iloc)) res.append((instr_update.INSERT, i6, iloc)) res.append((instr_update.REPLACE, i0, iloc, i)) return res
def update_preceding_func(self, pre, f): pre_f_instrs = self.func_instrs(pre) last_loc = self._get_loc(pre_f_instrs[-1]) last_loc.loc_label = '' junk = get_junk_codes(last_loc, 0) for j in junk: self.append_instrs(j, last_loc) i = DoubleInstr((self._ops['jmp'], Label(f.func_name), last_loc, None)) self.append_instrs(i, last_loc)
def get_switch_routine(self, loc): loc_without_label = copy.deepcopy(loc) loc_without_label.loc_label = '' junk = get_junk_codes(loc) i0 = DoubleInstr( (self._ops['jmp'], Label('*global_des'), loc_without_label, None)) junk.append(i0) # note junk can be length 0, the label modification must locate after the appending junk[0][-2].loc_label = ".globl switch_bb\nswitch_bb:" return junk
def update_succeeding_func(self, f, suc): f_instrs = self.func_instrs(f) last_loc = self._get_loc(f_instrs[-1]) last_loc.loc_label = '' junk = get_junk_codes(last_loc, 0) for j in junk: self.append_instrs(j, last_loc) i = DoubleInstr( (self._ops['jmp'], Label(suc.func_name), last_loc, None)) self.append_instrs(i, last_loc)
def _get_garbage(self, loc, mode=1): if mode == 1: nops = self._nop_garbage_instrs(loc) num_instrs = len(nops) # random.randint(1, len(nops) - 4) res = [] for i in range(num_instrs): res.append(random.choice(nops)) return res elif mode == 2: return get_junk_codes(loc, None) else: return []
def get_branch_routine(self, iloc): """ return the list of routine instructions for branch functions :param iloc: the location of instruction that routine being inserted :return: the list of routine instructions """ loc_with_branch_label = copy.deepcopy(iloc) loc_with_branch_label.loc_label = 'branch_routine: ' loc = copy.deepcopy(iloc) loc.loc_label = '' i0 = DoubleInstr((self._ops['pop'], Label('global_des'), loc_with_branch_label, None)) junk = get_junk_codes(loc) i1 = DoubleInstr((self._ops['jmp'], Label('*branch_des'), loc, None)) res = [i0] res.extend(junk) res.append(i1) return res
def update_current_bb(self, fb, last_loc, sb): fb_l = self.bb_instrs(fb) sb_l = self.bb_instrs(sb) fl = len(fb_l) sl = len(sb_l) if fl >= sl: for idx in range(fl): if sl <= idx: floc = self._get_loc(fb_l[idx]) floc.loc_label = '' # i = SingleInstr(('nop', floc, False)) # self.replace_instrs(i, floc, fb_l[idx]) junk = get_junk_codes(floc) if len(junk) == 0: junk.append(SingleInstr(('nop', floc, False))) self.replace_instrs(junk[0], floc, fb_l[idx]) for j in junk[1:]: self.append_instrs(j, floc) elif idx < sl: # Note: the get_loc return the reference of loc, which may cause side effect floc = self._get_loc(fb_l[idx]) sloc = self._get_loc(sb_l[idx]) floc.loc_label = sloc.loc_label sh_ = set_loc(sb_l[idx], floc) self.replace_instrs(sh_, floc, fb_l[idx]) else: for idx in range(sl): if idx >= fl: sloc = self._get_loc(sb_l[idx]) last_loc.loc_label = sloc.loc_label sh_ = set_loc(sb_l[idx], last_loc) self.insert_instrs(sh_, last_loc) elif idx == fl - 1: loc = self._get_loc(fb_l[idx]) sloc = self._get_loc(sb_l[idx]) loc.loc_label = sloc.loc_label sh_ = set_loc(sb_l[idx], loc) self.replace_instrs(sh_, loc, fb_l[idx]) else: floc = self._get_loc(fb_l[idx]) sloc = self._get_loc(sb_l[idx]) floc.loc_label = sloc.loc_label sh_ = set_loc(sb_l[idx], floc) self.replace_instrs(sh_, floc, fb_l[idx])
def get_opaque_routines(self, iloc): iloc1 = copy.deepcopy(iloc) iloc1.loc_label = 'opaque_func: ' tmp_loc = copy.deepcopy(iloc) tmp_loc.loc_label = '' iloc6 = copy.deepcopy(iloc) iloc6.loc_label = 'halt_func: ' i1 = DoubleInstr(('push', self._stack_regs['bp'], iloc1, None)) i2 = TripleInstr(('mov', self._stack_regs['bp'], self._stack_regs['sp'], tmp_loc, None)) junk = get_junk_codes(tmp_loc, 0) # set the value of %eax to be 0 i3 = TripleInstr(('mov', self._regs[0], Types.Normal(self.routine_constant), tmp_loc, None)) i4 = DoubleInstr(('pop', self._stack_regs['bp'], tmp_loc, None)) i5 = SingleInstr(('ret', tmp_loc, None)) i6 = SingleInstr(('hlt', iloc6, None)) res = [i1, i2] res.extend(junk) res.extend([i3, i4, i5, i6]) return res
def _branch_a_func(self, f): fil = self.func_instrs(f) find_a_valid_func = False for instr in fil: op = get_op(instr) des = get_cf_des(instr) if des is not None and isinstance(des, Label): if op in JumpOp: if random.random() > obfs_proportion: continue # here we modify the process of 2 situations, jmp and conditional jmp if p_op(op) == 'jmp' or p_op(op) == self._ops['jmp']: # this is a simple jump, we simply cache the des and call the routine find_a_valid_func = True loc = self._get_loc(instr) i0 = TripleInstr( (self._ops['mov'], Label('branch_des'), Label('$' + str(des)), loc, None)) loc1 = copy.deepcopy(loc) loc1.loc_label = '' i1 = DoubleInstr((self._ops['call'], Label('branch_routine'), loc1, None)) junk1 = get_junk_codes(loc1) junk2 = get_junk_codes(loc1) self.insert_instrs(i0, loc) for _i in junk1: self.insert_instrs(_i, loc) self.replace_instrs(i1, loc, instr) for _i in junk2: self.append_instrs(_i, loc) elif p_op(op) in {'je', 'jne', 'jl', 'jle', 'jg', 'jge'}: # we only handle with these conditional jmp find_a_valid_func = True loc = self._get_loc(instr) postfix = p_op(op)[1:] # we ues conditional move the modify a conditional jmp self._new_des_id += 1 fall_through_label = 'fall_through_label_%d' % self._new_des_id loc_no_label = copy.deepcopy(loc) loc_no_label.loc_label = '' loc_fall_through = copy.deepcopy(loc) loc_fall_through.loc_label = fall_through_label + ':' tmp = [ DoubleInstr((self._ops['push'], self._regs[0], loc, None)), # 0 replace DoubleInstr((self._ops['push'], self._regs[1], loc_no_label, None)), TripleInstr((self._ops['mov'], self._regs[0], Label('$' + fall_through_label), loc_no_label, None)), TripleInstr( (self._ops['mov'], self._regs[1], Label('$' + str(des)), loc_no_label, None)), TripleInstr(('cmov' + postfix, self._regs[0], self._regs[1], loc_no_label, None)), TripleInstr((self._ops['mov'], Label('branch_des'), self._regs[0], loc_no_label, None)), DoubleInstr((self._ops['pop'], self._regs[1], loc_no_label, None)), DoubleInstr((self._ops['pop'], self._regs[0], loc_no_label, None)), DoubleInstr( (self._ops['call'], Label('branch_routine'), loc_no_label, None)), SingleInstr( (self._ops['nop'], loc_fall_through, None)) ] self.replace_instrs(tmp[0], loc, instr) for _i in tmp[1:]: self.append_instrs(_i, loc) return find_a_valid_func
def get_opaque_header1(self, b, spt_pos=0): """ get the list of instructions which work as the opaque block the instructions works as 'if (y < 10 || x*(x-1) % 2 == 0)' It is clear the the statement is always true (if something wrong and it run into false branch, halt the program) :param b: the opaque block will be inserted before the block :param spt_pos: the opaque block will be inserted before the instruction(which is b[spt_pos]) :return: the instructions list of the opaque_block """ opaque_symbol = b.bblock_name + '_opaque_next' bil = self.bb_instrs(b) i = bil[spt_pos] iloc_with_block_label = self._get_loc(i) iloc_without_label = copy.deepcopy(iloc_with_block_label) iloc_without_label.loc_label = '' iloc_with_true_branch_label = copy.deepcopy(iloc_with_block_label) iloc_with_true_branch_label.loc_label = opaque_symbol + ': ' # false branch will call halt_func directly res = [] res.append((instr_update.INSERT, DoubleInstr(('push', self._regs[0], iloc_with_block_label, None)), iloc_with_block_label)) # use this reg as x res.append((instr_update.INSERT, SingleInstr((self._ops['pushf'], iloc_without_label, None)), iloc_with_block_label)) # save flag res.append((instr_update.INSERT, DoubleInstr(('push', self._regs[1], iloc_without_label, None)), iloc_with_block_label)) # use this reg as y # y < 10 res.append(( instr_update.INSERT, TripleInstr(('cmp', self._regs[1], Types.Normal(10), iloc_without_label, None)), iloc_with_block_label)) res.append((instr_update.INSERT, DoubleInstr(('jl', Types.Label(opaque_symbol), iloc_without_label, None)), iloc_with_block_label)) # x*(x-1) % 2 == 0, use y to store value of (x-1) res.append((instr_update.INSERT, TripleInstr(('mov', self._regs[0], self._regs[1], iloc_without_label, None)), iloc_with_block_label)) # junk code res.extend([(instr_update.INSERT, j, iloc_without_label) for j in get_junk_codes(iloc_without_label)]) res.append((instr_update.INSERT, TripleInstr(('sub', self._regs[1], Types.Normal(1), iloc_without_label, None)), iloc_with_block_label)) res.append((instr_update.INSERT, TripleInstr(('imul', self._regs[0], self._regs[1], iloc_without_label, None)), iloc_with_block_label)) res.append((instr_update.INSERT, TripleInstr(('and', self._regs[0], Types.Normal(1), iloc_without_label, None)), iloc_with_block_label)) res.append((instr_update.INSERT, TripleInstr(('test', self._regs[0], self._regs[0], iloc_without_label, None)), iloc_with_block_label)) res.append((instr_update.INSERT, DoubleInstr(('je', Types.Label(opaque_symbol), iloc_without_label, None)), iloc_with_block_label)) # false branch res.append((instr_update.INSERT, DoubleInstr(('call', Types.Label('abort'), iloc_without_label, None)), iloc_with_block_label)) # true branch res.append((instr_update.INSERT, DoubleInstr(('pop', self._regs[1], iloc_with_true_branch_label, None)), iloc_with_block_label)) res.append( (instr_update.INSERT, SingleInstr((self._ops['popf'], iloc_without_label, None)), iloc_with_block_label)) res.append( (instr_update.INSERT, DoubleInstr(('pop', self._regs[0], iloc_without_label, None)), iloc_with_block_label)) # remove the label of original block new_line = set_loc(i, iloc_without_label) res.append((instr_update.REPLACE, new_line, iloc_with_block_label, i)) return res