diff --git a/Makefile b/Makefile index 6fb03fd..59d5b41 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ .PHONY: all clean all: $(MAKE) -C proj/test1/ + $(MAKE) -C proj/test2/ -C /usr/src/linux-`uname -r` SUBDIRS=$PWD modules clean: $(MAKE) -C proj/test1/ + $(MAKE) -C proj/test2/ diff --git a/doc/intercept.md b/doc/intercept.md new file mode 100644 index 0000000..d1f98cf --- /dev/null +++ b/doc/intercept.md @@ -0,0 +1,7 @@ + +# Interception Protocol + +* `ok` - Do not modify the function call +* `modify ARGS...` - Modify the arguments of the function call +* `return VALUE` - Return the given value without calling the real function +* `fail ERROR` - Return with the given error code without calling the real function diff --git a/proj/server/src/server.py b/proj/server/src/server.py new file mode 100755 index 0000000..802c864 --- /dev/null +++ b/proj/server/src/server.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +from socketserver import UnixStreamServer, StreamRequestHandler, ThreadingMixIn +import os + + +class Handler(StreamRequestHandler): + def handle(self): + first = self.rfile.readline() + pid = int(first.split(b':')[1]) + print(f'Process with PID {pid} connected') + while True: + msg = self.rfile.readline() + if not msg: + return + timestamp, data = msg.split(b' ', 1) + if not data.startswith(b'return '): + print(data) + #self.wfile.write(b'ok\n') + else: + print(data) + + +class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer): + pass + + +def main() -> None: + os.unlink('/tmp/test') + with ThreadedUnixStreamServer('/tmp/test', Handler) as server: + server.serve_forever() + +if __name__ == '__main__': + main() diff --git a/proj/test1/Makefile b/proj/test1/Makefile index d49d6e6..9fec3da 100644 --- a/proj/test1/Makefile +++ b/proj/test1/Makefile @@ -1,20 +1,48 @@ CC=gcc CFLAGS=-std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809L -g -LDFLAGS=-lc .PHONY: all clean all: default -default: main main_wrapped test_preload.so +default: bin main test_preload.so main_wrapped #test_kernel.ko -test_preload.so: src/test_preload.c - $(CC) -shared -fPIC -o $@ $^ $(CFLAGS) $(LDFLAGS) +bin: + mkdir -p bin/ + +bin/test_preload.o: src/test_preload.c + $(CC) -fPIC -c -o $@ $^ $(CFLAGS) + +test_preload.so: bin/test_preload.o + $(CC) -shared -o $@ $^ $(CFLAGS) -lc -ldl + +test_kernel.ko: src/test_kernel.c + $(CC) -D__KERNEL__ -DMODULE -I/usr/src/linux/include -o $@ $^ main: src/main.c - $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) + $(CC) -o $@ $^ $(CFLAGS) -lc main_wrapped: src/main.c src/test_wrap.c - $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=getopt + $(CC) -o $@ $^ $(CFLAGS) -lc -Wl,--wrap=malloc,--wrap=free,--wrap=calloc,--wrap=realloc,--wrap=getopt clean: - rm -rf main test_preload.so main_wrapped + rm -rf main main_wrapped bin/* *.so *.ko *.o + + +#ifneq ($(KERNELRELEASE),) +# # call from kernel build system +# lifo-objs := main.o +# obj-m := lifo.o +#else +# KERNELDIR ?= /lib/modules/$(shell uname -r)/build +# PWD := $(shell pwd) +#modules: +# echo $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules +# $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules +#endif +# +#depend .depend dep: +# $(CC) $(CFLAGS) -M *.c > .depend +# +#ifeq (.depend,$(wildcard .depend)) +# include .depend +#endif diff --git a/proj/test1/src/test_kernel.c b/proj/test1/src/test_kernel.c new file mode 100644 index 0000000..8eb5121 --- /dev/null +++ b/proj/test1/src/test_kernel.c @@ -0,0 +1,65 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +unsigned long *sys_call_table; + +asmlinkage long (*original_sys_open)(const char __user *filename, int flags, int mode); + +asmlinkage int our_fake_open_function(const char __user *filename, int flags, int mode) { + struct nameidata nd, nd_t; + struct inode *inode, *inode_t; + + int error = user_path_walk(filename, &nd); + if (!error) { + inode = nd.dentry->d_inode; + // Have to do this before calling user_path_walk() from kernel space: + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + + // Protect /tmp/test. Change this to whatever file you want to protect + error = user_path_walk("/tmp/test", &nd_t); + set_fs(fs); + if (!error) { + inode_t = nd_t.dentry->d_inode; + if (inode == inode_t) + return -EACCES; + } + } + + return original_sys_open(filename, flags, mode); +} + +static int __init my_init(void) { + unsigned long *sys_table = (unsigned long *)&system_utsname; + + int found = 0; + for (int i = 0; i < 1024; i++, sys_table++) { + if (sys_table[__NR_read] == (unsigned long)sys_read) { + sys_call_table = sys_table; + found = 1; + break; + } + } + + if (found) { + original_sys_open = (void *)xchg(&sys_call_table[__NR_open], our_fake_open_function); + } + + return 0; +} + +static void my_exit(void) { + xchg(&sys_call_table[__NR_open], original_sys_open); +} + +module_init(my_init); +module_exit(my_exit); diff --git a/proj/test1/src/test_preload.c b/proj/test1/src/test_preload.c index cb2afa9..a2cac32 100644 --- a/proj/test1/src/test_preload.c +++ b/proj/test1/src/test_preload.c @@ -1,93 +1,208 @@ +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include -#define PREFIX "---====[ " +static int mode = 0; +static int intercept = 0; +static int *(*log)(int fd, const char *fmt, ...); -static void log_str(FILE *out, const char *str) { - fprintf(out, "%p:\"", str); +static void log_prefix(void) { + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + log(intercept, "%li.%09li ", spec.tv_sec, spec.tv_nsec); +} + +static void log_str(const char *str) { + log(intercept, "%p:\"", str); for (char ch; (ch = *str) != 0; str++) { if (ch == '\\' || ch == '"') { - fputc('\\', out); - fputc(ch, out); + log(intercept, "\\%c", ch); } else if (ch == '\t') { - fputs("\\t", out); + log(intercept, "\\t"); } else if (ch == '\n') { - fputs("\\n", out); + log(intercept, "\\n"); } else if (ch == '\r') { - fputs("\\r", out); + log(intercept, "\\r"); } else if ((ch >= 0 && ch < 0x20) || ch == 0x7F) { - fprintf(out, "\\x%02x", ch); + log(intercept, "\\x%02x", ch); } else { - fputc(ch, out); + log(intercept, "%c", ch); } } - fputc('"', out); + log(intercept, "\""); } -static void log_array_str(FILE *out, char *const array[], int n) { - fprintf(out, "%p:[", (void *)array); +static void log_array_str(char *const array[], int n) { + log(intercept, "%p:[", (void *)array); for (int i = 0; i < n; i++) { - if (i > 0) fputs(", ", stderr); - log_str(stderr, array[i]); + if (i > 0) log(intercept, ", "); + log_str(array[i]); } - fputc(']', out); + log(intercept, "]"); } +static void fin(void) { + if (intercept && mode > 2) + close(intercept); + if (mode > 0) + fprintf(stderr, "intercept: stopped\n"); + mode = 0, intercept = 0; +} + +static void init(void) { + if (mode) return; + if ((log = dlsym(RTLD_NEXT, "dprintf")) == NULL) { + fprintf(stderr, "intercept: unable to find dlsym dprintf: %s\n", strerror(errno)); + errno = 0; + mode = -1; + return; + } + atexit(fin); + const char *val = getenv("INTERCEPT"); + if (val && strcmp(val, "stdout") == 0) { + mode = 1; + fprintf(stderr, "intercept: intercepting function/system calls and logging to stdout\n"); + intercept = STDOUT_FILENO; + } else if (val && strcmp(val, "stderr") == 0) { + mode = 2; + fprintf(stderr, "intercept: intercepting function/system calls and logging to stderr\n"); + intercept = STDERR_FILENO; + } else if (val && strncmp(val, "file:", 5) == 0) { + mode = 3; + if ((intercept = open(val + 5, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) { + fprintf(stderr, "intercept: unable to open log file '%s': %s\nintercept: not logging or manipulating function/system calls\n", val + 5, strerror(errno)); + errno = 0; + mode = -1; + return; + } + fprintf(stderr, "intercept: intercepting function/system calls and logging to file\n"); + } else if (val && strncmp(val, "unix:", 5) == 0) { + mode = 4; + if ((intercept = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "intercept: unable to open unix socket '%s': %s\nintercept: not logging or manipulating function/system calls\n", val + 5, strerror(errno)); + errno = 0; + mode = -1; + return; + } + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + strncpy(addr.sun_path, val + 5, sizeof(addr.sun_path)); + if (connect(intercept, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "intercept: unable to connect to unix socket '%s': %s\nintercept: not logging or manipulating function/system calls\n", val + 5, strerror(errno)); + close(intercept); + errno = 0; + mode = -1; + return; + } + fprintf(stderr, "intercept: intercepting function/system calls and logging to unix socket\n"); + log_prefix(); + log(intercept, "PID:%li\n", getpid()); + } else if (val && strncmp(val, "tcp://", 6) == 0) { + mode = 5; + // TODO + } else { + mode = -1; + fprintf(stderr, "intercept: not logging or manipulating function/system calls\n"); + } +} + +void *(*__real_malloc)(size_t); + +void *__real_malloc(size_t); + void *malloc(size_t size) { - fprintf(stderr, PREFIX "malloc(%li)\n", size); - void *(*_malloc)(size_t); - if ((_malloc = dlsym(RTLD_NEXT, "malloc")) == NULL) { + init(); + if (intercept) { + log_prefix(); + log(intercept, "malloc(%li)\n", size); + } + if ((__real_malloc = dlsym(RTLD_NEXT, "malloc")) == NULL) { errno = ENOSYS; return NULL; } - void *ret = _malloc(size); - fprintf(stderr, PREFIX "-> %p\n", ret); + void *ret = __real_malloc(size); + if (intercept) { + log_prefix(); + log(intercept, "return %p\n", ret); + } return ret; } void *calloc(size_t nmemb, size_t size) { - fprintf(stderr, PREFIX "calloc(%li, %li)\n", nmemb, size); + init(); + if (intercept) { + log_prefix(); + log(intercept, "calloc(%li, %li)\n", nmemb, size); + } void *(*_calloc)(size_t, size_t); if ((_calloc = dlsym(RTLD_NEXT, "calloc")) == NULL) { errno = ENOSYS; return NULL; } void *ret = _calloc(nmemb, size); - fprintf(stderr, PREFIX "-> %p\n", ret); + if (intercept) { + log_prefix(); + log(intercept, "return %p\n", ret); + } return ret; } void *realloc(void *ptr, size_t size) { - fprintf(stderr, PREFIX "realloc(%p, %li)\n", ptr, size); + init(); + if (intercept) { + log_prefix(); + log(intercept, "realloc(%p, %li)\n", ptr, size); + } void *(*_realloc)(void *, size_t); if ((_realloc = dlsym(RTLD_NEXT, "realloc")) == NULL) { errno = ENOSYS; return NULL; } void *ret = _realloc(ptr, size); - fprintf(stderr, PREFIX "-> %p\n", ret); + if (intercept) { + log_prefix(); + log(intercept, "return %p\n", ret); + } return ret; } void free(void *ptr) { - fprintf(stderr, PREFIX "free(%p)\n", ptr); + init(); + if (intercept) { + log_prefix(); + log(intercept, "free(%p)\n", ptr); + } void (*_free)(void *); if ((_free = dlsym(RTLD_NEXT, "free")) == NULL) { errno = ENOSYS; return; } _free(ptr); - fprintf(stderr, PREFIX "-> void\n"); + if (intercept) { + log_prefix(); + log(intercept, "return void\n"); + } } int getopt(const int argc, char *const argv[], const char *shortopts) { - fprintf(stderr, PREFIX "getopt(%i, ", argc); - log_array_str(stderr, argv, argc); - fputs(", ", stderr); - log_str(stderr, shortopts); - fputs(")\n", stderr); + init(); + + if (intercept) { + log_prefix(); + log(intercept, "getopt(%i, ", argc); + log_array_str(argv, argc); + log(intercept, ", "); + log_str(shortopts); + log(intercept, ")\n"); + } int (*_getopt)(int, char *const[], const char *); if ((_getopt = dlsym(RTLD_NEXT, "getopt")) == NULL) { @@ -95,11 +210,16 @@ int getopt(const int argc, char *const argv[], const char *shortopts) { return -1; } int ret = _getopt(argc, argv, shortopts); - fprintf(stderr, PREFIX "-> %i", ret); - if (ret >= 0x20 && ret < 0x7F) { - fprintf(stderr, " ('%c')\n", ret); - } else { - fprintf(stderr, "\n"); + + if (intercept) { + log_prefix(); + log(intercept, "return %i", ret); + if (ret >= 0x20 && ret < 0x7F) { + log(intercept, " ('%c')\n", ret); + } else { + log(intercept, "\n"); + } } + return ret; }