简体   繁体   中英

Ptrace options not working in parent process

Ptrace options dont set the correct status when the desired systemcalls resume in the parent process. I can only use whats seen here no PEEKUSER, SYSGOOD or SYSCALL. Ive read the ptrace man and looked for examples for the past few days, Im mentally exhausted.

Any ideas/tips, no matter how small, are welcome. thanks.

argument: /bin/bash -c "echo 'first test' | wc -c"

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ptrace.h>

int wait_for_syscall(pid_t child) {
    int status;
    while (1) {
        ptrace(PTRACE_CONT, child, 0, 0);
        waitpid(child, &status, 0);
        if (WIFSTOPPED(status) && WSTOPSIG(status) | 0x80)
            return 0;
        if (WIFEXITED(status))
            return 1;
    }
}

int main(int argc, char *argv[]) {
    int status;
    int counter = 0;
    pid_t pid = fork();
    if (pid < 0)
        exit(1);
    else if (pid == 0) {
        ptrace(PTRACE_TRACEME, pid, NULL, NULL);
        raise(SIGSTOP);
        return execve(argv[1], &argv[1], NULL);
    } else {
        wait(&status);
        ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACESECCOMP);

        while (1) {
            if (wait_for_syscall(pid) != 0) break;
            
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8)))
                counter++;

            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)))
                counter++;

            if (wait_for_syscall(pid) != 0) break;
        }
    }
    return 0;
}

In your primary loop, you use a status that is scoped to main .

This is not the same status you are setting in wait_for_syscall (which has its own private copy of status ).

So, the status in main is never updated.


To fix, you can pass a pointer to wait_for_syscall

I've left it as is, but I believe that the second call to wait_for_syscall in the loop is extraneous and may actually silently absorb a syscall.

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ptrace.h>

int
wait_for_syscall(pid_t child,int *stp)
{
    int status;

    while (1) {
        ptrace(PTRACE_CONT, child, 0, 0);

        waitpid(child, &status, 0);
        *stp = status;

        if (WIFSTOPPED(status) && WSTOPSIG(status) | 0x80)
            return 0;
        if (WIFEXITED(status))
            return 1;
    }
}

int
main(int argc, char *argv[])
{
    int status;
    int counter = 0;

    pid_t pid = fork();
    if (pid < 0)
        exit(1);

    if (pid == 0) {
        ptrace(PTRACE_TRACEME, pid, NULL, NULL);
        raise(SIGSTOP);
        return execve(argv[1], &argv[1], NULL);
    }

    wait(&status);
    ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACESECCOMP | PTRACE_O_TRACECLONE);

    while (1) {
        if (wait_for_syscall(pid,&status) != 0)
            break;

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8)))
            counter++;

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)))
            counter++;

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)))
            counter++;

        if (wait_for_syscall(pid,&status) != 0)
            break;
    }

    return 0;
}

UPDATE:

