def call(self, resolvable, arguments=(), abi=None, **kwargs): """Add a call to the ROP chain Arguments: resolvable(str,int): Value which can be looked up via 'resolve', or is already an integer. arguments(list): List of arguments which can be passed to pack(). Alternately, if a base address is set, arbitrarily nested structures of strings or integers can be provided. """ if self.migrated: log.error('Cannot append to a migrated chain') # If we can find a function with that name, just call it if isinstance(resolvable, str): addr = self.resolve(resolvable) elif hasattr(resolvable, 'name') and hasattr(resolvable, 'address'): addr = resolvable.address resolvable = str(resolvable.name) else: addr = resolvable resolvable = '' if addr: self.raw(Call(resolvable, addr, arguments, abi)) # Otherwise, if it is a syscall we might be able to call it elif not self._srop_call(resolvable, arguments): log.error('Could not resolve %r.' % resolvable)
def _srop_call(self, resolvable, arguments): # Check that the call is a valid syscall resolvable = 'SYS_' + resolvable.lower() syscall_number = getattr(constants, resolvable, None) if syscall_number is None: return False log.info_once("Using sigreturn for %r" % resolvable) # Find an int 0x80 or similar instruction we can use syscall_gadget = None syscall_instructions = srop.syscall_instructions[context.arch] for instruction in syscall_instructions: syscall_gadget = self.find_gadget([instruction]) if syscall_gadget: break else: log.error("Could not find any instructions in %r" % syscall_instructions) # Generate the SROP frame which would invoke the syscall with context.local(arch=self.elfs[0].arch): frame = srop.SigreturnFrame() frame.pc = syscall_gadget frame.syscall = syscall_number try: SYS_sigreturn = constants.SYS_sigreturn except AttributeError: SYS_sigreturn = constants.SYS_rt_sigreturn for register, value in zip(frame.arguments, arguments): if not isinstance(value, six.integer_types + (Unresolved,)): frame[register] = AppendedArgument(value) else: frame[register] = value # Set up a call frame which will set EAX and invoke the syscall call = Call('SYS_sigreturn', syscall_gadget, [SYS_sigreturn], abi.ABI.sigreturn()) self.raw(call) self.raw(frame) # We do not expect to ever recover after the syscall, as it would # require something like 'int 0x80; ret' which does not ever occur # in the wild. self.migrated = True return True
def ret2dlresolve(self, dlresolve): elf = next(elf for elf in self.elfs if elf.get_section_by_name(".plt")) elf_base = elf.address if elf.pie else 0 plt_init = elf.get_section_by_name(".plt").header.sh_addr + elf_base log.debug("PLT_INIT: %#x", plt_init) reloc_index = dlresolve.reloc_index real_args = dlresolve.real_args call = Call("[plt_init] " + dlresolve.symbol.decode(), plt_init, dlresolve.real_args, before=[reloc_index]) self.raw(call)
def __call__(self, id: Union[int, str], args: list, ret: bool = False) -> None: '''Making system calls without the massive overhead of SIGROP >>> context.arch = 'amd64' >>> r = ROP('./binary') >>> r.system_call(0x3b, ['/bin/sh', 0, 0]) >>> print(r.dump()) 0x0000: 0x41e4af pop rax; ret 0x0008: 0x3b 0x0010: 0x44a309 pop rdx; pop rsi; ret 0x0018: 0x0 [arg2] rdx = 0 0x0020: 0x0 [arg1] rsi = 0 0x0028: 0x401696 pop rdi; ret 0x0030: 0x40 [arg0] rdi = AppendedArgument(['/bin/sh'], 0x0) 0x0038: 0x4022b4 syscall 0x0040: b'/bin/sh\x00' Arguments: `id`: integer syscall number OR string identifier for the syscall if int: integer is used directly as register value for syscall if str: The syscall number will be resolved with `pwnlib.constants`. `args`: arguments to the syscall `ret`: Specifically use a 'syscall; ret' gadget for syscalls (instead of 'syscall') `ret` WILL NOT WORK unless you have the dev verison of pwntools installed. Returns: Nothing. Will raise errors if things go wrong. ''' # get the syscall gadget if ret: if parse_version(PWNLIB_VER) < parse_version('4.4.0dev0'): raise NotImplementedError( '"syscall; ret" gadgets are only available on the ' 'latest version of pwntools.') # pwnlib.rop.srop.syscall_instructions == {'amd64': ['syscall'], 'arm': ['svc 0'], ...} syscall = self.rop.find_gadget( [rop.srop.syscall_instructions[context.arch][0], 'ret']) else: # Can lazily use ROP's __getattr__ here syscall = self.rop.syscall if syscall is None: raise AttributeError("ROP unable to find syscall gadget") # write the syscall id, label = self.label(id) self.rop.raw( Call(label, syscall.address, [id] + args, ABI.syscall()))