Esempio n. 1
0
def validate_integer_argument(pid,
                              syscall_object,
                              trace_arg,
                              exec_arg,
                              params=None):
    logging.debug(
        'Validating integer argument (trace position: %d '
        'execution position: %d)', trace_arg, exec_arg)
    # EAX is the system call number
    POS_TO_REG = {
        0: cint.EBX,
        1: cint.ECX,
        2: cint.EDX,
        3: cint.ESI,
        4: cint.EDI
    }
    if not params:
        arg = cint.peek_register(pid, POS_TO_REG[exec_arg])
    else:
        arg = params[exec_arg]
    arg_from_trace = int(syscall_object.args[trace_arg].value)
    logging.debug('Argument from execution: %d', arg)
    logging.debug('Argument from trace: %d', arg_from_trace)
    # Check to make sure everything is the same
    # Decide if this is a system call we want to replay
    if arg_from_trace != arg:
        raise ReplayDeltaError('Argument value at trace position: {}, '
                               'execution position: {} from execution  ({}) '
                               'differs argument value from trace ({})'.format(
                                   trace_arg, exec_arg, arg, arg_from_trace))
Esempio n. 2
0
def subcall_return_success_handler(syscall_id, syscall_object, pid):
    '''This probably should be here.  This badly named handler simply takes a
    socket subcall situation, validates the file descriptor involved, no-ops
    the call, and applies the return conditions from the system call object.
    For several socket calls, this is all that's required so we don't need to
    write individual handlers that all do the same thing.

    TODO: Move this to generic_handlers module
    TODO: Replace parameter extraction with call to
    extract_socketcall_parameters

    '''
    logging.debug('Entering subcall return success handler')
    if syscall_object.ret[0] == -1:
        logging.debug('Handling unsuccessful call')
    else:
        logging.debug('Handling successful call')
        ecx = cint.peek_register(pid, cint.ECX)
        logging.debug('Extracting parameters from address %s', ecx)
        params = extract_socketcall_parameters(pid, ecx, 1)
        fd = params[0]
        fd_from_trace = syscall_object.args[0].value
        logging.debug('File descriptor from execution: %s', fd)
        logging.debug('File descriptor from trace: %s', fd_from_trace)
        if fd != int(fd_from_trace):
            raise ReplayDeltaError(
                'File descriptor from execution ({}) '
                'differs from file descriptor from trace'.format(
                    fd, fd_from_trace))
    noop_current_syscall(pid)
    apply_return_conditions(pid, syscall_object)
Esempio n. 3
0
def socketcall_handler(syscall_id, syscall_object, entering, pid):
    ''' Validate the subcall (NOT SYSCALL!) id of the socket subcall against
    the subcall name we expect based on the current system call object.  Then,
    hand off responsibility to the appropriate subcall handler.

    TODO: rename to handle_socketcall and correct references as needed

    '''
    subcall_handlers = {
        ('socket', True): socket_subcall_entry_handler,
        ('socket', False): socket_exit_handler,
        ('accept', True): accept_subcall_entry_handler,
        ('accept', False): accept_subcall_entry_handler,
        ('bind', True): bind_entry_handler,
        ('bind', False): bind_exit_handler,
        ('listen', True): listen_entry_handler,
        ('listen', False): listen_exit_handler,
        ('recv', True): recv_subcall_entry_handler,
        ('recvfrom', True): recvfrom_subcall_entry_handler,
        ('setsockopt', True): setsockopt_entry_handler,
        ('send', True): send_entry_handler,
        ('send', False): send_exit_handler,
        ('connect', True): connect_entry_handler,
        ('connect', False): connect_exit_handler,
        ('getsockopt', True): getsockopt_entry_handler,
        # ('sendmmsg', True): sendmmsg_entry_handler,
        ('sendto', True): sendto_entry_handler,
        ('sendto', False): sendto_exit_handler,
        ('shutdown', True): shutdown_subcall_entry_handler,
        ('recvmsg', True): recvmsg_entry_handler,
        ('recvmsg', False): recvmsg_exit_handler,
        ('getsockname', True): getsockname_entry_handler,
        ('getsockname', False): getsockname_exit_handler,
        ('getpeername', True): getpeername_entry_handler
    }
    # The subcall id of the socket subcall is located in the EBX register
    # according to our Linux's convention.
    subcall_id = cint.peek_register(pid, cint.EBX)
    validate_subcall(subcall_id, syscall_object)
    try:
        subcall_handlers[(syscall_object.name, entering)](syscall_id,
                                                          syscall_object, pid)
    except KeyError:
        raise NotImplementedError('No handler for socket subcall %s %s',
                                  syscall_object.name,
                                  'entry' if entering else 'exit')
