I'll use ctypes
to show you how it could work, but porting it to python-xlib
should be straightforward. So lets start with loading the library:
import ctypes
X11 = ctypes.CDLL("libX11.so")
and defining the structures needed:
class Display(ctypes.Structure):
""
" opaque struct "
""
class XKeyEvent(ctypes.Structure):
_fields_ = [
('type', ctypes.c_int),
('serial', ctypes.c_ulong),
('send_event', ctypes.c_int),
('display', ctypes.POINTER(Display)),
('window', ctypes.c_ulong),
('root', ctypes.c_ulong),
('subwindow', ctypes.c_ulong),
('time', ctypes.c_ulong),
('x', ctypes.c_int),
('y', ctypes.c_int),
('x_root', ctypes.c_int),
('y_root', ctypes.c_int),
('state', ctypes.c_uint),
('keycode', ctypes.c_uint),
('same_screen', ctypes.c_int),
]
class XEvent(ctypes.Union):
_fields_ = [
('type', ctypes.c_int),
('xkey', XKeyEvent),
('pad', ctypes.c_long * 24),
]
X11.XOpenDisplay.restype = ctypes.POINTER(Display)
Now we just need to send the event to the root window:
display = X11.XOpenDisplay(None) key = XEvent(type = 2).xkey #KeyPress key.keycode = X11.XKeysymToKeycode(display, 0xffcc) #F15 key.window = key.root = X11.XDefaultRootWindow(display) X11.XSendEvent(display, key.window, True, 1, ctypes.byref(key)) X11.XCloseDisplay(display)
Xlib.ext.record.AllClients()
def listen(self):
self.grab()
while True:
evt = self.disp.next_event()
if evt.type in [X.KeyPress, X.KeyRelease]:
keycode = evt.detail
keysym = self.disp.keycode_to_keysym(keycode, 0)
char = XK.keysym_to_string(keysym)
self.disp.allow_events(X.ReplayKeyboard, X.CurrentTime)
self.mode(self, evt, char)
if evt.type == X.DestroyNotify:
if evt.window.id == self.id:
self.ungrab()
return
def normal_mode(self, event, char): events.append(event) if event.type == X.KeyPress and char: pressed.add(event_to_string(self, event)) return if event.type != X.KeyRelease: return handled = False if len(pressed) > 1: paste_style(self, pressed) handled = True elif len(pressed) == 1: # Get the only element in pressed ev = next(iter(pressed)) handled = handle_single_key(self, ev) # replay events to Inkscape if we couldn 't handle them if not handled: replay(self) events.clear() pressed.clear()
def __init__(self): threading.Thread.__init__(self) self.finished = threading.Event() # Give these some initial values self.mouse_position_x = 0 self.mouse_position_y = 0 self.ison = { "shift": False, "caps": False } # Compile our regex statements. self.isshift = re.compile('^Shift') self.iscaps = re.compile('^Caps_Lock') self.shiftablechar = re.compile('^[a-z0-9]$|^minus$|^equal$|^bracketleft$|^bracketright$|^semicolon$|^backslash$|^apostrophe$|^comma$|^period$|^slash$|^grave$') self.logrelease = re.compile('.*') self.isspace = re.compile('^space$') # Assign default function actions(do nothing). self.KeyDown = lambda x: True self.KeyUp = lambda x: True self.MouseAllButtonsDown = lambda x: True self.MouseAllButtonsUp = lambda x: True self.contextEventMask = [X.KeyPress, X.MotionNotify] # Hook to our display. self.local_dpy = display.Display() self.record_dpy = display.Display()
def makekeyhookevent(self, keysym, event):
storewm = self.xwindowinfo()
if event.type == X.KeyPress:
MessageName = "key down"
elif event.type == X.KeyRelease:
MessageName = "key up"
return pyxhookkeyevent(
storewm["handle"],
storewm["name"],
storewm["class"],
self.lookup_keysym(keysym),
self.asciivalue(keysym),
False,
event.detail,
MessageName
)
def __sendKeyPressEvent(self, keyCode, modifiers, theWindow = None):
if theWindow is None:
focus = self.localDisplay.get_input_focus().focus
else:
focus = theWindow
keyEvent = event.KeyPress(
detail = keyCode,
time = X.CurrentTime,
root = self.rootWindow,
window = focus,
child = X.NONE,
root_x = 1,
root_y = 1,
event_x = 1,
event_y = 1,
state = modifiers,
same_screen = 1
)
focus.send_event(keyEvent)
def run(self): # Create a recording context; we only want key and mouse events self.ctx = self.recordDisplay.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.ButtonPress), #X.KeyRelease, 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) # Enable the context; this only returns after a call to record_disable_context, # while calling the callback function in the meantime logger.info("XRecord interface thread starting") self.recordDisplay.record_enable_context(self.ctx, self.__processEvent) # Finally free the context self.recordDisplay.record_free_context(self.ctx) self.recordDisplay.close()
Last Updated : 28 Jun, 2022
Keylogger in Linux
pyxhook requires python-Xlib. Install it if you don’t have it already.
sudo apt - get install python - xlib
When a keyboard grab activates before generating any actual KeyPress event that activates the grab, G is the grab_window, and F is the current focus, the X server does the following: , When a keyboard grab deactivates after generating any actual KeyRelease event that deactivates the grab, G is the grab_window, and F is the current focus, the X server does the following: , Some of the terms used in this book are unique to X, and other terms that are common to other window systems have different meanings in X. You may find it helpful to refer to the glossary, which is located at the end of the book. , The ascent member is the logical extent of the font above the baseline that is used for determining line spacing. Specific characters may extend beyond this.
The encoding and interpretation of the display name are implementation-dependent. Strings in the Host Portable Character Encoding are supported; support for other characters is implementation-dependent. On POSIX-conformant systems, the display name or DISPLAY environment variable can be a string in the format:
protocol / hostname: number.screen_number
dual - headed: 0.1
The following concepts may serve to make the explanation of visual types clearer. The screen can be color or grayscale, can have a colormap that is writable or read-only, and can also have a colormap whose indices are decomposed into separate RGB pieces, provided one is not on a grayscale screen. This leads to the following diagram:
Color Gray - Scale
R / O R / W R / O R / W
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Undecomposed Static Pseudo Static Gray
Colormap Color Color Gray Scale
Decomposed True Direct
Colormap Color Color
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
/* Window attribute value mask bits */
/* Window attribute value mask bits */
#define CWBackPixmap(1 L << 0)
#define CWBackPixel(1 L << 1)
#define CWBorderPixmap(1 L << 2)
#define CWBorderPixel(1 L << 3)
#define CWBitGravity(1 L << 4)
#define CWWinGravity(1 L << 5)
#define CWBackingStore(1 L << 6)
#define CWBackingPlanes(1 L << 7)
#define CWBackingPixel(1 L << 8)
#define CWOverrideRedirect(1 L << 9)
#define CWSaveUnder(1 L << 10)
#define CWEventMask(1 L << 11)
#define CWDontPropagate(1 L << 12)
#define CWColormap(1 L << 13)
#define CWCursor(1 L << 14)
/* Values */
typedef struct {
Pixmap background_pixmap; /* background, None, or ParentRelative */
unsigned long background_pixel; /* background pixel */
Pixmap border_pixmap; /* border of the window or CopyFromParent */
unsigned long border_pixel; /* border pixel value */
int bit_gravity; /* one of bit gravity values */
int win_gravity; /* one of the window gravity values */
int backing_store; /* NotUseful, WhenMapped, Always */
unsigned long backing_planes; /* planes to be preserved if possible */
unsigned long backing_pixel; /* value to use in restoring planes */
Bool save_under; /* should bits under be saved? (popups) */
long event_mask; /* set of events that should be saved */
long do_not_propagate_mask; /* set of events that should not propagate */
Bool override_redirect; /* boolean value for override_redirect */
Colormap colormap; /* color map to be associated with window */
Cursor cursor; /* cursor to be displayed (or None) */
}
XSetWindowAttributes;
/* Configure window value mask bits */
#define CWX(1 << 0)
#define CWY(1 << 1)
#define CWWidth(1 << 2)
#define CWHeight(1 << 3)
#define CWBorderWidth(1 << 4)
#define CWSibling(1 << 5)
#define CWStackMode(1 << 6)
The pyautogui module can send virtual keypresses and mouse clicks to Windows, OS X, and Linux. Depending on which operating system you’re using, you may have to install some other modules (called dependencies) before you can install PyAutoGUI.,On Linux, run sudo pip3 install python3-xlib, sudo apt-get install scrot, sudo apt-get install python3-tk, and sudo apt-get install python3-dev. (Scrot is a screenshot program that PyAutoGUI uses.), On Linux, run sudo pip3 install python3-xlib, sudo apt-get install scrot, sudo apt-get install python3-tk, and sudo apt-get install python3-dev. (Scrot is a screenshot program that PyAutoGUI uses.) ,After these dependencies are installed, run pip install pyautogui (or pip3 on OS X and Linux) to install PyAutoGUI.
The XChangeKeyboardControl() function changes control of a keyboard and operates on a XKeyboardControl structure: , To obtain the current control values for the keyboard, use XGetKeyboardControl(). , To turn on keyboard auto-repeat, use XAutoRepeatOn(). , A bell generator connected with the console but not directly on a keyboard is treated as if it were part of the keyboard. The order in which controls are verified and altered is server-dependent. If an error is generated, a subset of the controls may have been altered.
/* Mask bits for ChangeKeyboardControl */
#define KBKeyClickPercent(1 L << 0)
#define KBBellPercent(1 L << 1)
#define KBBellPitch(1 L << 2)
#define KBBellDuration(1 L << 3)
#define KBLed(1 L << 4)
#define KBLedMode(1 L << 5)
#define KBKey(1 L << 6)
#define KBAutoRepeatMode(1 L << 7)
/* Values */
typedef struct {
int key_click_percent;
int bell_percent;
int bell_pitch;
int bell_duration;
int led;
int led_mode; /* LedModeOn, LedModeOff */
int key;
int auto_repeat_mode;
/* AutoRepeatModeOff, AutoRepeatModeOn,
AutoRepeatModeDefault */
}
XKeyboardControl;