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())))
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))