Python Device Hacking (Keyboard)
After spending a bit of time hacking at the gamepad I decided to take a deeper look in to how /dev devices worked in Python, the easiest device I could get my hands on of course was a keyboard.
First things first I needed to discover which device name represented my keyboard, to do this I used the virtual /proc filesystem at /proc/bus/input/devices :
I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input3
U: Uniq=
H: Handlers=sysrq kbd event2
B: PROP=0
B: EV=120013
B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7
From the above output I can see my device is event2 within Handlers, which I know is the block device /dev/input/event2 .
Next I want to confirm that by running a simple piece of Python while holding down the A key on the keyboard:
>>> f = open('/dev/input/event2', 'r')
>>> while True:
... print f.read(8)
Running the above snippet while holding the A key resulted in the below:
>>> f = open('/dev/input/event2', 'r')
>>> while True:
... print f.read(8)
...
|?N*
|?N*
|?N *
|?N;
|?N
;
Knowing the block device is correct, and Python can read from it we can move on to understanding how the keyboard device works.
Like in the last blog post I convert the binary output in to easily readable Python list containing ASCII character numbers using a simple function:
>>> def k():
... byte = []
... f = open('/dev/input/event2', 'r')
... while True:
... for bit in f.read(1):
... byte.append(ord(bit))
... if len(byte) == 8:
... print byte
... byte = []
Calling this function I then press the letter A again on the keyboard and review the output:
>>> k()
[72, 1, 225, 78, 39, 228, 8, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[72, 1, 225, 78, 53, 228, 8, 0]
[1, 0, 30, 0, 1, 0, 0, 0]
[72, 1, 225, 78, 57, 228, 8, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[72, 1, 225, 78, 188, 251, 9, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[72, 1, 225, 78, 199, 251, 9, 0]
[1, 0, 30, 0, 0, 0, 0, 0]
[72, 1, 225, 78, 202, 251, 9, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
This time I notice something a bit more interesting, something I’m sure that would of helped in the gamepad code. I notice on key press I get the following:
[1, 0, 30, 0, 1, 0, 0, 0]
and on key release I receive:
[1, 0, 30, 0, 0, 0, 0, 0]
This is great as I realize element 3 (30) is the letter, but more importantly the fifth element states if the key is pressed or release (1 of press and 0 for release).
Next was to determine what is returned when you hold down the key, so again I run the k() function and hold the key. This time I found the below:
>>> k()
[204, 2, 225, 78, 126, 197, 3, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[204, 2, 225, 78, 141, 197, 3, 0]
[1, 0, 30, 0, 1, 0, 0, 0]
[204, 2, 225, 78, 145, 197, 3, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[204, 2, 225, 78, 202, 116, 7, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[204, 2, 225, 78, 217, 116, 7, 0]
[1, 0, 30, 0, 2, 0, 0, 0]
[204, 2, 225, 78, 221, 116, 7, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[204, 2, 225, 78, 237, 237, 7, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[204, 2, 225, 78, 252, 237, 7, 0]
[1, 0, 30, 0, 2, 0, 0, 0]
[204, 2, 225, 78, 0, 238, 7, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[204, 2, 225, 78, 129, 104, 8, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[204, 2, 225, 78, 144, 104, 8, 0]
[1, 0, 30, 0, 2, 0, 0, 0]
[204, 2, 225, 78, 148, 104, 8, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[204, 2, 225, 78, 144, 231, 8, 0]
[4, 0, 4, 0, 30, 0, 0, 0]
[204, 2, 225, 78, 159, 231, 8, 0]
[1, 0, 30, 0, 2, 0, 0, 0]
[204, 2, 225, 78, 163, 231, 8, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
With a ken eye you will notice a line almost identical to the above two, but with one slight difference (a value to repeat!):
[1, 0, 30, 0, 2, 0, 0, 0]
At this point I’m pretty impressed and all that is left is to write a bit of Python to interrupt the keys and repeats. Luckily for me I found a piece of Python that already had a Keyboard Map written, slight editing it creates me a working keyboardMap dictionary:
keyboardMap = {
0: "KEY_RESERVED",
1: "KEY_ESC",
2: "KEY_1",
3: "KEY_2",
4: "KEY_3",
5: "KEY_4",
6: "KEY_5",
7: "KEY_6",
8: "KEY_7",
9: "KEY_8",
10: "KEY_9",
11: "KEY_0",
12: "KEY_MINUS",
13: "KEY_EQUAL",
14: "KEY_BACKSPACE",
15: "KEY_TAB",
16: "KEY_Q",
17: "KEY_W",
18: "KEY_E",
19: "KEY_R",
20: "KEY_T",
21: "KEY_Y",
22: "KEY_U",
23: "KEY_I",
24: "KEY_O",
25: "KEY_P",
26: "KEY_LEFTBRACE",
27: "KEY_RIGHTBRACE",
28: "KEY_ENTER",
29: "KEY_LEFTCTRL",
30: "KEY_A",
31: "KEY_S",
32: "KEY_D",
33: "KEY_F",
34: "KEY_G",
35: "KEY_H",
36: "KEY_J",
37: "KEY_K",
38: "KEY_L",
39: "KEY_SEMICOLON",
40: "KEY_APOSTROPHE",
41: "KEY_GRAVE",
42: "KEY_LEFTSHIFT",
43: "KEY_BACKSLASH",
44: "KEY_Z",
45: "KEY_X",
46: "KEY_C",
47: "KEY_V",
48: "KEY_B",
49: "KEY_N",
50: "KEY_M",
51: "KEY_COMMA",
52: "KEY_DOT",
53: "KEY_SLASH",
54: "KEY_RIGHTSHIFT",
55: "KEY_KPASTERISK",
56: "KEY_LEFTALT",
57: "KEY_SPACE",
58: "KEY_CAPSLOCK"
}
You will notice above, just like I said earlier the element with result 30 was the key for the letter A , you will notice the keymap maps 30 to KEY_A.
All that is left is that bit of Python I mentioned:
def k():
byte = []
f = open('/dev/input/event2', 'r')
while True:
for bit in f.read(1):
byte.append(ord(bit))
if len(byte) == 8:
if byte[2] in keyboardMap:
if byte == [1, 0, byte[2], 0, 1, 0, 0, 0]:
print keyboardMap[byte[2]]
elif byte == [1, 0, byte[2], 0, 2, 0, 0, 0]:
print keyboardMap[byte[2]]
byte = []
Now I can connect to my Linux device over SSH and run this bit of code, and when the keyboard on the Linux box is pressed I get the following output:
>>> k()
KEY_F
KEY_L
KEY_I
KEY_P
KEY_MINUS
KEY_E
KEY_D
KEY_E
KEY_S
KEY_I
KEY_G
KEY_N
KEY_DOT
KEY_C
KEY_O
KEY_M