I see. Its close bc now it only does one iteration in the while then it stops. So it doesnt continue/go to the next systemcall. :( – Olivia22

Okay, I've played with some variations a bit. Here's some changes:

  1. PTRACE_O_TRACEFORK is needed in addition to PTRACE_O_TRACECLONE (this is a big deal ;-)

  2. Use execvp to preserve environment variables (eg PATH )

  3. waitpid should get -1 for pid to catch subchildren, etc.

  4. I added debug printing ( dbgprt )

  5. Added signo to the ptrace(PTRACE_CONT,...)

Here's the refactored code (with lots of debug):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ptrace.h>

#define SHOW(_sym) \
    dbgprt(#_sym ": %8.8X %8.8X\n",_sym,SIGTRAP | (_sym << 8))

#define dbgprt(_fmt...) \
    do { \
        int sverr = errno; \
        printf(_fmt); \
        errno = sverr; \
    } while (0)

#define ONERR(_expr) \
    do { \
        if (! (_expr)) \
            break; \
        dbgprt("ONERR " #_expr " line %d -- %s\n",__LINE__,strerror(errno)); \
        exit(1); \
    } while (0)

int
main(int argc, char *argv[])
{
    int status;
    int counter = 0;
    long err;

    pid_t pidm = getpid();

    pid_t pid0 = fork();
    if (pid0 < 0)
        exit(1);

    if (pid0 == 0) {
        ptrace(PTRACE_TRACEME, pid0, NULL, NULL);
        //raise(SIGSTOP);
#if 0
        execve(argv[1], &argv[1], NULL);
#else
        execvp(argv[1], &argv[1]);
#endif
        exit(97);
    }

    dbgprt("main: pid0=%d/%d\n",pid0,pid0 - pidm);
    dbgprt("main: SIGSTOP %d\n",SIGSTOP);
    dbgprt("main: SIGTRAP %d\n",SIGTRAP);

    SHOW(PTRACE_EVENT_EXEC);
    SHOW(PTRACE_EVENT_SECCOMP);
    SHOW(PTRACE_EVENT_CLONE);

#if 0
    err = ptrace(PTRACE_ATTACH,pid0,0,0);
    ONERR(err < 0);
#endif

    err = waitpid(-1,&status,0);
    ONERR(err < 0);

#if 1
    unsigned int opt = 0;
    opt |= PTRACE_O_EXITKILL;
    opt |= PTRACE_O_TRACEEXEC;
    opt |= PTRACE_O_TRACESECCOMP;
    opt |= PTRACE_O_TRACECLONE;
    opt |= PTRACE_O_TRACEFORK;
    err = ptrace(PTRACE_SETOPTIONS, pid0, 0, opt);
    ONERR(err < 0);
#endif

    err = ptrace(PTRACE_CONT,pid0,0,0);
    ONERR(err < 0);

    while (1) {
#if 1
        pid_t pid = waitpid(-1, &status, 0);
#else
        pid_t pid = wait(&status);
#endif

        unsigned int evmsk = status >> 16;

        dbgprt("waitpid: pid=%d/%d status=%8.8X\n",pid,pid - pidm,status);
        dbgprt("waitpid: status %8.8X %8.8X evmsk=%8.8X\n",
            status >> 8,status & 0x7F,evmsk);

        if (pid < 0) {
            dbgprt("waitpid: errno=%d -- %s\n",errno,strerror(errno));
            break;
        }

        if (WIFEXITED(status)) {
            dbgprt("waitpid: WIFEXITED code=%d\n",WEXITSTATUS(status));
            continue;
        }

        int signo = 0;

        do {
            if (WIFSTOPPED(status)) {
                signo = WSTOPSIG(status);
                dbgprt("waitpid: WIFSTOPPED signo=%d\n",signo);
            }

            if (WIFSIGNALED(status)) {
                signo = WTERMSIG(status);
                dbgprt("waitpid: WIFSIGNALED signo=%d\n",signo);
            }
        } while (0);

#if 0
        if (WIFSTOPPED(status) && WSTOPSIG(status) | 0x80)
            return 0;
        if (WIFEXITED(status))
            return 1;
#endif

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
            dbgprt("EXEC\n");
            counter++;
        }

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
            dbgprt("SECCOMP\n");
            counter++;
        }

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
            dbgprt("CLONE\n");
            counter++;
        }

        dbgprt("main: counter=%d\n",counter);

        err = ptrace(PTRACE_CONT, pid, 0, signo);
        ONERR(err < 0);
    }

    return 0;
}

Here's the program invocation and output:

+ ./fix3 /bin/bash -c 'cat /bin/bash | wc -c'
main: pid0=1905216/1
main: SIGSTOP 19
main: SIGTRAP 5
PTRACE_EVENT_EXEC: 00000004 00000405
PTRACE_EVENT_SECCOMP: 00000007 00000705
PTRACE_EVENT_CLONE: 00000003 00000305
waitpid: pid=1905216/1 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=0
waitpid: pid=1905217/2 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905217/2 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=0
waitpid: pid=1905217/2 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=0
waitpid: pid=1905216/1 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=0
waitpid: pid=1905218/3 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905218/3 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=0
waitpid: pid=1905218/3 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=0
waitpid: pid=1905216/1 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=0
waitpid: pid=1905219/4 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905219/4 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=0
waitpid: pid=1905220/5 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905219/4 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=0
waitpid: pid=1905220/5 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=0
waitpid: pid=1905219/4 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=0
waitpid: pid=1905220/5 status=0004057F
waitpid: status 00000405 0000007F evmsk=00000004
waitpid: WIFSTOPPED signo=5
EXEC
main: counter=1
waitpid: pid=1905220/5 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905219/4 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=1
waitpid: pid=1905219/4 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=1
waitpid: pid=1905216/1 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=1
waitpid: pid=1905221/6 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=1
waitpid: pid=1905221/6 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=1
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=1
waitpid: pid=1905221/6 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=1
waitpid: pid=191190216
05222/7 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=1
waitpid: pid=1905222/7 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=1
waitpid: pid=1905221/6 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=1
waitpid: pid=1905222/7 status=0004057F
waitpid: status 00000405 0000007F evmsk=00000004
waitpid: WIFSTOPPED signo=5
EXEC
main: counter=2
waitpid: pid=1905222/7 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905221/6 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=2
waitpid: pid=1905221/6 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=2
waitpid: pid=1905216/1 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=2
waitpid: pid=1905223/8 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=2
waitpid: pid=1905223/8 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=2
waitpid: pid=1905216/1 status=0001057F
waitpid: status 00000105 0000007F evmsk=00000001
waitpid: WIFSTOPPED signo=5
main: counter=2
waitpid: pid=1905224/9 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=2
waitpid: pid=1905224/9 status=0000137F
waitpid: status 00000013 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=19
main: counter=2
waitpid: pid=1905223/8 status=0004057F
waitpid: status 00000405 0000007F evmsk=00000004
waitpid: WIFSTOPPED signo=5
EXEC
main: counter=3
waitpid: pid=1905224/9 status=0004057F
waitpid: status 00000405 0000007F evmsk=00000004
waitpid: WIFSTOPPED signo=5
EXEC
main: counter=4
waitpid: pid=1905223/8 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905224/9 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=1905216/1 status=0000117F
waitpid: status 00000011 0000007F evmsk=00000000
waitpid: WIFSTOPPED signo=17
main: counter=4
waitpid: pid=1905216/1 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: WIFEXITED code=0
waitpid: pid=-1/-1905216 status=00000000
waitpid: status 00000000 00000000 evmsk=00000000
waitpid: errno=10 -- No child processes

