Пример #1
0
    def is_JOP_COP_dispatcher(self):
        """
        :return boolean: Returns True if the gadget is a JOP or COP dispatcher. Defined as a gadget that begins with a
                         arithmetic operation on a register and ends with a branch to a deference of that register. Used
                         to iterate through instructions in payload. Only restrictions on the arithmetic operation is
                         that it doesn't use the same register as both operands.
        """
        first_instr = self.instructions[0]
        last_instr = self.instructions[len(self.instructions) - 1]

        # Only consider gadgets that end in dereference of a register and start with opcodes of interest
        if "[" in last_instr.op1 and \
           first_instr.opcode in ["inc", "dec", "add", "adc", "sub", "sbb"] and "[" not in first_instr.op1:
            gpi_target = Instruction.get_operand_register_family(last_instr.op1)
            arith_target_1 = Instruction.get_operand_register_family(first_instr.op1)

            # Secondary check: if the second op is a constant ensure it is in range [1, 32]
            if Instruction.is_constant(first_instr.op2):
                additive_value = Instruction.get_operand_as_constant(first_instr.op2)
                if additive_value < 1 or additive_value > 32:
                    return False

            arith_target_2 = Instruction.get_operand_register_family(first_instr.op2)
            return gpi_target == arith_target_1 and arith_target_1 != arith_target_2

        return False
Пример #2
0
    def check_register_ops(self):
        """
        :return void: Increases gadget's score if it contains operations on a value carrying or a bystander register
        """
        first_instr = self.instructions[0]

        # Check if the first instruction creates a value or is an xchg operand (excluded as an edge case)
        if not first_instr.creates_value() or "xchg" in first_instr.opcode:
            first_family = None
        else:
            # Check op1 to find the register family to protect
            first_family = Instruction.get_operand_register_family(first_instr.op1)

        for i in range(1, len(self.instructions)-1):
            cur_instr = self.instructions[i]

            # Ignore instructions that do not create values
            if not cur_instr.creates_value():
                continue

            # If the new value is a modification of the value-carrying register
            if first_family is not None and first_family == Instruction.get_operand_register_family(cur_instr.op1):
                if cur_instr.opcode in ["shl", "shr", "sar", "sal", "ror", "rol", "rcr", "rcl"]:
                    self.score += 1.5
                else:
                    self.score += 1.0  # Will be a static modification, otherwise it would have been rejected earlier
            elif "xchg" not in cur_instr.opcode and cur_instr.opcode != "pop":
                # The modification is to a "bystander register". static mods +0.5, non-static +1.0
                if cur_instr.op2 is not None and Instruction.get_operand_register_family(cur_instr.op2) is not None:
                    self.score += 1.0
                else:
                    self.score += 0.5
Пример #3
0
    def clobbers_indirect_target(self):
        """
        :return boolean: Returns True if the JOP/COP gadget's instructions modify the indirect branch register in
                         certain ways, False otherwise.
        """
        # Get the register family of the indirect jump / call
        last_instr = self.instructions[len(self.instructions)-1]
        if last_instr.opcode.startswith("jmp") or last_instr.opcode.startswith("call"):
            family = Instruction.get_operand_register_family(last_instr.op1)

            # Check each instruction to see if it clobbers the value
            for i in range(len(self.instructions)-1):
                cur_instr = self.instructions[i]

                # First check if the instruction modifies the target
                if cur_instr.op1 in Instruction.register_families[family]:
                    # Does the instruction zeroize out the target?
                    if cur_instr.opcode == "xor" and cur_instr.op1 == cur_instr.op2:
                        return True
                    # Does the instruction perform a RIP-relative LEA into the target?
                    if cur_instr.opcode == "lea" and ("rip" in cur_instr.op2 or "eip" in cur_instr.op2):
                        return True
                    # Does the instruction load a string or a value of an input port into the target?
                    if cur_instr.opcode.startswith("lods") or cur_instr.opcode == "in":
                        return True
                    # Does the instruction overwrite the target with a static value or segment register value?
                    if "mov" in cur_instr.opcode and (Instruction.is_constant(cur_instr.op2) or
                                                      Instruction.get_operand_register_family(cur_instr.op2) is None):
                        return True
        return False
Пример #4
0
    def clobbers_created_value(self):
        """
        :return boolean: Returns True if the gadget completely overwrites the value created in the first instruction,
                         False otherwise.
        """

        first_instr = self.instructions[0]

        # Check if the first instruction creates a value or is an xchg operand (excluded as an edge case)
        if not first_instr.creates_value() or "xchg" in first_instr.opcode:
            return False

        # Check op1 to find the register family to protect
        first_family = Instruction.get_operand_register_family(first_instr.op1)

        # Most likely means first operand is a constant, exclude from analysis
        if first_family is None:
            return False

        # Iterate through intermediate instructions, determine if it overwrites protected value (or part of it)
        for i in range(1, len(self.instructions)-1):
            cur_instr = self.instructions[i]

            # Ignore instructions that do not create values
            if not cur_instr.creates_value() or "xchg" in cur_instr.opcode:
                continue

            # Check for non-static modification of the register family
            if first_family == Instruction.get_operand_register_family(cur_instr.op1):
                if (cur_instr.op2 is None and cur_instr.opcode not in ["inc", "dec", "neg", "not"]) or \
                   (cur_instr.op2 is not None and not Instruction.is_constant(cur_instr.op2)):
                    return True

        return False
Пример #5
0
    def is_JOP_COP_dataloader(self):
        """
        :return boolean: Returns True if the gadget is a JOP or COP data loader. Defined as a gadget that begins with a
                         pop opcode to a non-memory location, that is also not the target of the GPI. Used to pop a
                         necessary value off stack en masse before redirecting to the dispatcher.
        """
        first_instr = self.instructions[0]

        if first_instr.opcode == "pop" and "[" not in first_instr.op1:
            gpi_target = Instruction.get_operand_register_family(self.instructions[len(self.instructions) - 1].op1)
            pop_target = Instruction.get_operand_register_family(first_instr.op1)
            return gpi_target != pop_target

        return False
Пример #6
0
    def is_JOP_trampoline(self):
        """
        :return boolean: Returns True if the gadget is a JOP trampoline. Defined as a gadget that begins with a
                         pop opcode to a non-memory location, and that ends in a dereference of that value. Used to
                         redirect execution to value stored in memory.
        """
        first_instr = self.instructions[0]
        gpi_target_op = self.instructions[len(self.instructions) - 1].op1

        if first_instr.opcode == "pop" and "[" not in first_instr.op1:
            gpi_target = Instruction.get_operand_register_family(gpi_target_op)
            pop_target = Instruction.get_operand_register_family(first_instr.op1)
            return gpi_target == pop_target and "[" in gpi_target_op

        return False
Пример #7
0
    def is_COP_strong_trampoline(self):
        """
        :return boolean: Returns True if the gadget is a COP strong trampoline. Defined as a gadget that begins with a
                         pop opcode, and contains at least one other pop operation. The last non-pop all operation must
                         target the call target.
        """
        first_instr = self.instructions[0]
        last_instr = self.instructions[len(self.instructions) - 1]
        call_target = Instruction.get_operand_register_family(last_instr.op1)

        # Only consider instructions that start with a pop
        if first_instr.opcode == "pop" and "[" not in first_instr.op1:
            cnt_pops = 1
            last_pop_target = first_instr.op1

            # Scan intermediate instructions for pops
            for i in range(1, len(self.instructions)-1):
                cur_instr = self.instructions[i]

                if cur_instr.opcode.startswith("popa"):
                    cnt_pops += 1

                if cur_instr.opcode == "pop" and "[" not in cur_instr.op1:
                    cnt_pops += 1
                    last_pop_target = cur_instr.op1

            # Check that at least two pops occurred and the last pop target is the call target
            if cnt_pops > 1 and last_pop_target in Instruction.register_families[call_target]:
                return True

        return False
Пример #8
0
    def is_COP_initializer(self):
        """
        :return boolean: Returns True if the gadget is a COP initializer. Defined as a gadget that begins with a
                         "pop all" opcode, does not use register bx/cx/dx/di as the call target, and does not clobber
                         bx/cx/dx or the call target in an intermediate instruction
        """
        first_instr = self.instructions[0]
        last_instr = self.instructions[len(self.instructions)-1]
        call_target = Instruction.get_operand_register_family(last_instr.op1)

        if first_instr.opcode.startswith("popa") and call_target not in [1, 2, 3, 5]:   # BX, CX, DX, DI families
            # Build collective list of register families to protect from being clobbered
            protected_families = [1, 2, 3, call_target]
            protected_registers = []
            for family in protected_families:
                for register in Instruction.register_families[family]:
                    protected_registers.append(register)

            # Scan intermediate instructions to ensure they do not clobber a protected register
            for i in range(1, len(self.instructions)-1):
                cur_instr = self.instructions[i]

                # Ignore instructions that do not create values
                if not cur_instr.creates_value():
                    continue

                # Check for non-static modification of the register family
                if cur_instr.op1 in protected_registers:
                    if (cur_instr.op2 is None and cur_instr.opcode not in ["inc", "dec", "neg", "not"]) or \
                       (cur_instr.op2 is not None and not Instruction.is_constant(cur_instr.op2)):
                        return False
            return True

        return False
