def logic_xori_obf(goal: Goal) -> Tuple[Promise, Goal]:
    """
    Modifies a constant by xor-ing it with a random value.

    The produced promise reverts the transformation based on the identity: (A xor B) xor B = A
    The value used for obfuscation is derived from high-quality entropy sources.
    """

    noise = randbits(goal.const.size)
    immediate = goal.const.int_val
    return (Promise(ALOps.XORI, goal.reg, _next_reg_placeholder(goal.reg), None,
                    Instruction.ImmediateConstant(goal.const.size, None, immediate ^ noise)),
            Goal(_next_reg_placeholder(goal.reg), Instruction.ImmediateConstant(goal.const.size, None, noise)))
def lui_primer(target: Instruction) -> Derivation:
    """Break a load upper immediate into less revealing operations (ors and shifts); obfuscate the 12 upper bits."""

    # Shift the bits around to load 12 + 8 bits separately: insert 12b -> shift left 8 -> insert 8b -> shift left 12
    loading_sequence = [
        Promise(ALOps.SLLI, target.r1, 0, None, Instruction.ImmediateConstant(12, None, 12)),
        Promise(ALOps.ORI,
                0,
                1,
                None,
                Instruction.ImmediateConstant(12, None, target.immediate.value[-8:].int_val())),
        Promise(ALOps.SLLI, 1, 2, None, Instruction.ImmediateConstant(12, None, 8))
        ]

    # With the previous shifting and loading machinery in place, target the 12 most significant bits for obfuscation
    return Derivation(loading_sequence,
                      Goal(2, Instruction.ImmediateConstant(12, None, target.immediate.value[:-8].int_val())))
def terminator(goal: Goal) -> Promise:
    """
    Load the goal's value through a load immediate instruction.

    Used to terminate a derivation chain.
    """

    return Promise(OtherOps.LI, goal.reg, None, None,
                   Instruction.ImmediateConstant(32, None, goal.const.int_val))
def generate_derivation_chain(instruction: Instruction, max_shifts: int, max_logical: int, min_length: int = 0) \
        -> List[Promise]:
    """
    Generate an obfuscated derivation chain for a given immediate instruction.

    The generated chain is random in length, composition and ordering, but respects the imposed constraints.

    :param instruction: the instruction to be obfuscated
    :param max_shifts: the maximum number of shift operations that the derivation chain should contain
    :param max_logical: the maximum number of boolean logic operations that the derivation chain should contain
    :param min_length: the minimum length of the derivation chain
    :return: a list of promises implementing the constant derivation
    """

    seed()

    # Load Immediate instructions have to be treated in a special way, since they have a very long immediate value.
    # Split the 'li' into 'lui' and 'ori', targeting the latter for obfuscation and keeping aside the 'lui' as a
    # residual.
    if instruction.opcode == 'li':
        oc = Derivation([Promise(ALOps.OR, instruction.r1, instruction.r1, 0, None)],
                        Goal(0, Instruction.ImmediateConstant(12, None, instruction.immediate.value[20:].int_val())))
        # TODO lui obfuscation should happen automatically, but how can we parameterize its?
        leftover = [Promise(OtherOps.LUI,
                            instruction.r1,
                            None,
                            None,
                            Instruction.ImmediateConstant(20, None, instruction.immediate.value[0:20].int_val()))]
    else:
        oc = primers[instruction.opcode](instruction)
        leftover = []

    # Build the obfuscators' pool
    obfuscators = choices(population=logic_obfuscators, k=max_logical) + ([shifter_obf] * max_shifts)
    obfuscators = sample(population=obfuscators, k=randrange(min_length, max_shifts + max_logical))

    # Grow the chain
    for obf in obfuscators:
        new_derivation_step, new_goal = obf(oc.remainder)
        oc = Derivation(oc.chain + [new_derivation_step], new_goal)

    # Terminate chain and return it, adding the eventually present residual
    return oc.chain + [terminator(oc.remainder)] + leftover
