diff --git a/proj/server/src/server.py b/proj/server/src/server.py index 802c864..055efb5 100755 --- a/proj/server/src/server.py +++ b/proj/server/src/server.py @@ -2,6 +2,7 @@ from socketserver import UnixStreamServer, StreamRequestHandler, ThreadingMixIn import os +import argparse class Handler(StreamRequestHandler): @@ -13,12 +14,19 @@ class Handler(StreamRequestHandler): 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') + timestamp, data = msg.rstrip(b'\n').split(b' ', 1) + if not data.startswith(b'return ') and not data == b'return': + call = data.decode('utf-8') + print(f'[{pid}] {call}') + if call.startswith('malloc('): + command = 'fail ENOMEM' + else: + command = 'ok' + print(f'[{pid}] -> {command}') + self.wfile.write(command.encode('utf-8') + b'\n') else: - print(data) + ret = data.decode('utf-8') + print(f'[{pid}] -> {ret}') class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer): @@ -26,9 +34,22 @@ class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer): def main() -> None: - os.unlink('/tmp/test') - with ThreadedUnixStreamServer('/tmp/test', Handler) as server: - server.serve_forever() + parser = argparse.ArgumentParser() + parser.add_argument('socket', metavar='FILE') + args = parser.parse_args() + + try: + with ThreadedUnixStreamServer(args.socket, Handler) as server: + server.serve_forever() + except KeyboardInterrupt: + print('\nBye') + server.shutdown() + finally: + try: + os.unlink(args.socket) + except FileNotFoundError: + pass + if __name__ == '__main__': main() diff --git a/proj/test1/.gitignore b/proj/test1/.gitignore index fb76a04..dd5b4ef 100644 --- a/proj/test1/.gitignore +++ b/proj/test1/.gitignore @@ -1,2 +1,2 @@ /main -/main_wrapped +/main_* diff --git a/proj/test1/Makefile b/proj/test1/Makefile index 9fec3da..0339ebb 100644 --- a/proj/test1/Makefile +++ b/proj/test1/Makefile @@ -4,24 +4,27 @@ CFLAGS=-std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE - .PHONY: all clean all: default -default: bin main test_preload.so main_wrapped #test_kernel.ko +default: bin main intercept.so main_intercept #test_kernel.ko bin: mkdir -p bin/ -bin/test_preload.o: src/test_preload.c - $(CC) -fPIC -c -o $@ $^ $(CFLAGS) +bin/main.o: src/main.c + $(CC) -c -o $@ $^ $(CFLAGS) -test_preload.so: bin/test_preload.o +bin/intercept_preload.o: src/intercept.c + $(CC) -fPIC -c -o $@ $^ $(CFLAGS) -DINTERCEPT_PRELOAD + +intercept.so: bin/intercept_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 +main: bin/main.o $(CC) -o $@ $^ $(CFLAGS) -lc -main_wrapped: src/main.c src/test_wrap.c +main_intercept: bin/main.o src/intercept.c $(CC) -o $@ $^ $(CFLAGS) -lc -Wl,--wrap=malloc,--wrap=free,--wrap=calloc,--wrap=realloc,--wrap=getopt clean: diff --git a/proj/test1/src/intercept.c b/proj/test1/src/intercept.c new file mode 100644 index 0000000..4ac9fc7 --- /dev/null +++ b/proj/test1/src/intercept.c @@ -0,0 +1,343 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef INTERCEPT_PRELOAD +static void *(*__real_malloc)(size_t); +static void *(*__real_calloc)(size_t, size_t); +static void *(*__real_realloc)(void *, size_t); +static void (*__real_free)(void *); +static int (*__real_getopt)(int, char *const [], const char *); +#define __load(var, name) \ + if (((var) = dlsym(RTLD_NEXT, name)) == NULL) { \ + fprintf(stderr, "intercept: unable to load symbol '%s': %s", name, strerror(errno)); \ + return; \ + } +#define __sym(name) name +#else +extern void *__real_malloc(size_t); +extern void *__real_calloc(size_t, size_t); +extern void *__real_realloc(void *, size_t); +extern void __real_free(void *); +extern int __real_getopt(int, char *const [], const char *); +#define __sym(name) __wrap_ ## name +#endif + +static int mode = 0; +static int intercept = 0; + +static size_t msg_str(char *buf, size_t maxlen, const char *str) { + size_t offset = snprintf(buf, maxlen, "%p:\"", str); + for (char ch; (ch = *str) != 0 && offset <= maxlen - 2; str++) { + if (ch == '\\' || ch == '"') { + buf[offset++] = '\\'; + buf[offset++] = ch; + } else if (ch == '\t') { + buf[offset++] = '\\'; + buf[offset++] = 't'; + } else if (ch == '\n') { + buf[offset++] = '\\'; + buf[offset++] = 'n'; + } else if (ch == '\r') { + buf[offset++] = '\\'; + buf[offset++] = 'r'; + } else if ((ch >= 0 && ch < 0x20) || ch == 0x7F) { + offset += snprintf(buf + offset, maxlen - offset, "\\x%02x", ch); + } else { + buf[offset++] = ch; + } + } + if (offset <= maxlen - 2) { + buf[offset++] = '"'; + buf[offset] = 0; + } + return offset; +} + +static size_t msg_array_str(char *buf, size_t maxlen, char *const array[], int n) { + size_t offset = snprintf(buf, maxlen, "%p:[", (void *)array); + for (int i = 0; i < n && offset <= maxlen - 2; i++) { + if (i > 0) { + buf[offset++] = ','; + buf[offset++] = ' '; + } + offset += msg_str(buf + offset, maxlen - offset, array[i]); + } + if (offset <= maxlen - 2) { + buf[offset++] = ']'; + buf[offset] = 0; + } + return offset; +} + +static void msg(const char *fmt, ...) { + if (!intercept) return; + char buf[256], sub_fmt[16]; + int sub_fmt_p = 0; + + va_list args; + va_start(args, fmt); + + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + size_t offset = snprintf(buf, sizeof(buf), "%li.%09li ", spec.tv_sec, spec.tv_nsec); + for (char ch, state = 0; (ch = *fmt) != 0 && offset < sizeof(buf); fmt++) { + if (state == '%') { + if (ch == '%') { + buf[offset++] = ch; + state = 0; + continue; + } else if (ch == 'e') { + state = 'e'; + continue; + } else if (ch == 'a') { + state = 'a'; + continue; + } else { + sub_fmt_p = 0; + state = '_'; + sub_fmt[sub_fmt_p++] = '%'; + sub_fmt[sub_fmt_p] = 0; + } + } + if (state == '_') { + sub_fmt[sub_fmt_p++] = ch; + sub_fmt[sub_fmt_p] = 0; + if (ch == 's' || ch == 'i' || ch == 'd' || ch == 'o' || ch == 'u' || ch == 'x' || ch == 'X' || ch == 'f' || ch == 'p') { + state = 0; + offset += snprintf(buf + offset, sizeof(buf) - offset, sub_fmt, va_arg(args, long int)); + } + } else if (state == 'e') { + if (ch == 's') { + // escaped string + offset += msg_str(buf + offset, sizeof(buf) - offset, va_arg(args, const char *)); + } else { + // error + va_end(args); + return; + } + state = 0; + } else if (state == 'a') { + if (ch == 's') { + // string array + char *const *array = va_arg(args, char *const *); + const int len = va_arg(args, int); + offset += msg_array_str(buf + offset, sizeof(buf) - offset, array, len); + } else { + // error + va_end(args); + return; + } + state = 0; + } else if (ch == '%') { + state = '%'; + } else { + buf[offset++] = ch; + } + } + if (offset <= sizeof(buf) - 2) { + buf[offset++] = '\n'; + buf[offset] = 0; + } + const size_t size = offset >= sizeof(buf) ? sizeof(buf) - 1 : offset; + + ssize_t ret; + for (size_t written = 0; written < size; written += ret) { + if ((ret = write(intercept, buf, size)) == -1) { + if (errno == EINTR) { + ret = 0; + continue; + } + va_end(args); + return; + } + } + + va_end(args); +} + +static void rcv(char *buf, const int size) { + if (!intercept) return; + size_t num = 0; + for (ssize_t ret; num == 0 || buf[num - 1] != '\n'; num += ret) { + if ((ret = read(intercept, buf, size)) == -1) { + if (errno == EINTR) { + ret = 0; + continue; + } + buf[0] = 0; + return; + } + } + buf[num - 1] = 0; +} + +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; +#ifdef INTERCEPT_PRELOAD + __load(__real_malloc, "malloc"); + __load(__real_calloc, "calloc"); + __load(__real_realloc, "realloc"); + __load(__real_free, "free"); + __load(__real_getopt, "getopt"); +#endif + 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"); + msg("PID:%li", 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 *__sym(malloc)(size_t size) { + init(); + msg("malloc(%li)", size); + if (mode >= 4) { + char buf[256]; + rcv(buf, sizeof(buf)); + if (strncmp(buf, "modify ", 7) == 0) { + // modify + char *end_ptr = NULL; + long val = strtol(buf + 7, &end_ptr, 0); + if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { + size = val; + } else { + fprintf(stderr, "intercept: malloc: invalid args in modify command: '%s'\n", buf + 7); + } + } else if (strncmp(buf, "return ", 7) == 0) { + // return + char *end_ptr = NULL; + long val = strtol(buf + 7, &end_ptr, 0); + if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { + msg("return %p", val); + return (void *)val; + } else { + fprintf(stderr, "intercept: malloc: invalid args in return command: '%s'\n", buf + 7); + } + } else if (strncmp(buf, "fail ", 5) == 0) { + // fail + if (strcmp(buf + 5, "ENOMEM") == 0) { + errno = ENOMEM; + } else { + errno = 0; + fprintf(stderr, "intercept: malloc: invalid error code in fail command: '%s'\n", buf + 5); + } + msg("return %p", NULL); + return NULL; + } else if (strcmp(buf, "ok") != 0) { + fprintf(stderr, "intercept: malloc: invalid command: '%s'\n", buf); + } + } + void *ret = __real_malloc(size); + msg("return %p", ret); + return ret; +} + +void *__sym(calloc)(size_t nmemb, size_t size) { + init(); + msg("calloc(%li, %li)", nmemb, size); + if (mode >= 4) { + char buf[256]; + rcv(buf, sizeof(buf)); + // TODO + } + void *ret = __real_calloc(nmemb, size); + msg("return %p", ret); + return ret; +} + +void *__sym(realloc)(void *ptr, size_t size) { + init(); + msg("realloc(%p, %li)", ptr, size); + if (mode >= 4) { + char buf[256]; + rcv(buf, sizeof(buf)); + // TODO + } + void *ret = __real_realloc(ptr, size); + msg("return %p", ret); + return ret; +} + +void __sym(free)(void *ptr) { + init(); + msg("free(%p)", ptr); + if (mode >= 4) { + char buf[256]; + rcv(buf, sizeof(buf)); + // TODO + } + __real_free(ptr); + msg("return"); +} + +int __sym(getopt)(const int argc, char *const argv[], const char *shortopts) { + init(); + msg("getopt(%i, %as, %es)", argc, argv, argc, shortopts); + if (mode >= 4) { + char buf[256]; + rcv(buf, sizeof(buf)); + // TODO + } + int ret = __real_getopt(argc, argv, shortopts); + msg("return %i", ret); + return ret; +} diff --git a/proj/test1/src/main.c b/proj/test1/src/main.c index 3c311b4..5c6395e 100644 --- a/proj/test1/src/main.c +++ b/proj/test1/src/main.c @@ -1,5 +1,6 @@ #include +#include #include #include #include diff --git a/proj/test1/src/test_preload.c b/proj/test1/src/test_preload.c deleted file mode 100644 index a2cac32..0000000 --- a/proj/test1/src/test_preload.c +++ /dev/null @@ -1,225 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int mode = 0; -static int intercept = 0; -static int *(*log)(int fd, const char *fmt, ...); - -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 == '"') { - log(intercept, "\\%c", ch); - } else if (ch == '\t') { - log(intercept, "\\t"); - } else if (ch == '\n') { - log(intercept, "\\n"); - } else if (ch == '\r') { - log(intercept, "\\r"); - } else if ((ch >= 0 && ch < 0x20) || ch == 0x7F) { - log(intercept, "\\x%02x", ch); - } else { - log(intercept, "%c", ch); - } - } - log(intercept, "\""); -} - -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) log(intercept, ", "); - log_str(array[i]); - } - 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) { - 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 = __real_malloc(size); - if (intercept) { - log_prefix(); - log(intercept, "return %p\n", ret); - } - return ret; -} - -void *calloc(size_t nmemb, size_t 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); - if (intercept) { - log_prefix(); - log(intercept, "return %p\n", ret); - } - return ret; -} - -void *realloc(void *ptr, size_t 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); - if (intercept) { - log_prefix(); - log(intercept, "return %p\n", ret); - } - return ret; -} - -void free(void *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); - if (intercept) { - log_prefix(); - log(intercept, "return void\n"); - } -} - -int getopt(const int argc, char *const argv[], const char *shortopts) { - 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) { - errno = ENOSYS; - return -1; - } - int ret = _getopt(argc, argv, shortopts); - - 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; -} diff --git a/proj/test1/src/test_wrap.c b/proj/test1/src/test_wrap.c deleted file mode 100644 index f61657f..0000000 --- a/proj/test1/src/test_wrap.c +++ /dev/null @@ -1,90 +0,0 @@ - -#include -#include -#include - -#define PREFIX "---====[ " - -static void log_str(FILE *out, const char *str) { - fprintf(out, "%p:\"", str); - for (char ch; (ch = *str) != 0; str++) { - if (ch == '\\' || ch == '"') { - fputc('\\', out); - fputc(ch, out); - } else if (ch == '\t') { - fputs("\\t", out); - } else if (ch == '\n') { - fputs("\\n", out); - } else if (ch == '\r') { - fputs("\\r", out); - } else if ((ch >= 0 && ch < 0x20) || ch == 0x7F) { - fprintf(out, "\\x%02x", ch); - } else { - fputc(ch, out); - } - } - fputc('"', out); -} - -static void log_array_str(FILE *out, char *const array[], int n) { - fprintf(out, "%p:[", (void *)array); - for (int i = 0; i < n; i++) { - if (i > 0) fputs(", ", stderr); - log_str(stderr, array[i]); - } - fputc(']', out); -} - -extern void *__real_malloc(size_t size); - -void *__wrap_malloc(size_t size) { - fprintf(stderr, PREFIX "malloc(%li)\n", size); - void *ret = __real_malloc(size); - fprintf(stderr, PREFIX "-> %p\n", ret); - return ret; -} - -extern void *__real_calloc(size_t nmemb, size_t size); - -void *__wrap_calloc(size_t nmemb, size_t size) { - fprintf(stderr, PREFIX "calloc(%li, %li)\n", nmemb, size); - void *ret = __real_calloc(nmemb, size); - fprintf(stderr, PREFIX "-> %p\n", ret); - return ret; -} - -extern void *__real_realloc(void *ptr, size_t size); - -void *__wrap_realloc(void *ptr, size_t size) { - fprintf(stderr, PREFIX "realloc(%p, %li)\n", ptr, size); - void *ret = __real_realloc(ptr, size); - fprintf(stderr, PREFIX "-> %p\n", ret); - return ret; -} - -extern void __real_free(void *ptr); - -void __wrap_free(void *ptr) { - fprintf(stderr, PREFIX "free(%p)\n", ptr); - __real_free(ptr); - fprintf(stderr, PREFIX "-> void\n"); -} - -extern int __real_getopt(int argc, char *const argv[], const char *shortopts); - -int __wrap_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); - - int ret = __real_getopt(argc, argv, shortopts); - fprintf(stderr, PREFIX "-> %i", ret); - if (ret >= 0x20 && ret < 0x7F) { - fprintf(stderr, " ('%c')\n", ret); - } else { - fprintf(stderr, "\n"); - } - return ret; -}