Source code for pwnypack.shellcode.translate

import inspect

import six

from pwnypack.shellcode.ops import SyscallInvoke, LoadRegister
from pwnypack.shellcode.types import Register, Offset, Buffer
from pwnypack import bytecode as bc


__all__ = ['translate', 'fragment']


class Fragment(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, env, *args, **kwargs):
        return translate(env, self.f, *args, **kwargs)


[docs]def fragment(f): """ Decorator to turn a function into a shellcode fragment that can be called as a function from within a translated function. Arguments: f(callable): The function to mark as a shellcode fragment. Returns: callable: The decorated shellcode fragment. """ return Fragment(f)
[docs]def translate(env, func, *args, **kwargs): """ Given a shellcode environment, a function and its parameters, translate the function to a list of shellcode operations ready to be compiled or assembled using :meth:`~pwnypack.shellcode.base.BaseEnvironment.compile` or :meth:`~pwnypack.shellcode.base.BaseEnvironment.assemble`. Arguments: env(~pwnypack.shellcode.base.Base): An instance of a shellcode environment. func(callable): The function to translate to shellcode. args(...): The positional arguments for the function. kwargs(...): The keyword arguments for the function. Returns: list: The high-level shellcode operations. """ func_code = six.get_function_code(func) func_globals = dict(__builtins__) func_globals.update(six.get_function_globals(func)) ops = bc.disassemble(func_code.co_code) program = [] f_args = inspect.getcallargs(func, *args, **kwargs) variables = dict( (func_code.co_varnames.index(arg_name), arg_value) for arg_name, arg_value in six.iteritems(f_args) ) stack = [] for op in ops: if op.name == 'LOAD_CONST': stack.append(func_code.co_consts[op.arg]) elif op.name == 'LOAD_GLOBAL': global_name = func_code.co_names[op.arg] stack.append(getattr(env, global_name, func_globals.get(global_name))) elif op.name == 'LOAD_FAST': var_name = func_code.co_varnames[op.arg] stack.append(getattr(env, var_name, variables.get(op.arg))) elif op.name == 'BUILD_LIST': items = stack[-op.arg:] del stack[-op.arg:] stack.append(items) elif op.name == 'LOAD_ATTR': obj = stack.pop() stack.append(getattr(obj, func_code.co_names[op.arg])) elif op.name == 'CALL_FUNCTION': nargs = op.arg & 0xff nkwargs = op.arg >> 8 if nkwargs: f_kwargs = dict(zip(stack[-nkwargs * 2::2], stack[-nkwargs * 2 + 1::2])) del stack[-nkwargs * 2:] else: f_kwargs = {} if nargs: f_args = stack[-nargs:] del stack[-nargs:] else: f_args = [] f = stack.pop() if isinstance(f, Fragment): stack.append(f(env, *f_args, **f_kwargs)) else: stack.append(f(*f_args, **f_kwargs)) elif op.name == 'STORE_FAST': value = stack.pop() var_name = func_code.co_varnames[op.arg] var = getattr(env, var_name, variables.get(op.arg, None)) if isinstance(var, Register): program.append(LoadRegister(var, value)) else: variables[op.arg] = value elif op.name == 'POP_TOP': value = stack.pop() if isinstance(value, SyscallInvoke): program.append(value) elif isinstance(value, list): program.extend(value) else: raise ValueError('No idea how to compile %s' % (value,)) elif op.name == 'RETURN_VALUE': stack.pop() elif op.name == 'DUP_TOP': value = stack[-1] if isinstance(value, SyscallInvoke): stack.insert(-1, env.SYSCALL_RET_REG) else: stack.append(value) elif op.name == 'BINARY_SUBSCR': index = stack.pop() value = stack.pop() stack.append(value[index]) elif op.name == 'STORE_SUBSCR': index = stack.pop() value = stack.pop() new_value = stack.pop() var = value[index] if isinstance(var, Register): program.append(LoadRegister(var, new_value)) else: value[index] = new_value elif op.name == 'INPLACE_ADD': value = stack.pop() reg = stack.pop() if not isinstance(reg, Register): raise TypeError('In-place addition is only supported on registers') program.extend(env.reg_add(reg, value)) stack.append(reg) elif op.name == 'INPLACE_SUBTRACT': value = stack.pop() reg = stack.pop() if not isinstance(reg, Register): raise TypeError('In-place subtraction is only supported on registers') program.extend(env.reg_sub(reg, value)) stack.append(reg) else: raise RuntimeError('Unsupported opcode: %s' % op.name) return program