UPDATE #2:

Thanks alot, Ill go study it and see how it works. Just one thing what does if 0 and if 1 mean here? – Olivia22

For illustration here, I use cpp conditionals to denote old vs. new code:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

Sometimes, I'll put in some experimental code, but it will turn out to be "not so good". So, I'll wrap it in a #if 0 . I'll keep it for a while to remind myself that I did it and it didn't work (eg the PTRACE_ATTACH block).

The conditionals are quick way to "comment out" code that is cleaner than using (eg) a /* and */ pair.

When I'm done and I think the code is final, I'll remove the conditionals as appropriate.

An easy way to do this is to run the code through unifdef -k (and we get):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ptrace.h>

#define SHOW(_sym) \
    dbgprt(#_sym ": %8.8X %8.8X\n",_sym,SIGTRAP | (_sym << 8))

#define dbgprt(_fmt...) \
    do { \
        int sverr = errno; \
        printf(_fmt); \
        errno = sverr; \
    } while (0)

#define ONERR(_expr) \
    do { \
        if (! (_expr)) \
            break; \
        dbgprt("ONERR " #_expr " line %d -- %s\n",__LINE__,strerror(errno)); \
        exit(1); \
    } while (0)

int
main(int argc, char *argv[])
{
    int status;
    int counter = 0;
    long err;

    pid_t pidm = getpid();

    pid_t pid0 = fork();
    if (pid0 < 0)
        exit(1);

    if (pid0 == 0) {
        ptrace(PTRACE_TRACEME, pid0, NULL, NULL);
        //raise(SIGSTOP);
        execvp(argv[1], &argv[1]);
        exit(97);
    }

    dbgprt("main: pid0=%d/%d\n",pid0,pid0 - pidm);
    dbgprt("main: SIGSTOP %d\n",SIGSTOP);
    dbgprt("main: SIGTRAP %d\n",SIGTRAP);

    SHOW(PTRACE_EVENT_EXEC);
    SHOW(PTRACE_EVENT_SECCOMP);
    SHOW(PTRACE_EVENT_CLONE);

    err = waitpid(-1,&status,0);
    ONERR(err < 0);

    unsigned int opt = 0;
    opt |= PTRACE_O_EXITKILL;
    opt |= PTRACE_O_TRACEEXEC;
    opt |= PTRACE_O_TRACESECCOMP;
    opt |= PTRACE_O_TRACECLONE;
    opt |= PTRACE_O_TRACEFORK;
    err = ptrace(PTRACE_SETOPTIONS, pid0, 0, opt);
    ONERR(err < 0);

    err = ptrace(PTRACE_CONT,pid0,0,0);
    ONERR(err < 0);

    while (1) {
        pid_t pid = waitpid(-1, &status, 0);

        unsigned int evmsk = status >> 16;

        dbgprt("waitpid: pid=%d/%d status=%8.8X\n",pid,pid - pidm,status);
        dbgprt("waitpid: status %8.8X %8.8X evmsk=%8.8X\n",
            status >> 8,status & 0x7F,evmsk);

        if (pid < 0) {
            dbgprt("waitpid: errno=%d -- %s\n",errno,strerror(errno));
            break;
        }

        if (WIFEXITED(status)) {
            dbgprt("waitpid: WIFEXITED code=%d\n",WEXITSTATUS(status));
            continue;
        }

        int signo = 0;

        do {
            if (WIFSTOPPED(status)) {
                signo = WSTOPSIG(status);
                dbgprt("waitpid: WIFSTOPPED signo=%d\n",signo);
            }

            if (WIFSIGNALED(status)) {
                signo = WTERMSIG(status);
                dbgprt("waitpid: WIFSIGNALED signo=%d\n",signo);
            }
        } while (0);

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
            dbgprt("EXEC\n");
            counter++;
        }

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
            dbgprt("SECCOMP\n");
            counter++;
        }

        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
            dbgprt("CLONE\n");
            counter++;
        }

        dbgprt("main: counter=%d\n",counter);

        err = ptrace(PTRACE_CONT, pid, 0, signo);
        ONERR(err < 0);
    }

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM