#include #include #include #include #include #include #include "interfere.h" #include "util.h" struct process *proc_new(void) { struct process *handle=malloc(sizeof(struct process)); if (!handle) die("malloc: %s\n", strerror(errno)); return proc_init(handle); } struct process *proc_init(struct process *handle) { handle->pid=0; handle->phase=PHASE_INIT1; handle->tampered=0; handle->again=0; handle->sv=0; handle->retval=0; handle->error=0; handle->last_error=0; handle->mem=-1; handle->exitsig=0; return handle; } void proc_collect(struct process *handle) { switch(handle->phase) { case PHASE_COLLECT1: if (ptrace(PTRACE_GETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_getregs: %s\n", strerror(errno)); handle->retval=handle->regs[EAX]; handle->last_error=0; if (handle->retval<0) { handle->last_error=handle->error=-handle->retval; handle->retval=-1; } handle->again=0; handle->phase=PHASE_COLLECT2; break; case PHASE_COLLECT2: if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_GO; break; default: abort(); } } void proc_attach(struct process *handle, pid_t pid) { char filename[256]; if (handle->pid) die("proc_attach called with uninitialised handle\n"); handle->pid=pid; if (ptrace(PTRACE_ATTACH, pid, NULL, NULL)) die("ptrace_attach: %s\n", strerror(errno)); sprintf(filename, "/proc/%li/mem", (long)pid); if ((handle->mem = open(filename, O_RDWR))==-1) die("open %s: %s\n", filename, strerror(errno)); } void do_proc_detach(struct process *handle) { switch(handle->phase) { case PHASE_INIT0: case PHASE_INIT1: case PHASE_INIT2: case PHASE_GO: break; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } if (handle->tampered) { /* long retval=handle->origregs[ORIG_EAX]; */ /* if (retval==-EINTR) handle->origregs[EIP]-=2; */ if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->origregs)) die("ptrace_setregs: %s\n", strerror(errno)); handle->tampered=0; } if (ptrace(PTRACE_DETACH, handle->pid, NULL, handle->exitsig)) die("ptrace PTRACE_DETACH: %s\n", strerror(errno)); close(handle->mem); handle->mem=-1; handle->pid=0; handle->again=0; } void do_proc_syncphase(struct process *handle) { int i; switch(handle->phase) { case PHASE_INIT0: case PHASE_INIT1: /* Wait for a syscall */ handle->phase++; if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; break; case PHASE_INIT2: if (ptrace(PTRACE_GETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_getregs: %s\n", strerror(errno)); /* Wait for a syscall entry */ if (handle->regs[EAX] != 0xffffffda) /* -ENOSYS */ { if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); break; } for (i=0; iorigregs[i]=handle->regs[i]; handle->regs[EIP] -= 2; handle->stack = handle->regs[UESP]; handle->phase=PHASE_GO; handle->tampered=1; handle->again=0; break; default: abort(); } } void do_proc_advance(struct process *handle) { switch(handle->phase) { case PHASE_INIT0: case PHASE_INIT1: case PHASE_INIT2: do_proc_syncphase(handle); return; case PHASE_GO: break; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } if (handle->tampered) { if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->origregs)) die("ptrace_setregs: %s\n", strerror(errno)); handle->tampered=0; } handle->phase=PHASE_INIT0; do_proc_syncphase(handle); } void do_proc_write(struct process *handle, int fd, const void *buf, size_t len) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=4; /* write */ handle->regs[EBX]=fd; handle->regs[ECX]=proc_pushstackbuf(handle, buf, len); handle->regs[EDX]=len; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_read(struct process *handle, int fd, void *buf, size_t len) { handle->stack=handle->origregs[UESP]; switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=3; /* read */ handle->regs[EBX]=fd; handle->regs[ECX]=proc_pushstack(handle, len); handle->regs[EDX]=len; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: proc_copystackbuf(handle, buf, len, handle->regs[ECX]); case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_getcwd(struct process *handle, void *buf, size_t len) { handle->stack=handle->origregs[UESP]; switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=183; /* getcwd */ handle->regs[EBX]=proc_pushstack(handle, len); handle->regs[ECX]=len; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: proc_copystackbuf(handle, buf, len, handle->regs[EBX]); case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_open(struct process *handle, const char *pathname, int flags, mode_t mode) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=5; /* open */ handle->regs[EBX]=proc_pushstackbuf(handle, pathname, strlen(pathname)+1); handle->regs[ECX]=flags; handle->regs[EDX]=mode; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_close(struct process *handle, int fd) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=6; /* close */ handle->regs[EBX]=fd; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_dup(struct process *handle, int fd) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=41; /* dup */ handle->regs[EBX]=fd; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_dup2(struct process *handle, int oldfd, int newfd) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=63; /* dup2 */ handle->regs[EBX]=oldfd; handle->regs[ECX]=newfd; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_lseek(struct process *handle, int fd, off_t offset, int whence) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=19; /* lseek */ handle->regs[EBX]=fd; handle->regs[ECX]=offset; handle->regs[EDX]=whence; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_signal(struct process *handle, int signum, void (*handler)(int)) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=48; /* signal */ handle->regs[EBX]=signum; handle->regs[ECX]=(unsigned long)handler; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } struct kernel_stat_106 { unsigned short st106_dev; unsigned short __pad1; unsigned long st106_ino; unsigned short st106_mode; unsigned short st106_nlink; unsigned short st106_uid; unsigned short st106_gid; unsigned short st106_rdev; unsigned short __pad2; unsigned long st106_size; unsigned long st106_blksize; unsigned long st106_blocks; unsigned long st106_atime; unsigned long __unused1; unsigned long st106_mtime; unsigned long __unused2; unsigned long st106_ctime; unsigned long __unused3; unsigned long __unused4; unsigned long __unused5; }; #define STAT_COPY(field) buf->st_ ## field = statbuf.st106_ ## field void do_proc_fstat(struct process *handle, int fd, struct stat *buf) { struct kernel_stat_106 statbuf; switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=108; /* fstat */ handle->regs[EBX]=fd; handle->regs[ECX]=proc_pushstack(handle, sizeof(statbuf)); handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: proc_copystackbuf(handle, &statbuf, sizeof(statbuf), handle->regs[ECX]); STAT_COPY(dev); STAT_COPY(ino); STAT_COPY(mode); STAT_COPY(nlink); STAT_COPY(uid); STAT_COPY(gid); STAT_COPY(rdev); STAT_COPY(size); STAT_COPY(blksize); STAT_COPY(blocks); STAT_COPY(atime); STAT_COPY(mtime); STAT_COPY(ctime); handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } void do_proc_fcntl(struct process *handle, int fd, int cmd, long arg) { switch(handle->phase) { case PHASE_GO: handle->oldstack=handle->stack; handle->regs[ORIG_EAX]=55; /* fcntl */ handle->regs[EBX]=fd; handle->regs[ECX]=cmd; handle->regs[EDX]=arg; handle->regs[UESP]=handle->stack; if (ptrace(PTRACE_SETREGS, handle->pid, NULL, (void *)handle->regs)) die("ptrace_setregs: %s\n", strerror(errno)); if (ptrace(PTRACE_SYSCALL, handle->pid, NULL, 0)) die("ptrace_syscall: %s\n", strerror(errno)); handle->again=1; handle->phase=PHASE_COLLECT1; return; case PHASE_COLLECT1: handle->stack = handle->oldstack; case PHASE_COLLECT2: proc_collect(handle); return; default: abort(); } } unsigned long proc_pushstack(struct process *handle, size_t len) { handle->stack -= len; return handle->stack; } unsigned long proc_popstack(struct process *handle, size_t len) { handle->stack += len; return handle->stack; } unsigned long proc_pushstackbuf(struct process *handle, const void *buf, size_t len) { unsigned long sp=handle->stack; unsigned char sbuf[len+7], *ptr; unsigned int offset=(sp-len) & 3; unsigned long addr=(sp-len) & ~3; unsigned long finaladdr=sp & ~3; unsigned int finalindex=finaladdr-addr; unsigned int i; if (!len) return handle->stack; handle->stack -= len; *(unsigned long *)&sbuf[finalindex]=ptrace(PTRACE_PEEKDATA, handle->pid, finaladdr, NULL); for (i=0,ptr=sbuf+offset; ipid, addr+i, *(unsigned long *)&sbuf[i]); return handle->stack; } void proc_copystackbuf(struct process *handle, void *buf, ssize_t len, off_t offset) { ssize_t bytes; if (!len) return; bytes=pread(handle->mem, buf, len, offset); if (bytes==-1) die("pread: %s\n", strerror(errno)); if (bytes