Esempio n. 4
0
def validate_address_argument(pid,
                              syscall_object,
                              trace_arg,
                              exec_arg,
                              params=None):
    logging.debug(
        'Validating address argument (trace position: %d '
        'execution position: %d)', trace_arg, exec_arg)
    if not params:
        arg = cint.peek_register(pid, _pos_to_reg(exec_arg))
    else:
        arg = params[exec_arg]
    # Convert signed interpretation from peek register to unsigned
    arg = arg & 0xffffffff
    arg_from_trace = int(syscall_object.args[trace_arg].value, 16)
    if arg_from_trace != arg:
        raise ReplayDeltaError('Argument value at trace position: {}, '
                               'execution position: {} from execution  ({}) '
                               'differs argument value from trace ({})'.format(
                                   trace_arg, exec_arg, arg, arg_from_trace))
Esempio n. 5
0
def noop_current_syscall(pid):
    ''''No-op' out the current system call the child process is trying to
    execute by replacing it with a call to getpid() (a system call that takes
    no parameters and has no side effects).  Then, configure ptrace to allow
    the child process to run until it exits this call to getpid() and tell our
    own process to wait for this notification.  Set the entering flip-flip flag
    to to show that we are exiting a system call (because the child application
    now believes the system call it tried to make completed successfully).

    When this function is called from a handler, the handler needs to deal with
    setting up the output buffers and return value that the system call would
    have done itself had we allowed it to run normally.

    Note: This function leaves the child process in a state of waiting at the
    point just before execution returns to userspace code.

    '''
    logging.debug('Nooping the current system call in pid: %s', pid)
    # Transform the current system call in the child process into a call to
    # getpid() by poking 20 into ORIG_EAX
    cint.poke_register(pid, cint.ORIG_EAX, 20)
    # Tell ptrace we want the child process to stop at the next system call
    # event and restart its execution.
    cint.syscall(pid)
    # Have our process monitor the execution of the child process until it
    # receives a system call event notification.  The notification we receive
    # at this point (if all goes according to plan) is the EXIT notification
    # for the getpid() call we forced the application to make.
    next_syscall()
    # Take a look at the current system call (i.e. the one that triggered the
    # notification we just received from ptrace).  It should be getpid().  If
    # it isnt, something has gone horribly wrong and we must bail out.
    skipping = cint.peek_register(pid, cint.ORIG_EAX)
    if skipping != 20:
        raise Exception(
            'Nooping did not result in getpid exit. Got {}'.format(skipping))
    # Because we are exiting the getpid() call so we need to set the entering
    # flip-flop flag to reflect this.  This allows later code (in main.py) to
    # set it BACK to entering before we begin processing the entry for the next
    # system call.
    tracereplay.entering_syscall = False
Esempio n. 6
0
def swap_trace_fd_to_execution_fd(pid, pos, syscall_object, params_addr=None):
    POS_TO_REG = {
        0: cint.EBX,
        1: cint.ECX,
        2: cint.EDX,
        3: cint.ESI,
        4: cint.EDI,
    }
    logging.debug('Cleaning up file descriptor at position: {}'.format(pos))
    trace_fd = int(syscall_object.args[pos].value)
    looked_up_fd = fd_pair_for_trace_fd(trace_fd)['os_fd']
    if params_addr:
        params = extract_socketcall_parameters(pid, params_addr, pos + 1)
        execution_fd = params[pos]
    else:
        execution_fd = cint.peek_register(pid, POS_TO_REG[pos])
    logging.debug(
        'Replacing old value (trace fd): {} with new value: {}'.format(
            execution_fd, looked_up_fd))
    if params_addr:
        update_socketcall_paramater(pid, params_addr, pos, looked_up_fd)
    else:
        cint.poke_register(pid, POS_TO_REG[pos], looked_up_fd)
