1
0
Files
BSc-Thesis/proj/test1/src/intercept.c

344 lines
11 KiB
C

#include <getopt.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <stdarg.h>
#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[1024], 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 <size>
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 <ptr>
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 <error>
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;
}