Last active
April 7, 2016 13:36
-
-
Save andyrudoff/53b636ddc185250f4b9a3a4935b62409 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.o | |
tester | |
tickle_me_elmo.so |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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, ®s) < 0) | |
err(1, "elmo child: ptrace GETREGS"); | |
if (report(parentfd, ®s)) | |
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