download
#include <stdio.h>
#i nclude <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/syscall.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <wait.h>
#include <stdint.h>
#include <errno.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sched.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/prctl.h>
struct gei_user_msg {
int gei_fd;
int pid_fd;
pid_t pid;
};
static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
unsigned int flags)
{
return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
}
void finish_msg(struct gei_user_msg *msg) {
close(msg->pid_fd);
close(msg->gei_fd);
}
int get_procfd(struct gei_user_msg *msg) {
char procpath[20];
if (sprintf(procpath, "/proc/%d", msg->pid) < 0) {
finish_msg(msg);
return -1;
};
int procdir = open(procpath, O_DIRECTORY|O_RDONLY);
if (procdir < 0 || sys_pidfd_send_signal(msg->pid_fd, 0, NULL, 0) < 0) {
fprintf(stderr, "process died while working! %d\n", msg->pid);
if (procdir >= 0) { close(procdir); }
finish_msg(msg);
return -1;
}
return procdir;
}
#define JAVA_NAME1 "/java"
#define NOTJAVA_PREFIX "/nix/store/"
int check_path_is_java(char *exe, int len) {
return !strncmp(exe + len - strlen(JAVA_NAME1),
JAVA_NAME1, strlen(JAVA_NAME1)) &&
strncmp(exe, NOTJAVA_PREFIX, strlen(NOTJAVA_PREFIX));
}
int is_java(int procfd) {
char buf[PATH_MAX];
int ret = readlinkat(procfd, "exe", buf, PATH_MAX);
if (ret < 0) { return 1; }
if (ret == PATH_MAX) {
size_t bufsize = PATH_MAX;
char *buf = NULL;
while (ret == bufsize) {
if (buf) { free(buf); }
bufsize *= 2;
buf = malloc(bufsize);
ret = readlinkat(procfd, "exe", buf, bufsize);
if (ret < 0) { return 1; }
}
ret = check_path_is_java(buf, ret);
free(buf);
return ret;
}
return check_path_is_java(buf, ret);
}
void ptrace_log_and_kill(char *msg, int pid) {
perror(msg); sys_pidfd_send_signal(pid, 9, NULL, 0); exit(1);
}
struct inject_env_info {
char *env_str;
pid_t childpid;
};
char *find_proc_mountpoint(int procfd, struct gei_user_msg *msg) {
int mifd = openat(procfd, "mountinfo", O_RDONLY);
if (mifd < 0) { return NULL; }
char buf[BUFSIZ]; char *ptr = buf; size_t len = 0;
char *pathcopy = malloc(PATH_MAX); size_t pathsize = PATH_MAX;
size_t pathidx = 0;
char *ret = NULL;
int num_spcs_found = 0;
while (1) {
if (len == 0) {
len = read(mifd, &buf, BUFSIZ);
if (len == 0) { goto ret; }
if (len < 0 && errno != EINTR) { goto ret; }
ptr = buf;
continue;
}
if (*ptr == '\n') { num_spcs_found = 0; pathidx = 0; }
if (num_spcs_found == 4) {
if (pathidx == pathsize) {
pathsize = pathsize * 2;
pathcopy = realloc(pathcopy, pathsize);
}
pathcopy[pathidx++] = *ptr;
}
if (*ptr == ' ') {
++num_spcs_found;
if (pathidx) { pathcopy[pathidx-1] = '\0'; }
if (num_spcs_found == 8) {
while (len < 6) {
memmove(buf, ptr, len); ptr = buf;
len += read(mifd, &buf[len], BUFSIZ-len);
if (len == 0) { goto ret; }
if (len < 0 && errno != EINTR) { goto ret; }
}
if (!strncmp(ptr, " proc ", 6)) { ret = pathcopy; goto ret; }
}
}
ptr += 1;
len -= 1;
}
ret:
close(mifd);
return ret;
}
void sleep_forever(void) {
while (1) { pause(); }
}
pid_t inject_path_helper(int gei_fd, int procfd, struct gei_user_msg *msg,
int preload_fd, char *procmt) {
int pipefd[2];
if (pipe(pipefd) < 0) { return -1; }
pid_t cpid = fork();
if (cpid < 0) { return -1; }
if (cpid == 0) {
close(pipefd[0]);
setns(msg->pid_fd, CLONE_NEWPID|CLONE_NEWNS);
finish_msg(msg);
pid_t ccpid = fork();
if (ccpid < 0) { exit(1); }
struct stat stat;
int mypid = getpid();
if (fstat(procfd, &stat) < 0) { mypid = -1; };
close(procfd);
close(0);
close(1);
close(2);
close(gei_fd);
setresgid(stat.st_uid, stat.st_uid, stat.st_uid);
setresuid(stat.st_gid, stat.st_gid, stat.st_gid);
prctl(PR_SET_DUMPABLE, 1);
if (!ccpid) { write(pipefd[1], &mypid, sizeof(pid_t)); }
close(pipefd[1]);
if (mypid < 0) { exit(1); }
char *cmpp;
if (asprintf(&cmpp, "%s/%d/fd/%d", procmt, mypid, preload_fd) < 0) {
sleep_forever();
}
size_t cmplen = strlen(cmpp);
char *cmpp2 = malloc(cmplen);
if (!cmpp2) { sleep_forever(); }
while (1) {
sleep(1);
int procfdfd = openat(procfd, "fd", O_RDONLY);
if (procfdfd < 0) { sleep_forever(); }
int procfdfd2 = dup(procfdfd);
if (procfdfd2 < 0) { close(procfdfd); sleep_forever(); }
DIR *procfddir = fdopendir(procfdfd2);
if (!procfddir) { close(procfdfd); close(procfdfd2); sleep_forever(); }
struct dirent *ent;
while (ent = readdir(procfddir)) {
if (readlinkat(procfdfd, ent->d_name, cmpp2, cmplen) < 0) { continue; }
if (!strncmp(cmpp, cmpp2, cmplen)) { exit(0); }
}
closedir(procfddir);
close(procfdfd);
}
}
if (cpid > 0) {
close(pipefd[1]);
pid_t ret;
read(pipefd[0], &ret, sizeof(pid_t));
close(pipefd[0]);
return ret;
}
}
char *get_iei(int gei_fd, int procfd, struct gei_user_msg *msg,
int preload_fd) {
char *procmt = find_proc_mountpoint(procfd, msg);
pid_t pid = inject_path_helper(gei_fd, procfd, msg, preload_fd, procmt);
char *ret;
if (asprintf(&ret, "LD_PRELOAD=%s/%d/fd/%d", procmt, pid, preload_fd) < 0) {
return NULL;
}
return ret;
}
void inject_java(int gei_fd, int procfd, struct gei_user_msg *msg,
int preload_fd) {
char *to_inject = get_iei(gei_fd, procfd, msg, preload_fd);
if (!to_inject) {
fprintf(stderr, "inject string determination failed!\n");
sys_pidfd_send_signal(msg->pid_fd, 9, NULL, 0);
}
fprintf(stderr, "string to inject: '%s'\n", to_inject);
if (ptrace(PTRACE_ATTACH, msg->pid, NULL, NULL)) {
perror("ptrace failed!\n");
sys_pidfd_send_signal(msg->pid_fd, 9, NULL, 0);
finish_msg(msg);
return;
}
if (sys_pidfd_send_signal(msg->pid_fd, 0, NULL, 0)) {
fprintf(stderr, "Process died before trace %d\n", msg->pid);
finish_msg(msg); return;
}
finish_msg(msg);
int status;
while (1) {
while (msg->pid != waitpid(msg->pid, &status, 0)) {}
if (WIFEXITED(status) || WIFSIGNALED(status)) {
fprintf(stderr, "Process died before stopping %d\n", msg->pid);
return;
}
int sig = WSTOPSIG(status);
if (WIFSTOPPED(status) && sig == SIGSTOP) { break; }
if (WIFSTOPPED(status) && sig == SIGTRAP) { sig = 0; }
if (ptrace(PTRACE_CONT, msg->pid, NULL, (void*)(uintptr_t)sig)) {
fprintf(stderr, "Process died before stopping (2) %d\n", msg->pid);
return;
}
}
struct user_regs_struct regs;
if (ptrace(PTRACE_GETREGS, msg->pid, NULL, ®s)) {
ptrace_log_and_kill("PTRACE_GETREGS", msg->pid_fd); return;
}
long argc = ptrace(PTRACE_PEEKDATA, msg->pid, regs.rsp, 0);
if (errno) {
ptrace_log_and_kill("PTRACE_PEEKDATA argc", msg->pid_fd); return;
}
long argvn = ptrace(PTRACE_PEEKDATA, msg->pid, regs.rsp + 8*(argc + 1), 0);
if (errno || argvn) {
ptrace_log_and_kill("PTRACE_PEEKDATA argvn", msg->pid_fd); return;
}
size_t shift_offset = 8 + (strlen(to_inject) + 1);
shift_offset = (shift_offset & ~ 0xf) + 16 * !!(shift_offset & 0xf);
size_t env_entry_address = 0;
int n_zeros_found = 0;
int in_aux = 0;
int in_auxval = 1;
size_t cur_addr = regs.rsp;
regs.rsp = regs.rsp - shift_offset;
while (n_zeros_found < 3) {
long entry = ptrace(PTRACE_PEEKDATA, msg->pid, cur_addr, 0);
if (errno) {
ptrace_log_and_kill("PTRACE_PEEKDATA entry", msg->pid_fd); return;
}
if (ptrace(PTRACE_POKEDATA, msg->pid, cur_addr - shift_offset, entry)) {
ptrace_log_and_kill("PTRACE_POKEDATA entry", msg->pid_fd);
}
if (!entry && !(in_aux && in_auxval)) {
n_zeros_found++;
if (n_zeros_found == 1) {
shift_offset = shift_offset - 8;
env_entry_address = cur_addr - shift_offset;
}
if (n_zeros_found == 2) {
in_aux = 1;
}
}
cur_addr += 8;
if (in_aux) { in_auxval = !in_auxval; }
}
cur_addr = cur_addr - shift_offset;
if (ptrace(PTRACE_POKEDATA, msg->pid, env_entry_address, cur_addr)) {
ptrace_log_and_kill("PTRACE_POKEDATA envp", msg->pid_fd);
}
union {
char c[8];
size_t i;
} un;
ssize_t len = strlen(to_inject) + 1;
while (len > 0) {
strncpy(un.c, to_inject, 8);
if (ptrace(PTRACE_POKEDATA, msg->pid, cur_addr, un.i)) {
ptrace_log_and_kill("PTRACE_POKEDATA un.i", msg->pid_fd);
}
len -= 8;
cur_addr += 8;
to_inject += 8;
}
if (ptrace(PTRACE_SETREGS, msg->pid, NULL, ®s)) {
ptrace_log_and_kill("PTRACE_SETREGS", msg->pid_fd);
}
ptrace(PTRACE_DETACH, msg->pid, 0, NULL);
}
int main(int argc, char *argv[]) {
int preload_fd = open("preload.so", O_RDONLY);
if (preload_fd < 0) { perror("open(preload)"); return 1; }
int f = open("/dev/global_execve_interceptor", O_RDONLY);
if (f < 0) { perror("fopen"); return 1; }
struct gei_user_msg msg;
while (1) {
ssize_t bytes = read(f, &msg, sizeof(msg));
fprintf(stderr, "[%ld] %d %d %d\n", bytes, msg.gei_fd, msg.pid_fd, msg.pid);
int procfd = get_procfd(&msg);
if (procfd < 0) { continue; }
if (is_java(procfd)) {
inject_java(f, procfd, &msg, preload_fd);
} else {
finish_msg(&msg);
}
close(procfd);
}
}