Пример #9
0
 def is_invalid_branch(self):
     """
     :return boolean: Returns True if the gadget is 'jmp' or 'call' ending and the call target is a constant offset
                      or does not target a recognized register family. False otherwise
     """
     last_instr = self.instructions[len(self.instructions)-1]
     if last_instr.opcode.startswith("call") or last_instr.opcode.startswith("jmp"):
         if Instruction.get_operand_register_family(last_instr.op1) is None:
             return True
     return False
Пример #10
0
    def check_branch_target_of_operation(self):
        """
        :return void: Increases gadget's score if the gadget has an intermediate instruction that performs certain
                      operations on the indirect branch target register family.
        """
        last_instr = self.instructions[len(self.instructions)-1]
        target_family = Instruction.get_operand_register_family(last_instr.op1)

        # Scan instructions to determine if they modify the target register family
        for i in range(len(self.instructions) - 1):
            cur_instr = self.instructions[i]

            # Ignore instructions that do not create values
            if not cur_instr.creates_value():
                continue

            # Increase score by 3 for shift/rotate ops, and 2 for others
            if Instruction.get_operand_register_family(cur_instr.op1) == target_family:
                if cur_instr.opcode in ["shl", "shr", "sar", "sal", "ror", "rol", "rcr", "rcl"]:
                    self.score += 3.0
                else:    # All other modifications to target register
                    self.score += 2.0
Пример #11
0
    def is_COP_intrastack_pivot(self):
        """
        :return boolean: Returns True if the gadget is a COP Intra-stack pivot gadget. Defined as a gadget that begins
                         with an additive operation on the stack pointer register. Used to move around in shellcode
                         during COP exploits. Only restriction on the arithmetic operation is that the second operand
                         is not a pointer.
        """
        first_instr = self.instructions[0]

        if first_instr.opcode in ["inc", "add", "adc", "sub", "sbb"] and "[" not in first_instr.op1:
            arith_target = Instruction.get_operand_register_family(first_instr.op1)
            if arith_target == 7:             # RSP, ESP family number
                if first_instr.op2 is None or "[" not in first_instr.op2:
                    return True

        return False
Пример #12
0
    def creates_unusable_value(self):
        """
        :return boolean: Returns True if the gadget creates a value in segment or extension registers, or are
                         RIP-relative, or are constant memory locations; False otherwise.
        """
        # Check if the first instruction creates a value (or may potentially set a flag
        first_instr = self.instructions[0]
        if first_instr.opcode in ["cmp", "test", "push"] or first_instr.op1 is None:
            return False

        # Check if first operand is not a constant and it does not belong to a recognized register family
        if not Instruction.is_constant(first_instr.op1) and \
           Instruction.get_operand_register_family(first_instr.op1) is None:
            return True

        return False
Пример #13
0
    def clobbers_stack_pointer(self):
        """
        :return boolean: Returns True if the ROP gadget's instructions assign a non-static value to the stack pointer
                         register, False otherwise.
        """
        # Only check ROP gadgets
        last_instr = self.instructions[len(self.instructions) - 1]
        if last_instr.opcode.startswith("ret"):
            for i in range(len(self.instructions) - 1):
                cur_instr = self.instructions[i]

                # Ignore instructions that do not create values
                if not cur_instr.creates_value():
                    continue

                # Check for non-static modification of the stack pointer register family
                if Instruction.get_operand_register_family(cur_instr.op1) == 7:  # RSP, ESP family number
                    if (cur_instr.op2 is None and cur_instr.opcode not in ["inc", "dec", "pop"]) or \
                       (cur_instr.op2 is not None and not Instruction.is_constant(cur_instr.op2)):
                        return True
        return False
Пример #14
0
    def check_sp_target_of_operation(self):
        """
        :return void: Increases gadget's score if the gadget has an intermediate instruction that performs certain
                      operations on the stack pointer register family.
        """
        # Scan instructions to determine if they modify the stack pointer register family
        for i in range(len(self.instructions)-1):
            cur_instr = self.instructions[i]

            # Ignore instructions that do not create values
            if not cur_instr.creates_value():
                continue

            # Increase score by 4 for move, load address, and exchange ops, 3 for shift/rotate ops, and 2 for others
            if Instruction.get_operand_register_family(cur_instr.op1) == 7:    # RSP, ESP family number
                if "xchg" in cur_instr.opcode or "mov" in cur_instr.opcode or cur_instr.opcode in ["lea"]:
                    self.score += 4.0
                elif cur_instr.opcode in ["shl", "shr", "sar", "sal", "ror", "rol", "rcr", "rcl"]:
                    self.score += 3.0
                elif cur_instr.opcode == "pop":
                    self.score += 1.0
                else:
                    self.score += 2.0   # Will be a static modification, otherwise it would have been rejected earlier