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
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
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
def check_negative_sp_offsets(self): """ :return void: Increases gadget's score if its cumulative register offsets are negative. """ sp_offset = 0 # Scan instructions to determine if they modify the stack pointer for i in range(len(self.instructions)): cur_instr = self.instructions[i] if cur_instr.opcode == "push": sp_offset -= 8 elif cur_instr.opcode == "pop" and cur_instr.op1 not in Instruction.register_families[7]: sp_offset += 8 elif cur_instr.opcode in ["add", "adc"] and cur_instr.op1 in Instruction.register_families[7] and \ Instruction.is_constant(cur_instr.op2): sp_offset += Instruction.get_operand_as_constant(cur_instr.op2) elif cur_instr.opcode in ["sub", "sbb"] and cur_instr.op1 in Instruction.register_families[7] and \ Instruction.is_constant(cur_instr.op2): sp_offset -= Instruction.get_operand_as_constant(cur_instr.op2) elif cur_instr.opcode == "inc" and cur_instr.op1 in Instruction.register_families[7]: sp_offset += 1 elif cur_instr.opcode == "dec" and cur_instr.op1 in Instruction.register_families[7]: sp_offset -= 1 elif cur_instr.opcode.startswith("ret") and cur_instr.op1 is not None: sp_offset += Instruction.get_operand_as_constant(cur_instr.op1) if sp_offset < 0: self.score += 2.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
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
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
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
def contains_intermediate_GPI(self): """ :return boolean: Returns True if the gadget's intermediate instructions contain a GPI (or a generic interrupt), False otherwise. """ for i in range(len(self.instructions)-1): cur_opcode = self.instructions[i].opcode cur_target = self.instructions[i].op1 if cur_opcode.startswith("ret") or \ cur_opcode == "syscall" or cur_opcode == "sysenter" or cur_opcode.startswith("int") or \ ("jmp" in cur_opcode and not Instruction.is_constant(cur_target)) or \ ("call" in cur_opcode and not Instruction.is_constant(cur_target)): return True return False
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
def contains_static_call(self): for i in range(1, len(self.instructions)-1): cur_instr = self.instructions[i] if cur_instr.opcode.startswith("call") and Instruction.is_constant(cur_instr.op1): return True return False
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
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
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
def has_invalid_ret_offset(self): """ :return boolean: Returns True if the gadget is 'ret' ending and contains a constant offset that is not byte aligned or is greater than 32 bytes, False otherwise """ last_instr = self.instructions[len(self.instructions)-1] if last_instr.opcode.startswith("ret") and last_instr.op1 is not None: offset = Instruction.get_operand_as_constant(last_instr.op1) if (offset % 2 != 0) or (offset > 32): return True return False
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
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
def __init__(self, raw_gadget): """ Gadget constructor :param str raw_gadget: raw line output from ROPgadget """ # Parse the raw line self.offset = raw_gadget[:raw_gadget.find(":")] self.instruction_string = raw_gadget[raw_gadget.find(":") + 2:] # Parse instruction objects self.instructions = [] for instr in self.instruction_string.split(" ; "): self.instructions.append(Instruction(instr)) # Initialize score self.score = 0.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
def classify_JOP_gadget(self, gadget): """ Analyzes a gadget to determine which expressivity classes it satisfies :param Gadget gadget: gadget to analyze :return: None, but modifies Gadget expressivity collections """ last_instr = gadget.instructions[len(gadget.instructions) - 1] op1 = last_instr.op1 op1_family = Instruction.get_word_operand_register_family(op1) if self.practical_ROP[3] is False: if "[" in op1 and op1_family in [ 0, 5 ] and "+" not in op1 and "-" not in op1 and "*" not in op1: self.practical_ROP[3] = True if self.practical_ROP[5] is False: if op1_family in [ 0, 5 ] and "+" not in op1 and "-" not in op1 and "*" not in op1: self.practical_ROP[5] = True if self.practical_ROP[7] is False: if "[" in op1 and op1_family in [ 0, 4, 5 ] and "+" not in op1 and "-" not in op1 and "*" not in op1: self.practical_ROP[7] = True if self.practical_ASLR_ROP[0] is False: if "[" not in op1 and op1_family not in [None, 6, 7]: self.practical_ASLR_ROP[0] = True if self.practical_ASLR_ROP[1] is False: if "[" in op1 and op1_family not in [ None, 6, 7 ] and "+" not in op1 and "-" not in op1 and "*" not in op1: self.practical_ASLR_ROP[1] = True
def classify_gadget(self, gadget): """ Analyzes a gadget to determine which expressivity classes it satisfies :param Gadget gadget: gadget to analyze :return: None, but modifies Gadget expressivity collections """ first_instr = gadget.instructions[0] opcode = first_instr.opcode op1 = first_instr.op1 op2 = first_instr.op2 op1_family = Instruction.get_word_operand_register_family(op1) op2_family = Instruction.get_word_operand_register_family(op2) # For performance, iterate through the expressivity classes and perform analysis. Analysis rules should # set as many classes as possible. if self.practical_ROP[0] is False: if opcode == "dec" and op1_family in [0, 5] and "[" not in op1: self.practical_ROP[0] = True # Also satisfies: self.turing_complete_ROP[0] = True self.practical_ASLR_ROP[9] = True if self.practical_ROP[1] is False: if opcode == "inc" and op1_family in [0, 5] and "[" not in op1: self.practical_ROP[1] = True # Also satisfies: self.turing_complete_ROP[0] = True self.practical_ASLR_ROP[8] = True if self.practical_ROP[2] is False: if opcode == "pop" and op1_family in [0, 5] and "[" not in op1: self.practical_ROP[2] = True # Also satisfies: self.turing_complete_ROP[1] = True self.practical_ASLR_ROP[5] = True if self.practical_ROP[3] is False: if (opcode == "pop" and op1_family == 4 and "[" not in op1) or \ (opcode in ["xchg", "move"] and op1_family == 4 and op2_family in [0, 5] and "[" not in op1 and "[" not in op2) or \ (opcode == "lea" and op1_family == 4 and op2_family in [0, 5] and "+" not in op2 and "-" not in op2 and "*" not in op2) or \ (opcode == "xchg" and op1_family in [0, 5] and op2_family == 4 and "[" not in op1 and "[" not in op2): self.practical_ROP[3] = True if self.practical_ROP[4] is False: if opcode == "xchg" and ((op1_family == 0 and op2_family == 5) or (op2_family == 0 and op1_family == 5)) \ and "[" not in op1 and "[" not in op2: self.practical_ROP[4] = True if self.practical_ROP[5] is False: if opcode == "push" and op1_family in [0, 4, 5] and "[" not in op1: self.practical_ROP[5] = True if self.practical_ROP[6] is False: if opcode in ["clc", "sahf"] or \ (opcode in ["test", "add", "adc", "sub", "sbb", "and", "or", "xor", "cmp"] and op1_family in [0, 4, 5] and op2_family in [0, 4, 5] and "[" not in op1 and "[" not in op2): self.practical_ROP[6] = True # Also satisfies: self.turing_complete_ROP[7] = True self.practical_ASLR_ROP[4] = True if self.practical_ROP[7] is False: if (opcode.startswith("stos") or opcode in ["mov", "add", "or"]) and "[" in op1 and "+" not in op1 and \ "-" not in op1 and "*" not in op1 and op1_family in [0, 4, 5] and op2_family in [0, 4, 5] and \ op1_family != op2_family: self.practical_ROP[7] = True # Also satisfies: self.turing_complete_ROP[6] = True self.practical_ASLR_ROP[2] = True if self.practical_ROP[8] is False: if (opcode.startswith("lods") or opcode in ["mov", "add", "adc", "sub", "sbb", "and", "or", "xor"]) and \ "[" in op2 and "+" not in op2 and "-" not in op2 and "*" not in op2 and op1_family in [0, 4, 5] and \ op2_family in [0, 4, 5] and op1_family != op2_family: self.practical_ROP[8] = True # Also satisfies: self.turing_complete_ROP[5] = True self.practical_ASLR_ROP[1] = True # NOTE: Single rule for two classes if self.practical_ROP[9] is False or self.practical_ASLR_ROP[ 7] is False: if opcode == "leave": self.practical_ROP[9] = True self.practical_ASLR_ROP[7] = True if self.practical_ROP[10] is False: if (opcode == "pop" and op1_family == 6 and "[" not in op1) or \ (opcode == "xchg" and op1_family is not None and op2_family is not None and op1_family != op2_family and (op1_family == 6 or op2_family == 6) and "[" not in op1 and "[" not in op2) or \ (opcode in ["add", "adc", "sub", "sbb"] and "[" not in op1 and op1_family == 6 and op2_family not in [None, 6] and "+" not in op2 and "-" not in op2 and "*" not in op2): self.practical_ROP[10] = True if self.turing_complete_ROP[0] is False: if opcode in ["inc", "dec"] and op1_family not in [None, 7] and "+" not in op1 and "-" not in op1 and \ "*" not in op1: self.turing_complete_ROP[0] = True if self.turing_complete_ROP[1] is False: if opcode == "pop" and op1_family not in [None, 7 ] and "[" not in op1: self.turing_complete_ROP[1] = True if self.turing_complete_ROP[2] is False: if opcode in ["add", "adc", "sub", "sbb"] and op1_family not in [None, 7] and "+" not in op1 and \ "-" not in op1 and "*" not in op1 and op2_family not in [None, 7] and "+" not in op2 and \ "-" not in op2 and "*" not in op2 and op1_family != op2_family: self.turing_complete_ROP[2] = True if self.turing_complete_ROP[3] is False: if (opcode == "xor" and op1_family not in [None, 7] and "+" not in op1 and "-" not in op1 and "*" not in op1 and op2_family not in [None, 7] and "+" not in op2 and "-" not in op2 and "*" not in op2 and op1_family != op2_family) or \ (opcode in ["neg", "not"] and op1_family not in [None, 7] and "+" not in op1 and "-" not in op1 and "*" not in op1): self.turing_complete_ROP[3] = True if self.turing_complete_ROP[4] is False: if opcode in ["and", "or"] and op1_family not in [None, 7] and "+" not in op1 and \ "-" not in op1 and "*" not in op1 and op2_family not in [None, 7] and "+" not in op2 and \ "-" not in op2 and "*" not in op2 and op1_family != op2_family: self.turing_complete_ROP[4] = True if self.turing_complete_ROP[5] is False: if (opcode.startswith("lods") or opcode in ["mov", "add", "adc", "sub", "sbb", "and", "or", "xor"]) and \ "[" in op2 and "+" not in op2 and "-" not in op2 and "*" not in op2 and \ op1_family not in [None, 7] and op2_family not in [None, 7] and op1_family != op2_family: self.turing_complete_ROP[5] = True if self.turing_complete_ROP[6] is False: if (opcode.startswith("stos") or opcode in ["mov", "add", "or"]) and "[" in op1 and "+" not in op1 and \ "-" not in op1 and "*" not in op1 and op1_family not in [None, 7] and op2_family not in [None, 7] and \ op1_family != op2_family: self.turing_complete_ROP[6] = True if self.turing_complete_ROP[7] is False: if opcode in ["clc", "sahf"] or \ (opcode in ["test", "add", "adc", "sub", "sbb", "and", "or", "xor", "cmp"] and op1_family not in [None, 7] and op2_family not in [None, 7] and "+" not in op1 and "-" not in op1 and "*" not in op1 and "+" not in op2 and "-" not in op2 and "*" not in op2 and op1_family != op2_family): self.turing_complete_ROP[7] = True if self.turing_complete_ROP[8] is False: if opcode in ["add", "adc", "sub", "sbb"] and "[" not in op2 and op2_family == 7 and \ op1_family not in [None, 7] and "+" not in op1 and "-" not in op1 and "*" not in op1: self.turing_complete_ROP[8] = True if self.turing_complete_ROP[9] is False: if (opcode == "pop" and op1_family == 7 and "[" not in op1) or \ (opcode == "xchg" and op1_family is not None and op2_family is not None and op1_family != op2_family and (op1_family == 7 or op2_family == 7) and "[" not in op1 and "[" not in op2) or \ (opcode in ["add", "adc", "sub", "sbb"] and "[" not in op1 and op1_family == 7 and op2_family not in [None, 7] and "+" not in op2 and "-" not in op2 and "*" not in op2): self.turing_complete_ROP[9] = True if self.turing_complete_ROP[10] is False: if opcode in ["lahf", "pushf"] or \ (opcode in ["adc", "sbb"] and op1_family not in [None, 7] and op2_family not in [None, 7] and "+" not in op1 and "-" not in op1 and "*" not in op1 and "+" not in op2 and "-" not in op2 and "*" not in op2 and op1_family != op2_family): self.turing_complete_ROP[10] = True # Next 6 classes have common and very specific requirements, check once if opcode == "xchg" and "[" not in op1 and "[" not in op2 and op1_family != op2_family: if self.turing_complete_ROP[11] is False: if op1_family in [0, 1] and op2_family in [0, 1]: self.turing_complete_ROP[11] = True if self.turing_complete_ROP[12] is False: if op1_family in [0, 2] and op2_family in [0, 2]: self.turing_complete_ROP[12] = True if self.turing_complete_ROP[13] is False: if op1_family in [0, 3] and op2_family in [0, 3]: self.turing_complete_ROP[13] = True if self.turing_complete_ROP[14] is False: if op1_family in [0, 6] and op2_family in [0, 6]: self.turing_complete_ROP[14] = True if self.turing_complete_ROP[15] is False: if op1_family in [0, 4] and op2_family in [0, 4]: self.turing_complete_ROP[15] = True if self.turing_complete_ROP[16] is False: if op1_family in [0, 5] and op2_family in [0, 5]: self.turing_complete_ROP[16] = True if self.practical_ASLR_ROP[0] is False: if opcode == "push" and op1_family not in [None, 6, 7 ] and "[" not in op1: self.practical_ASLR_ROP[0] = True if self.practical_ASLR_ROP[1] is False: if (opcode.startswith("lods") or opcode in ["mov", "add", "adc", "sub", "sbb", "and", "or", "xor"]) and \ "[" in op2 and "+" not in op2 and "-" not in op2 and "*" not in op2 and \ op1_family not in [None, 7] and op2_family not in [None, 6, 7] and op1_family != op2_family: self.practical_ASLR_ROP[1] = True if self.practical_ASLR_ROP[2] is False: if (opcode.startswith("stos") or opcode == "mov") and "[" in op1 and "+" not in op1 and \ "-" not in op1 and "*" not in op1 and op1_family not in [None, 7] and op2_family not in [None, 7] and \ op1_family != op2_family: self.practical_ASLR_ROP[2] = True if self.practical_ASLR_ROP[3] is False: if opcode in ["mov", "add", "adc", "and", "or", "xor"] and "[" not in op1 and "[" not in op2 and \ op1_family not in [None, 7] and op2_family == 7: self.practical_ASLR_ROP[3] = True if self.practical_ASLR_ROP[4] is False: if opcode in ["clc", "sahf"] or \ (opcode in ["test", "add", "adc", "sub", "sbb", "and", "or", "xor", "cmp"] and op1_family not in [None, 7] and op2_family not in [None, 7] and "[" not in op1 and "[" not in op2): self.practical_ASLR_ROP[4] = True if self.practical_ASLR_ROP[5] is False: if opcode == "pop" and op1_family in [0, 4, 5] and "[" not in op1: self.practical_ASLR_ROP[5] = True if self.practical_ASLR_ROP[6] is False: if opcode == "pop" and op1_family in [1, 2, 3, 6 ] and "[" not in op1: self.practical_ASLR_ROP[6] = True # NOTE class 8 (index 7) is combined above if self.practical_ASLR_ROP[8] is False: if opcode == "inc" and op1_family not in [None, 7 ] and "[" not in op1: self.practical_ASLR_ROP[8] = True if self.practical_ASLR_ROP[9] is False: if opcode == "dec" and op1_family not in [None, 7 ] and "[" not in op1: self.practical_ASLR_ROP[9] = True if self.practical_ASLR_ROP[10] is False: if opcode in ["add", "adc", "sub", "sbb"] and op1_family not in [None, 7] and "[" not in op1 and \ op2_family not in [None, 7] and "[" not in op2 and op1_family != op2_family: self.practical_ASLR_ROP[10] = True # For the next 24 classes, some fairly common gadgets will satisfy many classes and significant # overlap in definitions exists. Check these without first seeing if the class is satisfied # POP AX if opcode == "pop" and "[" not in op1 and op1_family == 0: self.practical_ASLR_ROP[13] = True self.practical_ASLR_ROP[17] = True self.practical_ASLR_ROP[21] = True self.practical_ASLR_ROP[25] = True self.practical_ASLR_ROP[29] = True self.practical_ASLR_ROP[33] = True # XCHG AX with another GPR if opcode == "xchg" and "[" not in op1 and "[" not in op2: if op1_family == 0: if op2_family == 1: self.practical_ASLR_ROP[11] = True self.practical_ASLR_ROP[12] = True self.practical_ASLR_ROP[13] = True self.practical_ASLR_ROP[14] = True elif op2_family == 2: self.practical_ASLR_ROP[15] = True self.practical_ASLR_ROP[16] = True self.practical_ASLR_ROP[17] = True self.practical_ASLR_ROP[18] = True elif op2_family == 3: self.practical_ASLR_ROP[19] = True self.practical_ASLR_ROP[20] = True self.practical_ASLR_ROP[21] = True self.practical_ASLR_ROP[22] = True elif op2_family == 6: self.practical_ASLR_ROP[23] = True self.practical_ASLR_ROP[24] = True self.practical_ASLR_ROP[25] = True self.practical_ASLR_ROP[26] = True elif op2_family == 4: self.practical_ASLR_ROP[27] = True self.practical_ASLR_ROP[28] = True self.practical_ASLR_ROP[29] = True self.practical_ASLR_ROP[30] = True elif op2_family == 5: self.practical_ASLR_ROP[31] = True self.practical_ASLR_ROP[32] = True self.practical_ASLR_ROP[33] = True self.practical_ASLR_ROP[34] = True elif op2_family == 0: if op1_family == 1: self.practical_ASLR_ROP[11] = True self.practical_ASLR_ROP[12] = True self.practical_ASLR_ROP[13] = True self.practical_ASLR_ROP[14] = True elif op1_family == 2: self.practical_ASLR_ROP[15] = True self.practical_ASLR_ROP[16] = True self.practical_ASLR_ROP[17] = True self.practical_ASLR_ROP[18] = True elif op1_family == 3: self.practical_ASLR_ROP[19] = True self.practical_ASLR_ROP[20] = True self.practical_ASLR_ROP[21] = True self.practical_ASLR_ROP[22] = True elif op1_family == 6: self.practical_ASLR_ROP[23] = True self.practical_ASLR_ROP[24] = True self.practical_ASLR_ROP[25] = True self.practical_ASLR_ROP[26] = True elif op1_family == 4: self.practical_ASLR_ROP[27] = True self.practical_ASLR_ROP[28] = True self.practical_ASLR_ROP[29] = True self.practical_ASLR_ROP[30] = True elif op1_family == 5: self.practical_ASLR_ROP[31] = True self.practical_ASLR_ROP[32] = True self.practical_ASLR_ROP[33] = True self.practical_ASLR_ROP[34] = True # MOV between AX and another GPR if opcode == "mov" and "[" not in op1 and "[" not in op2: if op1_family == 0: if op2_family == 1: self.practical_ASLR_ROP[13] = True self.practical_ASLR_ROP[14] = True elif op2_family == 2: self.practical_ASLR_ROP[17] = True self.practical_ASLR_ROP[18] = True elif op2_family == 3: self.practical_ASLR_ROP[21] = True self.practical_ASLR_ROP[22] = True elif op2_family == 6: self.practical_ASLR_ROP[25] = True self.practical_ASLR_ROP[26] = True elif op2_family == 4: self.practical_ASLR_ROP[29] = True self.practical_ASLR_ROP[30] = True elif op2_family == 5: self.practical_ASLR_ROP[33] = True self.practical_ASLR_ROP[34] = True elif op2_family == 0: if op1_family == 1: self.practical_ASLR_ROP[11] = True self.practical_ASLR_ROP[12] = True elif op1_family == 2: self.practical_ASLR_ROP[15] = True self.practical_ASLR_ROP[16] = True elif op1_family == 3: self.practical_ASLR_ROP[19] = True self.practical_ASLR_ROP[20] = True elif op1_family == 6: self.practical_ASLR_ROP[23] = True self.practical_ASLR_ROP[24] = True elif op1_family == 4: self.practical_ASLR_ROP[27] = True self.practical_ASLR_ROP[28] = True elif op1_family == 5: self.practical_ASLR_ROP[31] = True self.practical_ASLR_ROP[32] = True # ["add", "adc", "sub", "sbb", "and", "or", "xor"] between AX and another GPR if opcode in ["add", "adc", "sub", "sbb", "and", "or", "xor" ] and "[" not in op1 and "[" not in op2: if op1_family == 0: if op2_family == 1: self.practical_ASLR_ROP[14] = True elif op2_family == 2: self.practical_ASLR_ROP[18] = True elif op2_family == 3: self.practical_ASLR_ROP[22] = True elif op2_family == 6: self.practical_ASLR_ROP[26] = True elif op2_family == 4: self.practical_ASLR_ROP[30] = True elif op2_family == 5: self.practical_ASLR_ROP[34] = True elif op2_family == 0: if op1_family == 1: self.practical_ASLR_ROP[12] = True elif op1_family == 2: self.practical_ASLR_ROP[16] = True elif op1_family == 3: self.practical_ASLR_ROP[20] = True elif op1_family == 6: self.practical_ASLR_ROP[24] = True elif op1_family == 4: self.practical_ASLR_ROP[28] = True elif op1_family == 5: self.practical_ASLR_ROP[32] = True # Resume checks for individual classes 11, 15, 19, 23, 27, and 31. Others entirely checked entirely above if self.practical_ASLR_ROP[11] is False: if opcode == "pop" and "[" not in op1 and op1_family == 1: self.practical_ASLR_ROP[11] = True if self.practical_ASLR_ROP[15] is False: if opcode == "pop" and "[" not in op1 and op1_family == 2: self.practical_ASLR_ROP[15] = True if self.practical_ASLR_ROP[19] is False: if opcode == "pop" and "[" not in op1 and op1_family == 3: self.practical_ASLR_ROP[19] = True if self.practical_ASLR_ROP[23] is False: if opcode == "pop" and "[" not in op1 and op1_family == 6: self.practical_ASLR_ROP[23] = True if self.practical_ASLR_ROP[27] is False: if opcode == "pop" and "[" not in op1 and op1_family == 4: self.practical_ASLR_ROP[27] = True if self.practical_ASLR_ROP[31] is False: if opcode == "pop" and "[" not in op1 and op1_family == 5: self.practical_ASLR_ROP[31] = True