diff options
Diffstat (limited to 'iv/orodja/ldmitm')
-rw-r--r-- | iv/orodja/ldmitm/ldmitm.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/iv/orodja/ldmitm/ldmitm.c b/iv/orodja/ldmitm/ldmitm.c new file mode 100644 index 0000000..66cba41 --- /dev/null +++ b/iv/orodja/ldmitm/ldmitm.c @@ -0,0 +1,203 @@ +/* +DISKUSIJA +Bolje bi bilo uporabiti frida gadget: https://frida.re/docs/gadget/ +Kako deluje: https://tbrindus.ca/correct-ld-preload-hooking-libc/ +Prevedi z: gcc -shared -fPIC -ldl ldmitm.c -o ldmitm.so +Poženi z: LD_PRELOAD=$PWD/ldmitm.so php -S 0:1234 +A je treba beležit execve in fopen? Po mojem ne. Odtekanje flagov itak vidimo v TCP sessionu. +TODO: add mutex locks!!! +*/ +#include <stdio.h> +#include <dlfcn.h> +#include <sys/socket.h> +#include <stdbool.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sqlite3.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#if SQLITE_VERSION_NUMBER < 3037000 +#error "we require sqlite newer than 3037000, yours is " #SQLITE_VERSION_NUMBER " --- because of STRICT!" +#endif +#define LOG(x, ...) if (getenv("LDMITM_LOG")) printf("[ldmitm %s] " x "\n", __func__ __VA_OPT__(,) __VA_ARGS__) +struct stream { + bool active; + bool silenced; + int id; // id in sqlite3 database +}; +typedef int (*accept_t)(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); +typedef int (*close_t)(int fd); +static accept_t real_accept; +static close_t real_close; +static __thread sqlite3 * db = NULL; +struct stream * streams = NULL; +int streams_sizeof = 0; +static void setup_db () { // should be able to ignore being called in case db is already set up on this thread + if (db) + return; + int ret = sqlite3_open_v2(getenv("LDMITM_DB") ? getenv("LDMITM_DB") : ":memory:", &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_EXRESCODE, NULL); + if (ret != SQLITE_OK) { + LOG("failed to open db: %s", sqlite3_errstr(ret)); + goto fail; + } + sqlite3_stmt * stmt = NULL; + ret = sqlite3_prepare_v3(db, "PRAGMA foreign_keys = ON;", -1, 0, &stmt, NULL); + if (ret != SQLITE_OK) { + LOG("failed to prepare pragma foreign_keys: %s", sqlite3_errstr(ret)); + if (stmt) + sqlite3_finalize(stmt); + stmt = NULL; + goto fail; + } + ret = sqlite3_step(stmt); + sqlite3_finalize(stmt); + stmt = NULL; + if (ret != SQLITE_DONE) { + LOG("failed to step pragma foreign_keys: %s", sqlite3_errstr(ret)); + goto fail; + } +#define CREATE_TABLE(name, columns) \ + ret = sqlite3_prepare_v3(db, "create table if not exists " name " (" columns ") STRICT;", -1, 0, &stmt, NULL); \ + if (ret != SQLITE_OK) { \ + LOG("failed to prepare create " name ": %s, statement: %s", sqlite3_errstr(ret), "create table if not exists " name " (" columns ") STRICT;"); \ + if (stmt) \ + sqlite3_finalize(stmt); \ + stmt = NULL; \ + goto fail; \ + } \ + ret = sqlite3_step(stmt); \ + sqlite3_finalize(stmt); \ + stmt = NULL; \ + if (ret != SQLITE_DONE) { \ + LOG("failed to step create " name ": %s", sqlite3_errstr(ret)); \ + goto fail; \ + } + CREATE_TABLE("connections", "id INTEGER PRIMARY KEY, peer TEXT NOT NULL, accepted TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, closed TEXT, silenced TEXT"); // accepted, closed and silenced are iso datestrings + CREATE_TABLE("messages", "connection INTEGER NOT NULL, direction INTEGER NOT NULL CHECK (direction IN (0, 1)), time TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, FOREIGN KEY(connection) REFERENCES connections(id)"); + return; +fail: + if (db) + sqlite3_close_v2(db); + db = NULL; + return; +} +static void end_stream (int fd) { // cleanup function, should be able to handle being called on already ended stream + if (fd >= streams_sizeof || !streams[fd].active) + return; + setup_db(); + if (db) { + sqlite3_stmt * stmt = NULL; + int ret = sqlite3_prepare_v3(db, "update connections set closed=strftime('%FT%R:%f', 'now') WHERE id=:i;", -1, 0, &stmt, NULL); + if (ret != SQLITE_OK) { + LOG("failed to prepare update connections set closed: %s", sqlite3_errstr(ret)); + sqlite3_finalize(stmt); + stmt = NULL; + goto fail; + } + ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), streams[fd].id); + if (ret != SQLITE_OK) { + LOG("failed to bind update connections set closed: %s", sqlite3_errstr(ret)); + sqlite3_finalize(stmt); + stmt = NULL; + goto fail; + } + ret = sqlite3_step(stmt); + sqlite3_finalize(stmt); + stmt = NULL; + if (ret != SQLITE_DONE) { + LOG("failed to step update connections set closed: %s", sqlite3_errstr(ret)); + goto fail; + } + } +fail: + memset(&(streams[fd]), 0, sizeof streams[fd]); +} +int close (int fd) { + if (!real_close) + real_close = dlsym(RTLD_NEXT, "close"); + LOG("called close()"); + int ret = real_close(fd); + int old_errno = errno; + end_stream(fd); + errno = old_errno; + return ret; +} +int accept (int socket, struct sockaddr *restrict address, socklen_t *restrict address_len) { + if (!real_accept) + real_accept = dlsym(RTLD_NEXT, "accept"); + LOG("called accept()"); + int ret = real_accept(socket, address, address_len); + int saved_errno = errno; + if (ret >= streams_sizeof) { + int streams_sizeof_new; + if (streams_sizeof == 0) + streams_sizeof_new = 128; + else + streams_sizeof_new = streams_sizeof * 2; + struct stream * streams_new = realloc(streams, streams_sizeof_new*sizeof *streams); + if (!streams_new) { + LOG("ENOMEM realloc streams!"); + goto fail; + } + memset(streams_new+streams_sizeof, 0, (streams_sizeof_new-streams_sizeof)*sizeof *streams); + streams = streams_new; + streams_sizeof = streams_sizeof_new; + } + end_stream(ret); + streams[ret].active = true; + setup_db(); + if (db) { + sqlite3_stmt * stmt = NULL; + int sqlret = sqlite3_prepare_v3(db, "insert into connections (peer) values (:p);", -1, 0, &stmt, NULL); + if (sqlret != SQLITE_OK) { + LOG("failed to prepare insert connections: %s", sqlite3_errstr(sqlret)); + goto sqlfail; + } + char peeraddress[INET_ADDRSTRLEN+128]; + switch (address->sa_family) { + case AF_INET: + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *) address)->sin_addr), peeraddress, *address_len)) { + int myerrno = errno; + strcpy(peeraddress, "!"); + strcat(peeraddress, strerror(myerrno)); + } + sprintf(peeraddress+strlen(peeraddress), "/%d", ntohs(((struct sockaddr_in *) address)->sin_port)); + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) address)->sin6_addr), peeraddress, *address_len)) { + int myerrno = errno; + strcpy(peeraddress, "!"); + strcat(peeraddress, strerror(myerrno)); + } + sprintf(peeraddress+strlen(peeraddress), "/%d", ntohs(((struct sockaddr_in6 *) address)->sin6_port)); + break; + default: + strcpy(peeraddress, "unknown family"); + break; + } + sqlret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":p"), peeraddress, -1, SQLITE_STATIC); + if (sqlret != SQLITE_OK) { + LOG("failed to bind insert connection: %s", sqlite3_errstr(sqlret)); + goto sqlfail; + } + sqlret = sqlite3_step(stmt); + sqlite3_finalize(stmt); + stmt = NULL; + if (sqlret != SQLITE_DONE) { + LOG("failed to step insert connection: %s", sqlite3_errstr(ret)); + goto sqlfail; + } +sqlfail: + sqlite3_finalize(stmt); + stmt = NULL; + goto fail; + } +fail: + errno = saved_errno; + return ret; +} +__attribute__((constructor)) static void setup (void) { + LOG("called setup()"); +} |