Skip to content

Instantly share code, notes, and snippets.

@andyrudoff
Last active April 7, 2016 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andyrudoff/53b636ddc185250f4b9a3a4935b62409 to your computer and use it in GitHub Desktop.
Save andyrudoff/53b636ddc185250f4b9a3a4935b62409 to your computer and use it in GitHub Desktop.
*.o
tester
tickle_me_elmo.so
CFLAGS = -std=gnu99 -Wall -Werror -fPIC
all: tickle_me_elmo.so tester
tester: tester.o
$(CC) -o tester tester.o
tickle_me_elmo.so: tickle_me_elmo.o
$(CC) -Wl,-z,relro -shared -Wl,-soname,elmo.so -o tickle_me_elmo.so tickle_me_elmo.o
test: all
LD_PRELOAD=tickle_me_elmo.so LD_LIBRARY_PATH=. ./tester
/*
* trivial program for testing
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
int
main(int argc, char *argv[])
{
printf("hello from main.\n");
FILE *fp = fopen("/etc/passwd", "r");
char buf[8192];
fgets(buf, 8192, fp);
printf("read line from /etc/passwd: %s", buf);
fclose(fp);
DIR *dp = opendir(".");
struct dirent *entp = readdir(dp);
printf("read file name \"%s\"\n", entp->d_name);
closedir(dp);
access("/tmp", F_OK);
printf("goodbye from main.\n");
exit(0);
}
/*
* Copyright (c) 2013, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* elmo.c -- (e)xecution (l)ocation (mo)difier
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <err.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <dirent.h>
/*
* System call handlers (called instead of syscall instruction)...
*
* 64-bit Linux syscalls place the system call number in rax,
* arguments in rdi, rsi, rdx, r10, r8, and r9. After the syscall
* instruction, the return value will be left by the kernel in rax,
* where return values in the range -4095 to -1 indicate an error
* and the value is -errno.
*
* Syscalls are allowed to trash rcx and r11, according to the ABI.
* That's good since our patch code overwrote r11 to get us here.
*
* Since our handlers get control where the syscall instruction would
* normally happen, the input arguments are in registers as described
* above. From that point on we're in normal C land so the return value
* should be like what the libc syscall wrappers would return (-1 on error
* with errno set).
*/
/*
* elmo_read -- this gets control when a read() syscall trap happens
*/
static ssize_t
elmo_read(/* int fd, void *buf, size_t count */)
{
register long rdi asm("rdi"); /* int fd */
register long rsi asm("rsi"); /* void *buf */
register long rdx asm("rdx"); /* size_t count */
int fd = (int) rdi;
void *buf = (void *) rsi;
size_t count = (size_t) rdx;
char *p = "this is my fake read data\n";
fprintf(stderr, "elmo_read(%d, %p, %zu)", fd, buf, count);
strcpy(buf, p);
fprintf(stderr, " = %zu\n", strlen(p));
return strlen(p);
}
/*
* Code that arranges for system call interception...
*/
struct tickle_info {
unsigned long long sc_number;
unsigned long long rip;
};
int Nexttrace;
#define MAXTRACE 10000
struct {
struct tickle_info info;
int line;
const char *code;
} Trace[MAXTRACE];
/*
* read_reports -- (internal) read any & all info structs from pipe
*/
void
read_reports(int childfd, int line, const char *code)
{
int cc;
struct tickle_info info;
while ((cc = read(childfd, &info, sizeof (info))) > 0)
if (cc != sizeof (info)) {
/* can this even happen? check just in case. */
errx(1, "elmo parent: short read %d != %d",
cc, (int) sizeof (info));
} else {
Trace[Nexttrace].info = info;
Trace[Nexttrace].line = line;
Trace[Nexttrace++].code = code;
}
if (cc < 0 && errno != EAGAIN)
err(1, "elmo parent: read");
}
/*
* tickle_me_elmo -- (internal) tickle interesting syscalls, hooking them
*
* childfd is the read side of a pipe from the child who is tracing
* us, so we can read the results of the trace.
*/
static void
tickle_me_elmo(int childfd)
{
struct tickle_info info;
int fd;
struct stat stbuf;
if (read(childfd, &info, sizeof (info)) < 0)
err(1, "elmo parent: read from pipe");
fprintf(stderr, "elmo parent: read from child returned\n");
if (fcntl(childfd, F_SETFL, O_NONBLOCK) < 0)
err(1, "elmo parent: set O_NONBLOCK on pipe");
read_reports(childfd, __LINE__, "start");
#define TICKLE(x) if (1) { x; read_reports(childfd, __LINE__, #x); }
TICKLE(write(-1, 0, 0));
TICKLE(fd = open("/dev/null", 0, 0));
TICKLE(fstat(fd, &stbuf));
TICKLE(fstatat(fd, "moose", &stbuf, 0));
TICKLE(close(-1));
TICKLE(readv(-1, 0, 0));
DIR *dp;
TICKLE(dp = opendir("."));
if (dp == NULL)
err(1, "elmo parent: opendir");
TICKLE(readdir(dp));
TICKLE(closedir(dp));
/* indicate the end of tickling to the child */
TICKLE(alarm(0));
}
/*
* report -- (internal) report a syscall trace to the parent
*
* Returns true if the end of tickling is detected.
*/
static int
report(int fd, struct user_regs_struct *rp)
{
static int first_read_reported;
struct tickle_info info;
/* don't trace parent's read from pipe */
if (rp->orig_rax != SYS_read || !first_read_reported) {
info.sc_number = rp->orig_rax;
info.rip = rp->rip;
if (write(fd, &info, sizeof (info)) < 0)
err(1, "elmo child: write");
if (rp->orig_rax == SYS_read)
first_read_reported = 1;
}
/* detect end of tickling */
return (rp->orig_rax == SYS_alarm);
}
/*
* tracer -- (internal) function used in child process to trace parent
*
* parentfd is the write side of a pipe to the parent we're tracing, so
* we can send the results back for processing.
*/
static void
tracer(int parentfd)
{
pid_t ppid = getppid();
struct tickle_info info;
int status;
int inone = 0;
if (ptrace(PTRACE_ATTACH, ppid, 0, 0) < 0)
err(1, "elmo child: ptrace");
if (write(parentfd, &info, sizeof (info)) < 0)
err(1, "elmo child: write to pipe");
if (waitpid(ppid, &status, 0) < 0)
err(1, "elmo child: initial waitpid(pid=%d)", ppid);
while (1) {
if (ptrace(PTRACE_SYSCALL, ppid, 0, 0) < 0)
err(1, "elmo child: ptrace SYSCALL");
if (waitpid(ppid, &status, 0) < 0)
err(1, "elmo child: waitpid(pid=%d)", ppid);
if (WIFSTOPPED(status)) {
struct user_regs_struct regs;
if (!inone) {
if (ptrace(PTRACE_GETREGS, ppid, 0, &regs) < 0)
err(1, "elmo child: ptrace GETREGS");
if (report(parentfd, &regs))
break;
}
inone = !inone;
} else if (WIFEXITED(status))
errx(1, "elmo child: tracee exit %d",
WEXITSTATUS(status));
else if (WIFSIGNALED(status))
errx(1, "elmo child: tracee %s",
strsignal(WTERMSIG(status)));
else
errx(1, "elmo child: unexpected wait status: 0x%x",
status);
}
}
/*
* strsyscall -- (internal) convert syscall number to printable string
*
* Used for debugging, when printing the list of syscalls traced
*/
static const char *
strsyscall(int num)
{
static char buf[100];
switch (num) {
default:
sprintf(buf, "unknown(%d)", num);
return buf;
#define CASEFOR(x) case x: return #x
CASEFOR(SYS__sysctl);
CASEFOR(SYS_accept);
CASEFOR(SYS_accept4);
CASEFOR(SYS_access);
CASEFOR(SYS_acct);
CASEFOR(SYS_add_key);
CASEFOR(SYS_adjtimex);
CASEFOR(SYS_afs_syscall);
CASEFOR(SYS_alarm);
CASEFOR(SYS_arch_prctl);
CASEFOR(SYS_bind);
CASEFOR(SYS_brk);
CASEFOR(SYS_capget);
CASEFOR(SYS_capset);
CASEFOR(SYS_chdir);
CASEFOR(SYS_chmod);
CASEFOR(SYS_chown);
CASEFOR(SYS_chroot);
CASEFOR(SYS_clock_adjtime);
CASEFOR(SYS_clock_getres);
CASEFOR(SYS_clock_gettime);
CASEFOR(SYS_clock_nanosleep);
CASEFOR(SYS_clock_settime);
CASEFOR(SYS_clone);
CASEFOR(SYS_close);
CASEFOR(SYS_connect);
CASEFOR(SYS_creat);
CASEFOR(SYS_create_module);
CASEFOR(SYS_delete_module);
CASEFOR(SYS_dup);
CASEFOR(SYS_dup2);
CASEFOR(SYS_dup3);
CASEFOR(SYS_epoll_create);
CASEFOR(SYS_epoll_create1);
CASEFOR(SYS_epoll_ctl);
CASEFOR(SYS_epoll_ctl_old);
CASEFOR(SYS_epoll_pwait);
CASEFOR(SYS_epoll_wait);
CASEFOR(SYS_epoll_wait_old);
CASEFOR(SYS_eventfd);
CASEFOR(SYS_eventfd2);
CASEFOR(SYS_execve);
CASEFOR(SYS_exit);
CASEFOR(SYS_exit_group);
CASEFOR(SYS_faccessat);
CASEFOR(SYS_fadvise64);
CASEFOR(SYS_fallocate);
CASEFOR(SYS_fanotify_init);
CASEFOR(SYS_fanotify_mark);
CASEFOR(SYS_fchdir);
CASEFOR(SYS_fchmod);
CASEFOR(SYS_fchmodat);
CASEFOR(SYS_fchown);
CASEFOR(SYS_fchownat);
CASEFOR(SYS_fcntl);
CASEFOR(SYS_fdatasync);
CASEFOR(SYS_fgetxattr);
CASEFOR(SYS_finit_module);
CASEFOR(SYS_flistxattr);
CASEFOR(SYS_flock);
CASEFOR(SYS_fork);
CASEFOR(SYS_fremovexattr);
CASEFOR(SYS_fsetxattr);
CASEFOR(SYS_fstat);
CASEFOR(SYS_fstatfs);
CASEFOR(SYS_fsync);
CASEFOR(SYS_ftruncate);
CASEFOR(SYS_futex);
CASEFOR(SYS_futimesat);
CASEFOR(SYS_get_kernel_syms);
CASEFOR(SYS_get_mempolicy);
CASEFOR(SYS_get_robust_list);
CASEFOR(SYS_get_thread_area);
CASEFOR(SYS_getcpu);
CASEFOR(SYS_getcwd);
CASEFOR(SYS_getdents);
CASEFOR(SYS_getdents64);
CASEFOR(SYS_getegid);
CASEFOR(SYS_geteuid);
CASEFOR(SYS_getgid);
CASEFOR(SYS_getgroups);
CASEFOR(SYS_getitimer);
CASEFOR(SYS_getpeername);
CASEFOR(SYS_getpgid);
CASEFOR(SYS_getpgrp);
CASEFOR(SYS_getpid);
CASEFOR(SYS_getpmsg);
CASEFOR(SYS_getppid);
CASEFOR(SYS_getpriority);
CASEFOR(SYS_getresgid);
CASEFOR(SYS_getresuid);
CASEFOR(SYS_getrlimit);
CASEFOR(SYS_getrusage);
CASEFOR(SYS_getsid);
CASEFOR(SYS_getsockname);
CASEFOR(SYS_getsockopt);
CASEFOR(SYS_gettid);
CASEFOR(SYS_gettimeofday);
CASEFOR(SYS_getuid);
CASEFOR(SYS_getxattr);
CASEFOR(SYS_init_module);
CASEFOR(SYS_inotify_add_watch);
CASEFOR(SYS_inotify_init);
CASEFOR(SYS_inotify_init1);
CASEFOR(SYS_inotify_rm_watch);
CASEFOR(SYS_io_cancel);
CASEFOR(SYS_io_destroy);
CASEFOR(SYS_io_getevents);
CASEFOR(SYS_io_setup);
CASEFOR(SYS_io_submit);
CASEFOR(SYS_ioctl);
CASEFOR(SYS_ioperm);
CASEFOR(SYS_iopl);
CASEFOR(SYS_ioprio_get);
CASEFOR(SYS_ioprio_set);
CASEFOR(SYS_kcmp);
CASEFOR(SYS_kexec_load);
CASEFOR(SYS_keyctl);
CASEFOR(SYS_kill);
CASEFOR(SYS_lchown);
CASEFOR(SYS_lgetxattr);
CASEFOR(SYS_link);
CASEFOR(SYS_linkat);
CASEFOR(SYS_listen);
CASEFOR(SYS_listxattr);
CASEFOR(SYS_llistxattr);
CASEFOR(SYS_lookup_dcookie);
CASEFOR(SYS_lremovexattr);
CASEFOR(SYS_lseek);
CASEFOR(SYS_lsetxattr);
CASEFOR(SYS_lstat);
CASEFOR(SYS_madvise);
CASEFOR(SYS_mbind);
CASEFOR(SYS_memfd_create);
CASEFOR(SYS_migrate_pages);
CASEFOR(SYS_mincore);
CASEFOR(SYS_mkdir);
CASEFOR(SYS_mkdirat);
CASEFOR(SYS_mknod);
CASEFOR(SYS_mknodat);
CASEFOR(SYS_mlock);
CASEFOR(SYS_mlockall);
CASEFOR(SYS_mmap);
CASEFOR(SYS_modify_ldt);
CASEFOR(SYS_mount);
CASEFOR(SYS_move_pages);
CASEFOR(SYS_mprotect);
CASEFOR(SYS_mq_getsetattr);
CASEFOR(SYS_mq_notify);
CASEFOR(SYS_mq_open);
CASEFOR(SYS_mq_timedreceive);
CASEFOR(SYS_mq_timedsend);
CASEFOR(SYS_mq_unlink);
CASEFOR(SYS_mremap);
CASEFOR(SYS_msgctl);
CASEFOR(SYS_msgget);
CASEFOR(SYS_msgrcv);
CASEFOR(SYS_msgsnd);
CASEFOR(SYS_msync);
CASEFOR(SYS_munlock);
CASEFOR(SYS_munlockall);
CASEFOR(SYS_munmap);
CASEFOR(SYS_name_to_handle_at);
CASEFOR(SYS_nanosleep);
CASEFOR(SYS_newfstatat);
CASEFOR(SYS_nfsservctl);
CASEFOR(SYS_open);
CASEFOR(SYS_open_by_handle_at);
CASEFOR(SYS_openat);
CASEFOR(SYS_pause);
CASEFOR(SYS_perf_event_open);
CASEFOR(SYS_personality);
CASEFOR(SYS_pipe);
CASEFOR(SYS_pipe2);
CASEFOR(SYS_pivot_root);
CASEFOR(SYS_poll);
CASEFOR(SYS_ppoll);
CASEFOR(SYS_prctl);
CASEFOR(SYS_pread64);
CASEFOR(SYS_preadv);
CASEFOR(SYS_prlimit64);
CASEFOR(SYS_process_vm_readv);
CASEFOR(SYS_process_vm_writev);
CASEFOR(SYS_pselect6);
CASEFOR(SYS_ptrace);
CASEFOR(SYS_putpmsg);
CASEFOR(SYS_pwrite64);
CASEFOR(SYS_pwritev);
CASEFOR(SYS_query_module);
CASEFOR(SYS_quotactl);
CASEFOR(SYS_read);
CASEFOR(SYS_readahead);
CASEFOR(SYS_readlink);
CASEFOR(SYS_readlinkat);
CASEFOR(SYS_readv);
CASEFOR(SYS_reboot);
CASEFOR(SYS_recvfrom);
CASEFOR(SYS_recvmmsg);
CASEFOR(SYS_recvmsg);
CASEFOR(SYS_remap_file_pages);
CASEFOR(SYS_removexattr);
CASEFOR(SYS_rename);
CASEFOR(SYS_renameat);
CASEFOR(SYS_renameat2);
CASEFOR(SYS_request_key);
CASEFOR(SYS_restart_syscall);
CASEFOR(SYS_rmdir);
CASEFOR(SYS_rt_sigaction);
CASEFOR(SYS_rt_sigpending);
CASEFOR(SYS_rt_sigprocmask);
CASEFOR(SYS_rt_sigqueueinfo);
CASEFOR(SYS_rt_sigreturn);
CASEFOR(SYS_rt_sigsuspend);
CASEFOR(SYS_rt_sigtimedwait);
CASEFOR(SYS_rt_tgsigqueueinfo);
CASEFOR(SYS_sched_get_priority_max);
CASEFOR(SYS_sched_get_priority_min);
CASEFOR(SYS_sched_getaffinity);
CASEFOR(SYS_sched_getattr);
CASEFOR(SYS_sched_getparam);
CASEFOR(SYS_sched_getscheduler);
CASEFOR(SYS_sched_rr_get_interval);
CASEFOR(SYS_sched_setaffinity);
CASEFOR(SYS_sched_setattr);
CASEFOR(SYS_sched_setparam);
CASEFOR(SYS_sched_setscheduler);
CASEFOR(SYS_sched_yield);
CASEFOR(SYS_security);
CASEFOR(SYS_select);
CASEFOR(SYS_semctl);
CASEFOR(SYS_semget);
CASEFOR(SYS_semop);
CASEFOR(SYS_semtimedop);
CASEFOR(SYS_sendfile);
CASEFOR(SYS_sendmmsg);
CASEFOR(SYS_sendmsg);
CASEFOR(SYS_sendto);
CASEFOR(SYS_set_mempolicy);
CASEFOR(SYS_set_robust_list);
CASEFOR(SYS_set_thread_area);
CASEFOR(SYS_set_tid_address);
CASEFOR(SYS_setdomainname);
CASEFOR(SYS_setfsgid);
CASEFOR(SYS_setfsuid);
CASEFOR(SYS_setgid);
CASEFOR(SYS_setgroups);
CASEFOR(SYS_sethostname);
CASEFOR(SYS_setitimer);
CASEFOR(SYS_setns);
CASEFOR(SYS_setpgid);
CASEFOR(SYS_setpriority);
CASEFOR(SYS_setregid);
CASEFOR(SYS_setresgid);
CASEFOR(SYS_setresuid);
CASEFOR(SYS_setreuid);
CASEFOR(SYS_setrlimit);
CASEFOR(SYS_setsid);
CASEFOR(SYS_setsockopt);
CASEFOR(SYS_settimeofday);
CASEFOR(SYS_setuid);
CASEFOR(SYS_setxattr);
CASEFOR(SYS_shmat);
CASEFOR(SYS_shmctl);
CASEFOR(SYS_shmdt);
CASEFOR(SYS_shmget);
CASEFOR(SYS_shutdown);
CASEFOR(SYS_sigaltstack);
CASEFOR(SYS_signalfd);
CASEFOR(SYS_signalfd4);
CASEFOR(SYS_socket);
CASEFOR(SYS_socketpair);
CASEFOR(SYS_splice);
CASEFOR(SYS_stat);
CASEFOR(SYS_statfs);
CASEFOR(SYS_swapoff);
CASEFOR(SYS_swapon);
CASEFOR(SYS_symlink);
CASEFOR(SYS_symlinkat);
CASEFOR(SYS_sync);
CASEFOR(SYS_sync_file_range);
CASEFOR(SYS_syncfs);
CASEFOR(SYS_sysfs);
CASEFOR(SYS_sysinfo);
CASEFOR(SYS_syslog);
CASEFOR(SYS_tee);
CASEFOR(SYS_tgkill);
CASEFOR(SYS_time);
CASEFOR(SYS_timer_create);
CASEFOR(SYS_timer_delete);
CASEFOR(SYS_timer_getoverrun);
CASEFOR(SYS_timer_gettime);
CASEFOR(SYS_timer_settime);
CASEFOR(SYS_timerfd_create);
CASEFOR(SYS_timerfd_gettime);
CASEFOR(SYS_timerfd_settime);
CASEFOR(SYS_times);
CASEFOR(SYS_tkill);
CASEFOR(SYS_truncate);
CASEFOR(SYS_tuxcall);
CASEFOR(SYS_umask);
CASEFOR(SYS_umount2);
CASEFOR(SYS_uname);
CASEFOR(SYS_unlink);
CASEFOR(SYS_unlinkat);
CASEFOR(SYS_unshare);
CASEFOR(SYS_uselib);
CASEFOR(SYS_ustat);
CASEFOR(SYS_utime);
CASEFOR(SYS_utimensat);
CASEFOR(SYS_utimes);
CASEFOR(SYS_vfork);
CASEFOR(SYS_vhangup);
CASEFOR(SYS_vmsplice);
CASEFOR(SYS_vserver);
CASEFOR(SYS_wait4);
CASEFOR(SYS_waitid);
CASEFOR(SYS_write);
CASEFOR(SYS_writev);
}
}
/*
* patch -- hot patch the code at "from" to jump to "to"
*
* This is x86_64 specific.
*
* The code at "from" is overwritten to contain a jump instruction to
* the new routine at "to". The patched routine is destroyed -- you never
* jump back to it. Instead, the new routine is expected to use syscall(2)
* to perform the function of the old routine as necessary.
*
* The mprotect() call could be optimized not to change protections more
* than once on the same page.
*/
static void
patch(void *from, void *to)
{
unsigned char *p = (unsigned char *)from;
unsigned long long pgbegin = (unsigned long long)from & PAGE_MASK;
unsigned long long pgend = ((unsigned long long)from + 12) & PAGE_MASK;
/* allow writes to the normally read-only code pages */
if (mprotect((void *) pgbegin, pgend - pgbegin + PAGE_SIZE,
PROT_READ|PROT_WRITE|PROT_EXEC) < 0)
err(1, "elmo mprotect");
p[0] = 0x49; /* movabs */
p[1] = 0xbb; /* %r11 */
memcpy(&p[2], &to, 8); /* 64-bit abs value */
p[10] = 0x41; /* jmp */
p[11] = 0xff; /* jmp */
p[12] = 0xe3; /* *%r11 */
}
/*
* elmo_constructor -- constructor for elmo library
*
* Called automatically by the run-time loader.
*/
__attribute__((constructor))
static void
elmo_constructor(void)
{
pid_t pid;
int status;
int tracerpipe[2]; /* pipe from tracer to parent */
if (pipe(tracerpipe) < 0)
err(1, "elmo: pipe");
if ((pid = fork()) < 0)
err(1, "elmo: fork");
else if (pid) {
/* parent */
close(tracerpipe[1]);
tickle_me_elmo(tracerpipe[0]);
/* wait for child */
if (waitpid(pid, &status, 0) < 0)
err(1, "elmo parent: waitpid(pid=%d)", pid);
read_reports(tracerpipe[0], __LINE__, "end");
close(tracerpipe[0]);
} else {
/* child */
close(tracerpipe[0]);
tracer(tracerpipe[1]);
close(tracerpipe[1]);
_exit(0);
}
/* for debugging, show the trace of syscalls produced by tickling */
printf("%d trace records\n", Nexttrace);
for (int i = 0; i < Nexttrace; i++)
printf("[%d] %d: \"%s\" %s %llx\n",
i, Trace[i].line, Trace[i].code,
strsyscall(Trace[i].info.sc_number),
Trace[i].info.rip);
/* hook the syscalls we care about */
for (int i = 0; i < Nexttrace; i++)
if (Trace[i].info.sc_number == SYS_read) {
fprintf(stderr, "hooking read\n");
patch((void *) Trace[i].info.rip, (void *) elmo_read);
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment