윈도우 환경에서 Python을 사용하여 키보드 후킹을 하는 방법 관련하여 좀더 쓰기 편하게 코드 정리를 하였다. 특수키를 확인하는 방법도 추가되었다.
소스
KeyRogue.py
# -*- coding: utf-8 -*-
#!/usr/bin/python
import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD
class KeyRogue:
user32 = windll.user32
kernel32 = windll.kernel32
WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
WM_SYSKEYDOWN = 0x0104
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_SHIFT = 0x10
hooked = None
hookProc = None
pointer = None
@staticmethod
def installHookProc(hookProc):
if KeyRogue.hooked is not None:
print("already installed. uninstalling..")
KeyRogue.uninstallHookProc()
KeyRogue.hookProc = hookProc
KeyRogue.pointer = KeyRogue.getFPTR(KeyRogue.hookProcInternal)
KeyRogue.hooked = KeyRogue.user32.SetWindowsHookExA(
KeyRogue.WH_KEYBOARD_LL,
KeyRogue.pointer,
KeyRogue.kernel32.GetModuleHandleW(None),
0
)
if not KeyRogue.hooked:
return False
return True
@staticmethod
def uninstallHookProc():
if KeyRogue.hooked is None:
return
KeyRogue.user32.UnhookWindowsHookEx(KeyRogue.hooked)
KeyRogue.hooked = None
@staticmethod
def bytes(integer):
return divmod(integer, 0x10000)
@staticmethod
def getFPTR(fn):
CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
return CMPFUNC(fn)
@staticmethod
def startKeyLog():
msg = MSG()
KeyRogue.user32.GetMessageA(byref(msg), 0, 0, 0)
@staticmethod
def getKeyCode(lParam):
high, low = KeyRogue.bytes(lParam[0])
return low
@staticmethod
def getKeyInfo(lParam):
keyCode = KeyRogue.getKeyCode(lParam)
return (keyCode, chr(keyCode))
@staticmethod
def checkModifier(lParam, vKey):
if KeyRogue.user32.GetAsyncKeyState(vKey) & 0x8000:
return True
return False
@staticmethod
def hookProcInternal(nCode, wParam, lParam):
if wParam != KeyRogue.WM_KEYDOWN and wParam != KeyRogue.WM_SYSKEYDOWN:
return KeyRogue.user32.CallNextHookEx(KeyRogue.hooked, nCode, wParam, lParam)
if KeyRogue.hookProc:
KeyRogue.hookProc(nCode, wParam, lParam)
return KeyRogue.user32.CallNextHookEx(KeyRogue.hooked, nCode, wParam, lParam)
@staticmethod
def start(hookProc):
if KeyRogue.installHookProc(hookProc):
KeyRogue.startKeyLog()
else:
print("install failed")
@staticmethod
def stop():
KeyRogue.uninstallHookProc()
client.py
from KeyRogue import *
def hookProc(nCode, wParam, lParam):
key = KeyRogue.getKeyInfo(lParam)
print("key code = %d" % key[0])
print("key char = %c" % key[1])
if KeyRogue.checkModifier(lParam, KeyRogue.VK_CONTROL):
print("CTRL pressed")
if (key[1] == 'Q'):
print("exit key pressed, call stop()")
KeyRogue.stop()
sys.exit(-1)
if KeyRogue.checkModifier(lParam, KeyRogue.VK_MENU):
print("ALT pressed")
if KeyRogue.checkModifier(lParam, KeyRogue.VK_SHIFT):
print("SHIFT pressed")
KeyRogue.start(hookProc)
- Hook 설치 성공시 .start() 함수는 리턴되지 않는다.(심지어 stop() 함수를 사용하여 언인스톨되었을 경우에도) 따라서 GUI가 있는 프로그램에 키보드 후킹 기능을 추가하려면 별도의 쓰레드에서 실행되도록 처리가 필요하다.
참고사항
<테스트 환경>
- OS : Windows 10
- Python 버전 : 3.6.5(32/64bit), 3.7.5(32bit)