Esempio n. 7
0
def handle_syscall(syscall_id, syscall_object, entering, pid):
    ''' Validate the id of the system call against the name of the system call
    we are expecting based on the current system call object.  Then hand off
    responsiblity to the appropriate subcall handler.
    TODO: cosmetic - Reorder handler entrys numerically
    

    '''
    logging.debug('Handling syscall')
    # If we are entering a system call, update the number of system calls we
    # have handled
    if entering:
        tracereplay.handled_syscalls += 1
    # System call id 102 corresponds to 'socket subcall'.  This system call is
    # the entry point for code calls the appropriate socketf code based on the
    # subcall id in EBX.
    if syscall_id == 102:
        logging.debug('This is a socket subcall')
        # TODO: delete this logging
        ebx = cint.peek_register(pid, cint.EBX)
        logging.debug('Socketcall id from EBX is: %s', ebx)

        # Hand off to code that deals with socket calls and return once that is
        # complete.  Exceptions will be thrown if something is unsuccessful
        # that end.  Return immediately after because we don't want our system
        # call handler code double-handling the already handled socket subcall
        socketcall_handler(syscall_id, syscall_object, entering, pid)
        return
    logging.debug('Checking syscall against execution')
    validate_syscall(orig_eax, syscall_object)
    # We ignore these system calls because they have to do with aspecs of
    # execution that we don't want to try to replay and, at the same time,
    # don't have interesting information that we want to validate with a
    # handler.
    ignore_list = [
        77,  # sys_getrusage
        162,  # sys_nanosleep
        125,  # sys_mprotect
        175,  # sys_rt_sigprocmask
        116,  # sys_sysinfo
        119,  # sys_sigreturn
        126,  # sys_sigprocmask
        186,  # sys_sigaltstack
        266,  # set_clock_getres
        240,  # sys_futex
        242,  # sys_sched_getaffinity
        243,  # sys_set_thread_area
        311,  # sys_set_robust_list
        340,  # sys_prlimit64
        191,  # !!!!!!!!! sys_getrlimit
    ]
    handlers = {
        (8, True):
        creat_entry_handler,
        (8, False):
        check_return_value_exit_handler,
        # These calls just get their return values checked ####
        # (9, True): check_return_value_entry_handler,
        # (9, False): check_return_value_exit_handler,
        (12, True):
        syscall_return_success_handler,
        # (195, True): check_return_value_entry_handler,
        # (195, False): check_return_value_exit_handler,
        (39, True):
        check_return_value_entry_handler,
        (39, False):
        check_return_value_exit_handler,
        (45, True):
        check_return_value_entry_handler,
        (45, False):
        check_return_value_exit_handler,
        (91, True):
        check_return_value_entry_handler,
        (91, False):
        check_return_value_exit_handler,
        # (125, True): check_return_value_entry_handler,
        # (125, False): check_return_value_exit_handler,
        # mmap2 calls are never replayed. Sometimes we must fix a file
        # descriptor  in position 4.
        (192, True):
        mmap2_entry_handler,
        (192, False):
        mmap2_exit_handler,
        (196, True):
        lstat64_entry_handler,
        (10, True):
        unlink_entry_handler,
        (10, False):
        check_return_value_exit_handler,
        (20, True):
        syscall_return_success_handler,
        (30, True):
        syscall_return_success_handler,
        (38, True):
        rename_entry_handler,
        (38, False):
        check_return_value_exit_handler,
        (15, True):
        syscall_return_success_handler,
        (78, True):
        gettimeofday_entry_handler,
        (13, True):
        time_entry_handler,
        (27, True):
        syscall_return_success_handler,
        (5, True):
        open_entry_handler,
        (5, False):
        open_exit_handler,
        (60, True):
        syscall_return_success_handler,
        (85, True):
        readlink_entry_handler,
        (93, True):
        ftruncate_entry_handler,
        (93, False):
        ftruncate_exit_handler,
        (94, True):
        fchmod_entry_handler,
        (94, False):
        check_return_value_entry_handler,
        (145, True):
        readv_entry_handler,
        (145, False):
        check_return_value_exit_handler,
        (146, True):
        writev_entry_handler,
        (146, False):
        writev_exit_handler,
        (197, True):
        fstat64_entry_handler,
        (197, False):
        check_return_value_exit_handler,
        (122, True):
        uname_entry_handler,
        (183, True):
        getcwd_entry_handler,
        (140, True):
        llseek_entry_handler,
        (140, False):
        llseek_exit_handler,
        (42, True):
        pipe_entry_handler,
        # (43, True): times_entry_handler,
        # (10, True): syscall_return_success_handler,
        (33, True):
        syscall_return_success_handler,
        (199, True):
        syscall_return_success_handler,
        (200, True):
        syscall_return_success_handler,
        (201, True):
        syscall_return_success_handler,
        (202, True):
        syscall_return_success_handler,
        (4, True):
        write_entry_handler,
        (4, False):
        write_exit_handler,
        (3, True):
        read_entry_handler,
        (3, False):
        check_return_value_exit_handler,
        (6, True):
        close_entry_handler,
        (6, False):
        close_exit_handler,
        (168, True):
        poll_entry_handler,
        (54, True):
        ioctl_entry_handler,
        (54, False):
        ioctl_exit_handler,
        (195, True):
        stat64_entry_handler,
        (195, False):
        check_return_value_exit_handler,
        (141, True):
        getdents_entry_handler,
        (142, False):
        getdents_exit_handler,
        (142, True):
        select_entry_handler,
        (82, True):
        select_entry_handler,
        (221, True):
        fcntl64_entry_handler,
        (196, True):
        lstat64_entry_handler,
        (268, True):
        statfs64_entry_handler,
        (265, True):
        clock_gettime_entry_handler,
        (41, True):
        dup_entry_handler,
        (41, False):
        dup_exit_handler,
        (150, True):
        syscall_return_success_handler,
        (174, True):
        rt_sigaction_entry_handler,
        (186, True):
        sigaltstack_entry_handler,
        (194, True):
        ftruncate64_entry_handler,
        (194, False):
        ftruncate64_entry_handler,
        (207, True):
        fchown_entry_handler,
        (207, False):
        check_return_value_entry_handler,
        (209, True):
        getresuid_entry_handler,
        (211, True):
        getresgid_entry_handler,
        (220, True):
        getdents64_entry_handler,
        (220, False):
        getdents64_exit_handler,
        (228, True):
        fsetxattr_entry_handler,
        (228, False):
        fsetxattr_exit_handler,
        (231, True):
        fgetxattr_entry_handler,
        (231, False):
        fgetxattr_exit_handler,
        (234, True):
        flistxattr_entry_handler,
        (234, False):
        flistxattr_entry_handler,
        (242, True):
        sched_getaffinity_entry_handler,
        (243, True):
        syscall_return_success_handler,
        (258, True):
        set_tid_address_entry_handler,
        (258, False):
        set_tid_address_exit_handler,
        (259, True):
        timer_create_entry_handler,
        (260, True):
        timer_settime_entry_handler,
        (261, True):
        timer_gettime_entry_handler,
        (263, True):
        timer_delete_entry_handler,
        (265, True):
        clock_gettime_entry_handler,
        (271, True):
        syscall_return_success_handler,
        (272, True):
        fadvise64_64_entry_handler,
        (272, False):
        check_return_value_exit_handler,
        (295, True):
        openat_entry_handler,
        (295, False):
        openat_exit_handler,
        (300, True):
        fstatat64_entry_handler,
        (300, False):
        check_return_value_exit_handler,
        (301, True):
        unlinkat_entry_handler,
        (301, False):
        check_return_value_exit_handler,
        (311, True):
        syscall_return_success_handler,
        (320, True):
        utimensat_entry_handler,
        (320, False):
        check_return_value_exit_handler,
        (328, True):
        eventfd2_entry_handler,
        (340, True):
        prlimit64_entry_handler,
        (345, True):
        sendmmsg_entry_handler,
        (345, False):
        sendmmsg_exit_handler
    }
    if syscall_id not in ignore_list:
        try:
            handlers[(syscall_id, entering)](syscall_id, syscall_object, pid)
        except KeyError:
            raise NotImplementedError('Encountered un-ignored syscall {} '
                                      'with no handler: {}({})'.format(
                                          'entry' if entering else 'exit',
                                          syscall_id, syscall_object.name))
