Compare commits

..

No commits in common. "f3e89ef14c827773e558b34099f4445719f07283" and "fc3586eac968780b7537cff4292879867ad03dd0" have entirely different histories.

4 changed files with 59 additions and 84 deletions

View File

@ -6,13 +6,6 @@ This is an application for monitoring your key presses.
It will store all your keypresses in a database called 'tikker.db' in current work directory. It will store all your keypresses in a database called 'tikker.db' in current work directory.
It didn't came well out of the [review](tikker.c.md).
- It contains one bug for sure.
- Other issues were false positives.
- Did not agree on some points.
- I can do whatever I want, but in the end i'll just be a 6-quality-person.
- Tsoding says, you have two kinds of people. One that writes perfect code and the ones that get shit done. I'm the latter. School time's over. It's time for work.
Pre-build binaries: Pre-build binaries:
- Download using `curl -OJ https://retoor.molodetz.nl/api/packages/retoor/generic/tikker/1.0.0/tikker` - Download using `curl -OJ https://retoor.molodetz.nl/api/packages/retoor/generic/tikker/1.0.0/tikker`
. .

View File

@ -1,46 +1,35 @@
markdown markdown
# Keyboard Input Event Logger Summary # Keylogger Program Analysis
## Overview ## Overview
This C program captures keyboard input events, resolves device names, and logs these events into a specified SQLite database `tikker.db`. It utilizes a custom library `sormc.h` for database management. This document reviews a C-based program designed to monitor multiple keyboard devices for input events and log them into a database. The code includes mappings of keycodes to character representations and utilizes system calls to interact with input devices efficiently.
## Features ## Code Highlights
- **Key Event Mapping**: Maps keycodes to their respective characters for better representation. ### Bugs
- **Event Logging**: Logs key events with timestamps efficiently into the database. - **Key Mapping:** The `keycode_to_char` array lacks comprehensive keycode definitions, leading to potential null pointer dereferences.
- **User Configuration**: Allows device selection via a command-line option for targeted event capturing. - **Security Risk:** SQL injection vulnerability due to direct variable embedding in queries.
- **MIT License**: Allows free use, modification, and distribution of the software. - **Unhandled Returns:** Undefined behavior for unknown keycodes, potentially causing `NULL` insertions in the database.
## Bugs and Issues ### Optimizations
- Implement error handling for `snprintf` in loops and use `strncasecmp` for safer keyboard checks.
- Minimize `EVIOCGNAME` calls by caching device names.
- Bound checks to prevent `keycode_to_char` array access overflow and batch `read` operations for performance.
- Ensure proper resource cleanup, including database connection closure.
- Adopt dynamic memory allocation if `device_path` exceeds 32 characters.
1. **Undefined Functions**: Missing definitions for `rargs_get_option_string`, `sormc`, and `sormq`, leading to potential undefined behavior. ### Strengths
2. **Device Resolution**: Uses `O_RDONLY` to open devices, which may need root access, potentially causing permission issues. - Efficient monitoring of multiple devices using `fd_set` and `select()`.
3. **Array Boundaries**: Access to `keycode_to_char` without bounds checking could lead to undefined behavior. - Proper use of `snprintf` to prevent buffer overflow.
4. **Error Handling**: Lack of checks after using `snprintf`, `open`, and `ioctl`, leading to potential failures and bugs. - Logical division between device acquisition and event processing.
## Recommendations ## Summary
- **Argument Parsing**: Use robust libraries for better flexibility and error management. Despite its functional capability, the program presents issues primarily in security, efficiency, and resource management. Addressing vulnerabilities and performance limitations could substantially enhance its reliability.
- **Signal Handling**: Implement graceful termination with proper file descriptor management.
- **Error Checking**: Add checks post `snprintf`, `open`, and `ioctl` to handle errors effectively.
- **Optimize Performance**: Reduce redundancy in `printf` statements for better efficiency.
## Positive Attributes ### Recommendations
- Effectively maps keycodes to characters. Consider using open-source alternatives for better functionality:
- Provides informative console output for ongoing events. - **Logkeys:** Offers broader functionality and community support.
- Efficient database logging of key events. - **Keylogger:** Lightweight with active development on GitHub.
## Potential Improvements
To enhance its robustness and user-friendliness, the program needs the implementation of missing functions and improved error-handling mechanisms.
## Alternative Open Source Tools
- **logkeys**: A Linux keylogger similar in function.
- **Keylogger (Python)**: Uses `pynput` for cross-platform keylogging.
- **Linux-dirty-injector**: Provides additional functionalities including keylogging.
---
*Note: The program is reviewed with a grade of 6.0, suggesting more development is needed in error handling and functionality completion.*