def shifter_obf(goal: Goal) -> Tuple[Promise, Goal]:
    """
    Modify a constant by bit-shifting it, producing a promise for an instruction that reverses the shift.

    The shifting direction that results in the highest number of shifts is used.
    """

    def _count_leading_zeros(constant: BitVector):
        leading_zeroes = 0
        while constant[0] == 0:
            leading_zeroes += 1
            constant <<= 1

        return leading_zeroes

    def _count_trailing_zeroes(constant: BitVector):
        trailing_zeroes = 0
        while constant[-1] == 0:
            trailing_zeroes += 1
            constant >>= 1

        return trailing_zeroes

    if goal.const.int_val != 0:
        lead = _count_leading_zeros(goal.const.value)
        trail = _count_trailing_zeroes(goal.const.value)
    else:
        lead = goal.const.size
        trail = goal.const.size

    if lead > trail:
        shift = lead
        new_val = goal.const.value << lead
        instruction = ALOps.SRLI
    else:
        shift = trail
        new_val = goal.const.value >> trail
        instruction = ALOps.SLLI

    return (Promise(instruction, goal.reg, _next_reg_placeholder(goal.reg), None,
                    Instruction.ImmediateConstant(goal.const.size, None, shift)),
            Goal(_next_reg_placeholder(goal.reg), Instruction.ImmediateConstant(new_val.size, None, new_val.int_val())))
示例#6
0
def placer(cfg: DiGraph, promises: List[Promise], node_chain: List[NodeBlock], target_instr: int) -> int:
    """
    the role of this function is to convert the promises into real instructions and insert these one in the previously
    identified positions
    @param node_chain: the list of NodeBlock that identify the valid nodes where to put the new instruction generated by
                       the constant obfuscation
    @param target_instr: the instruction to be obfuscated
    @param cfg: the graph that represent the assembly program
    @param promises: the list of promises
    """
    register_matrix = {}
    positions = generate_positions(node_chain, len(promises))
    iter_list = list()
    for i in range(len(positions)):
        for t in range(len(positions[i][1])):
            iter_list.append(i)
    target_instr = fix_original_instruction(target_instr, positions)
    instr_queue = Queue()
    promises.reverse()
    next_pos = 1
    for prom, i in zip(promises, iter_list):
        if i != iter_list[next_pos]:
            rd = check_reg(prom.rd, register_matrix, node_chain[i].conjunction_reg)
        else:
            rd = check_reg(prom.rd, register_matrix, sample(node_chain[i].reg_pool, 1)[0])
        rs1 = check_reg(prom.rs1, register_matrix, sample(node_chain[i].reg_pool, 1)[0])
        rs2 = check_reg(prom.rs2, register_matrix, sample(node_chain[i].reg_pool, 1)[0])
        instr = Instruction(prom.op.name.lower(), opcd_family[prom.op.name.lower()], r1=rd, r2=rs1, r3=rs2,
                            immediate=prom.const)
        instr.inserted = True
        instr_queue.put(instr)
        if next_pos < len(iter_list)-1:
            next_pos += 1
    for i in range(len(positions)):
        active_block = cfg.nodes[positions[i][0]]['block']
        for t in range(len(positions[i][1])):
            line = positions[i][1][t]
            instr = instr_queue.get()
            active_block.insert(line, instr)
            bind_register_to_value(cfg, positions[i][0])
    return target_instr
def mem_primer(target: Instruction) -> Derivation:
    """Generate an equivalent instruction sequence as a memory op with no offset and an already shifted base."""

    # 0: new address base
    # 1: register from where the offset is to be loaded
    starting_sequence = [
        Promise(MemOps[target.opcode.upper()], target.r1, 0, None, Instruction.ImmediateConstant(12, None, 0)),
        Promise(ALOps.ADD, 0, target.r2, 1, None)
        ]

    # Prime the derivation with the starting sequence and the correct promise
    return Derivation(starting_sequence, Goal(1, target.immediate))
示例#8
0
def srl_instr(free_regs: list, used_reg: list) -> Instruction:
    return Instruction("srl", "r", r1=choice(free_regs), r2=choice(used_reg), r3=choice(used_reg))
示例#9
0
def slti_instr(free_regs: list, used_reg: list) -> Instruction:
    return Instruction("slti", "i", r1=choice(free_regs), r2=choice(used_reg), immediate=getrandbits(12))
示例#10
0
def mv_instr(free_regs: list, used_reg: list) -> Instruction:
    return Instruction("mv", "_2arg", r1=choice(free_regs), r2=choice(used_reg))
示例#11
0
def sraiw_instr(free_regs: list, used_reg: list) -> Instruction:
    # generates a 5-bit immediate, only these are meaningful for the instruction

    return Instruction("sraiw", "i", r1=choice(free_regs), r2=choice(used_reg), immediate=getrandbits(5))
示例#12
0
def mulhsu_instr(free_regs: list, used_reg: list) -> Instruction:
    return Instruction("mulhsu", "r", r1=choice(free_regs), r2=choice(used_reg), r3=choice(used_reg))