pythonsystem-callsstraceptrace

Python3 Ptrace duplicate syscalls


I'm trying to build a simple version of starce, which shows you the first x syscalls a process made. The problem is that currently it seems like every syscall appears twice (except execve and exit_group).

This is the code I use to get the syscalls:

import os
import ctypes


# Get libc:
libc = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')


# Define Constants:

PTRACE_PEEKTEXT   = 1
PTRACE_PEEKDATA   = 2
PTRACE_POKETEXT   = 4
PTRACE_POKEDATA   = 5
PTRACE_CONT       = 7
PTRACE_SINGLESTEP = 9
PTRACE_GETREGS    = 12
PTRACE_SETREGS    = 13
PTRACE_ATTACH     = 16
PTRACE_DETACH     = 17
PTRACE_TRACEME    = 0
PTRACE_SYSCALL    = 24
PTRACE_PEEKUSER   = 32


class user_regs_struct(ctypes.Structure):
    _fields_ = [
        ("r15", ctypes.c_ulonglong),
        ("r14", ctypes.c_ulonglong),
        ("r13", ctypes.c_ulonglong),
        ("r12", ctypes.c_ulonglong),
        ("rbp", ctypes.c_ulonglong),
        ("rbx", ctypes.c_ulonglong),
        ("r11", ctypes.c_ulonglong),
        ("r10", ctypes.c_ulonglong),
        ("r9", ctypes.c_ulonglong),
        ("r8", ctypes.c_ulonglong),
        ("rax", ctypes.c_ulonglong),
        ("rcx", ctypes.c_ulonglong),
        ("rdx", ctypes.c_ulonglong),
        ("rsi", ctypes.c_ulonglong),
        ("rdi", ctypes.c_ulonglong),
        ("orig_rax", ctypes.c_ulonglong),
        ("rip", ctypes.c_ulonglong),
        ("cs", ctypes.c_ulonglong),
        ("eflags", ctypes.c_ulonglong),
        ("rsp", ctypes.c_ulonglong),
        ("ss", ctypes.c_ulonglong),
        ("fs_base", ctypes.c_ulonglong),
        ("gs_base", ctypes.c_ulonglong),
        ("ds", ctypes.c_ulonglong),
        ("es", ctypes.c_ulonglong),
        ("fs", ctypes.c_ulonglong),
        ("gs", ctypes.c_ulonglong),
    ]

ptrace = libc.ptrace
ptrace.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p]
ptrace.restype = ctypes.c_int



pid = 123 # whatever PID you need

syscalls = []
while len(syscalls) < 1024:
    try:
        _, status = os.waitpid(pid, os.WUNTRACED)
        if os.WIFEXITED(status):
            break
    except:
        break

    regs = user_regs_struct()
    ptrace(PTRACE_GETREGS, pid, 0, ctypes.byref(regs))
    syscalls.append(regs.orig_rax)

    ptrace(PTRACE_SYSCALL, pid, 0, 0)

And the first 16 syscalls I get for example when running on ls are these:
execve, brk, brk, arch_prctl, arch_prctl, access, access, openat, openat, fstat, fstat, mmap, mmap, close, close, openat
While if I run strace, the first 16 are these:
execve, brk, arch_prctl, access, openat, fstat, mmap, close, openat, read, fstat, mmap, mmap, mprotect, mmap, mmap

As you can see, my code outputs each syscall twice.
What can cause this? And how can it be fixed?


Solution

  • After digging a bit in other threads here, I found that every syscall is supposed to appear twice, once before it was called, and another time after it was called.
    So the solution will be to simply to add the syscall to the list only once every two iterations.