BIN
tikker

Binary file not shown.

View File

@ -1,20 +1,3 @@
/*
Written by retoor@molodetz.nl
This program captures keyboard input events, resolves device names, and logs these events into a specified database.
Includes:
- sormc.h: Custom library file for database management.
MIT License:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "sormc.h" #include "sormc.h"
#include <fcntl.h> #include <fcntl.h>
#include <linux/input.h> #include <linux/input.h>
@ -24,8 +7,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#define DATABASE_NAME "tikker.db"
#define DEVICE_TO_READ_DEFAULT "keyboard"
#define MAX_DEVICES 32 #define MAX_DEVICES 32
#define DEVICE_PATH "/dev/input/event" #define DEVICE_PATH "/dev/input/event"
@ -33,6 +14,7 @@ const char *keycode_to_char[] = {
[2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5", [2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5",
[7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0", [7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0",
[12] = "-", [13] = "=", [14] = "[BACKSPACE]", [15] = "[TAB]", [12] = "-", [13] = "=", [14] = "[BACKSPACE]", [15] = "[TAB]",
[16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T", [16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T",
[21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P", [21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P",
[26] = "[", [27] = "]", [28] = "[ENTER]\n", [29] = "[LEFT_CTRL]", [26] = "[", [27] = "]", [28] = "[ENTER]\n", [29] = "[LEFT_CTRL]",
@ -43,78 +25,85 @@ const char *keycode_to_char[] = {
[49] = "N", [50] = "M", [51] = ",", [52] = ".", [53] = "/", [49] = "N", [50] = "M", [51] = ",", [52] = ".", [53] = "/",
[54] = "[RIGHT_SHIFT]", [55] = "[KEYPAD_*]", [56] = "[LEFT_ALT]", [54] = "[RIGHT_SHIFT]", [55] = "[KEYPAD_*]", [56] = "[LEFT_ALT]",
[57] = " ", [58] = "[CAPSLOCK]", [57] = " ", [58] = "[CAPSLOCK]",
[59] = "[F1]", [60] = "[F2]", [61] = "[F3]", [62] = "[F4]", [59] = "[F1]", [60] = "[F2]", [61] = "[F3]", [62] = "[F4]",
[63] = "[F5]", [64] = "[F6]", [65] = "[F7]", [66] = "[F8]", [63] = "[F5]", [64] = "[F6]", [65] = "[F7]", [66] = "[F8]",
[67] = "[F9]", [68] = "[F10]", [87] = "[F11]", [88] = "[F12]", [67] = "[F9]", [68] = "[F10]", [87] = "[F11]", [88] = "[F12]",
[69] = "[NUMLOCK]", [70] = "[SCROLLLOCK]", [71] = "[KEYPAD_7]", [69] = "[NUMLOCK]", [70] = "[SCROLLLOCK]", [71] = "[KEYPAD_7]",
[72] = "[KEYPAD_8]", [73] = "[KEYPAD_9]", [74] = "[KEYPAD_-]", [72] = "[KEYPAD_8]", [73] = "[KEYPAD_9", [74] = "[KEYPAD_-]",
[75] = "[KEYPAD_4]", [76] = "[KEYPAD_5]", [77] = "[KEYPAD_6]", [75] = "[KEYPAD_4]", [76] = "[KEYPAD_5", [77] = "[KEYPAD_6]",
[78] = "[KEYPAD_+]", [79] = "[KEYPAD_1]", [80] = "[KEYPAD_2]", [78] = "[KEYPAD_+]", [79] = "[KEYPAD_1", [80] = "[KEYPAD_2]",
[81] = "[KEYPAD_3]", [82] = "[KEYPAD_0]", [83] = "[KEYPAD_.]", [81] = "[KEYPAD_3]", [82] = "[KEYPAD_0", [83] = "[KEYPAD_.]",
[86] = "<", [100] = "[RIGHT_ALT]", [97] = "[RIGHT_CTRL]", [86] = "<", [100] = "[RIGHT_ALT]", [97] = "[RIGHT_CTRL]",
[119] = "[PAUSE]", [120] = "[SYSRQ]", [121] = "[BREAK]", [119] = "[PAUSE]", [120] = "[SYSRQ]", [121] = "[BREAK]",
[102] = "[HOME]", [103] = "[UP]", [104] = "[PAGEUP]", [102] = "[HOME]", [103] = "[UP]", [104] = "[PAGEUP]",
[105] = "[LEFT]", [106] = "[RIGHT]", [107] = "[END]", [105] = "[LEFT]", [106] = "[RIGHT]", [107] = "[END]",
[108] = "[DOWN]", [109] = "[PAGEDOWN]", [110] = "[INSERT]", [108] = "[DOWN]", [109] = "[PAGEDOWN]", [110] = "[INSERT]",
[111] = "[DELETE]", [111] = "[DELETE]",
[113] = "[MUTE]", [114] = "[VOLUME_DOWN]", [115] = "[VOLUME_UP]", [113] = "[MUTE]", [114] = "[VOLUME_DOWN]", [115] = "[VOLUME_UP]",
[163] = "[MEDIA_NEXT]", [165] = "[MEDIA_PREV]", [164] = "[MEDIA_PLAY_PAUSE]" [163] = "[MEDIA_NEXT]", [165] = "[MEDIA_PREV]", [164] = "[MEDIA_PLAY_PAUSE]"
}; };
char *resolve_device_name(int fd) {
static char device_name[256];
device_name[0] = 0; int is_keyboard(int fd) {
char device_name[256] = {0};
if (ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name) < 0) { if (ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name) < 0) {
return 0; return 0;
} }
return device_name;
return strstr(device_name, "keyboard") != NULL;
} }
int main(int argc, char *argv[]) { int main() {
char *device_to_read = rargs_get_option_string(argc, argv, "--device", DEVICE_TO_READ_DEFAULT);
int db = sormc(DATABASE_NAME); int db = sormc("tikker.db");
ulonglong times_repeated = 0; ulonglong times_repeated = 0;
ulonglong times_pressed = 0; ulonglong times_pressed = 0;
ulonglong times_released = 0; ulonglong times_released = 0;
sormq(db, "CREATE TABLE IF NOT EXISTS kevent (id INTEGER PRIMARY KEY AUTOINCREMENT, code,event,name,timestamp,char)"); sormq(db, "CREATE TABLE IF NOT EXISTS kevent (id INTEGER PRIMARY KEY AUTOINCREMENT, code,event,name,timestamp,char)");
int keyboard_fds[MAX_DEVICES]; int keyboard_fds[MAX_DEVICES];
int num_keyboards = 0; int num_keyboards = 0;
for (int i = 0; i < MAX_DEVICES; i++) { for (int i = 0; i < MAX_DEVICES; i++) {
char device_path[32]; char device_path[32];
snprintf(device_path, sizeof(device_path), "%s%d", DEVICE_PATH, i); snprintf(device_path, sizeof(device_path), "%s%d", DEVICE_PATH, i);
int fd = open(device_path, O_RDONLY); int fd = open(device_path, O_RDONLY);
if (fd < 0) { if (fd < 0) {
continue; continue;
} }
char *device_name = resolve_device_name(fd);
if (!device_name) { if (is_keyboard(fd)) {
close(fd);
continue;
}
bool is_device_to_read = strstr(device_name, device_to_read) != NULL;
printf("[%s] %s. Mount: %s.\n", is_device_to_read ? "-" : "+", device_name, device_path);
if (is_device_to_read) {
keyboard_fds[num_keyboards++] = fd; keyboard_fds[num_keyboards++] = fd;
printf("Found keyboard: %s\n", device_path);
} else { } else {
close(fd); close(fd);
} }
} }
if (num_keyboards == 0) { if (num_keyboards == 0) {
fprintf(stderr, "No keyboard found. Are you running as root?\n" fprintf(stderr, "No keyboard found.\n");
"If your device is listed above with a minus [-] in front, \n"
"run this application using --device='[DEVICE_NAME]'\n");
return 1; return 1;
} }
printf("Monitoring %d keyboards.\n", num_keyboards); printf("Monitoring %d keyboards.\n", num_keyboards);
struct input_event ev; struct input_event ev;
fd_set read_fds; fd_set read_fds;
while (1) { while (1) {
FD_ZERO(&read_fds); FD_ZERO(&read_fds);
int max_fd = -1; int max_fd = -1;
for (int i = 0; i < num_keyboards; i++) { for (int i = 0; i < num_keyboards; i++) {
FD_SET(keyboard_fds[i], &read_fds); FD_SET(keyboard_fds[i], &read_fds);
if (keyboard_fds[i] > max_fd) { if (keyboard_fds[i] > max_fd) {
@ -130,14 +119,17 @@ int main(int argc, char *argv[]) {
for (int i = 0; i < num_keyboards; i++) { for (int i = 0; i < num_keyboards; i++) {
if (FD_ISSET(keyboard_fds[i], &read_fds)) { if (FD_ISSET(keyboard_fds[i], &read_fds)) {
ssize_t bytes = read(keyboard_fds[i], &ev, sizeof(struct input_event)); ssize_t bytes = read(keyboard_fds[i], &ev, sizeof(struct input_event));
if (bytes == sizeof(struct input_event)) { if (bytes == sizeof(struct input_event)) {
if (ev.type == EV_KEY) { if (ev.type == EV_KEY) {
char *char_name = NULL; char * char_name = NULL;
if (ev.code < sizeof(keycode_to_char) / sizeof(keycode_to_char[0])) { if (ev.code < sizeof(keycode_to_char) / sizeof(keycode_to_char[0])) {
char_name = (char *)keycode_to_char[ev.code]; char_name = (char *)keycode_to_char[ev.code];
} }
char keyboard_name[256]; char keyboard_name[256];
ioctl(keyboard_fds[i], EVIOCGNAME(sizeof(keyboard_name)), keyboard_name); ioctl(keyboard_fds[i], EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
printf("Keyboard: %s, ", keyboard_name); printf("Keyboard: %s, ", keyboard_name);
char *event_name = NULL; char *event_name = NULL;
if (ev.value == 1) { if (ev.value == 1) {
@ -150,8 +142,9 @@ int main(int argc, char *argv[]) {
event_name = "REPEATED"; event_name = "REPEATED";
times_repeated++; times_repeated++;
} }
sormq(db, "INSERT INTO kevent (code, event, name,timestamp,char) VALUES (%d, %s, %s, DATETIME('now'),%s)", ev.code, sormq(db, "INSERT INTO kevent (code, event, name,timestamp,char) VALUES (%d, %s, %s, DATETIME('now'),%s)", ev.code,
event_name, keyboard_name, char_name); event_name, keyboard_name,char_name);
printf("Event: %s, ", ev.value == 1 ? "PRESSED" : ev.value == 0 ? "RELEASED" : "REPEATED"); printf("Event: %s, ", ev.value == 1 ? "PRESSED" : ev.value == 0 ? "RELEASED" : "REPEATED");
printf("Key Code: %d, ", ev.code); printf("Key Code: %d, ", ev.code);
printf("Name: %s, ", char_name); printf("Name: %s, ", char_name);