Esempio n. 8
0
        t = Trace.Trace(trace)
        tracereplay.system_calls = t.syscalls
        logging.info('Parsed trace with %s syscalls', len(t.syscalls))
        logging.info('Entering syscall handling loop')

        # Loop until we are no longer receiving syscall notifications from our
        # ptrace session with the child process.
        while next_syscall():
            # Get the system call id of the current system call.  Convention in
            # our flavor of Linux is for this to be passed in the EAX
            # register (ORIG_EAX, in ptrace terms).  Ptrace does not inform us
            # of whether the current system call action we have been notified
            # of is an entry or exit so we operate on the assumption that the
            # first notification we receive is an entry, the next is an exit,
            # the next is an entry etc.
            orig_eax = cint.peek_register(pid, cint.ORIG_EAX)
            logging.info('===')
            logging.info('Advanced to next system call')
            logging.info('System call id from execution: %d', orig_eax)
            logging.info('Looked up system call name: %s', SYSCALLS[orig_eax])
            logging.info('This is a system call %s',
                         'entry' if tracereplay.entering_syscall else 'exit')
            # This if statement is an ugly hack.  We skip these system calls
            # out of sequence because they do not result in a corresponding
            # 'system call exit' notification from ptrace meaning they throw
            # off the pattern of 'entry', 'exit', 'entry', 'exit'... that we
            # rely on for determining whether the ptrace message we have
            # received is a system call entry or exit.
            if SYSCALLS[orig_eax] == 'sys_exit_group' or \
               SYSCALLS[orig_eax] == 'sys_execve' or \
               SYSCALLS[orig_eax] == 'sys_exit':