1*48fe012aSMike Ryan#!/usr/bin/env python3 2e25b118aSDominic Spill# 3e25b118aSDominic Spill# Copyright 2009 Joshua Wright, Michael Ossmann 4e25b118aSDominic Spill# 5e25b118aSDominic Spill# This file is part of gr-bluetooth 6e25b118aSDominic Spill# 7e25b118aSDominic Spill# gr-bluetooth is free software; you can redistribute it and/or modify 8e25b118aSDominic Spill# it under the terms of the GNU General Public License as published by 9e25b118aSDominic Spill# the Free Software Foundation; either version 2, or (at your option) 10e25b118aSDominic Spill# any later version. 11e25b118aSDominic Spill# 12e25b118aSDominic Spill# gr-bluetooth is distributed in the hope that it will be useful, 13e25b118aSDominic Spill# but WITHOUT ANY WARRANTY; without even the implied warranty of 14e25b118aSDominic Spill# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15e25b118aSDominic Spill# GNU General Public License for more details. 16e25b118aSDominic Spill# 17e25b118aSDominic Spill# You should have received a copy of the GNU General Public License 18e25b118aSDominic Spill# along with gr-bluetooth; see the file COPYING. If not, write to 19e25b118aSDominic Spill# the Free Software Foundation, Inc., 51 Franklin Street, 20e25b118aSDominic Spill# Boston, MA 02110-1301, USA. 21e25b118aSDominic Spill 22e25b118aSDominic Spillimport sys 23e25b118aSDominic Spillimport struct 24e25b118aSDominic Spillimport time 25e25b118aSDominic Spillfrom pcapdump.pcapdump import * 26e25b118aSDominic Spill 27e25b118aSDominic SpillDLT_EN10MB = 1 28e25b118aSDominic SpillDLT_BLUETOOTH_HCI_H4 = 187 295cf4c9cbSDominic SpillELLISYS_CSV_HDR = "\"Depth\",\"Time\",\"Name\",\"Data\"\x0d\x0a" 305cf4c9cbSDominic SpillELLISYS_HID_INPUT = "HID Input 1" 31e25b118aSDominic SpillUSBHID_MAP = { 32e25b118aSDominic Spill 0x04 : "a", 33e25b118aSDominic Spill 0x05 : "b", 34e25b118aSDominic Spill 0x06 : "c", 35e25b118aSDominic Spill 0x07 : "d", 36e25b118aSDominic Spill 0x08 : "e", 37e25b118aSDominic Spill 0x09 : "f", 38e25b118aSDominic Spill 0x0A : "g", 39e25b118aSDominic Spill 0x0B : "h", 40e25b118aSDominic Spill 0x0C : "i", 41e25b118aSDominic Spill 0x0D : "j", 42e25b118aSDominic Spill 0x0E : "k", 43e25b118aSDominic Spill 0x0F : "l", 44e25b118aSDominic Spill 0x10 : "m", 45e25b118aSDominic Spill 0x11 : "n", 46e25b118aSDominic Spill 0x12 : "o", 47e25b118aSDominic Spill 0x13 : "p", 48e25b118aSDominic Spill 0x14 : "q", 49e25b118aSDominic Spill 0x15 : "r", 50e25b118aSDominic Spill 0x16 : "s", 51e25b118aSDominic Spill 0x17 : "t", 52e25b118aSDominic Spill 0x18 : "u", 53e25b118aSDominic Spill 0x19 : "v", 54e25b118aSDominic Spill 0x1A : "w", 55e25b118aSDominic Spill 0x1B : "x", 56e25b118aSDominic Spill 0x1C : "y", 57e25b118aSDominic Spill 0x1D : "z", 58e25b118aSDominic Spill 0x1E : "1", 59e25b118aSDominic Spill 0x1F : "2", 60e25b118aSDominic Spill 0x20 : "3", 61e25b118aSDominic Spill 0x21 : "4", 62e25b118aSDominic Spill 0x22 : "5", 63e25b118aSDominic Spill 0x23 : "6", 64e25b118aSDominic Spill 0x24 : "7", 65e25b118aSDominic Spill 0x25 : "8", 66e25b118aSDominic Spill 0x26 : "9", 67e25b118aSDominic Spill 0x27 : "0", 68e25b118aSDominic Spill 0x28 : "[Return]\n", 69e25b118aSDominic Spill 0x29 : "[Esc]", 70e25b118aSDominic Spill 0x2A : "[Backspace]", 71e25b118aSDominic Spill 0x2B : "[Tab]\t", 72e25b118aSDominic Spill 0x2C : " ", 73e25b118aSDominic Spill 0x2D : "-", 74e25b118aSDominic Spill 0x2E : "=", 75e25b118aSDominic Spill 0x2F : "[", 76e25b118aSDominic Spill 0x30 : "]", 77e25b118aSDominic Spill 0x31 : "\\", 78e25b118aSDominic Spill 0x32 : "#", 79e25b118aSDominic Spill 0x33 : ";", 80e25b118aSDominic Spill 0x34 : "'", 81e25b118aSDominic Spill 0x35 : "[Grave Accent]", 82e25b118aSDominic Spill 0x36 : ",", 83e25b118aSDominic Spill 0x37 : ".", 84e25b118aSDominic Spill 0x38 : "/", 85e25b118aSDominic Spill 0x39 : "[Caps Lock]", 86e25b118aSDominic Spill 0x3A : "[F1]", 87e25b118aSDominic Spill 0x3B : "[F2]", 88e25b118aSDominic Spill 0x3C : "[F3]", 89e25b118aSDominic Spill 0x3D : "[F4]", 90e25b118aSDominic Spill 0x3E : "[F5]", 91e25b118aSDominic Spill 0x3F : "[F6]", 92e25b118aSDominic Spill 0x40 : "[F7]", 93e25b118aSDominic Spill 0x41 : "[F8]", 94e25b118aSDominic Spill 0x42 : "[F9]", 95e25b118aSDominic Spill 0x43 : "[F10]", 96e25b118aSDominic Spill 0x44 : "[F11]", 97e25b118aSDominic Spill 0x45 : "[F12]", 98e25b118aSDominic Spill 0x46 : "[PrintScreen]", 99e25b118aSDominic Spill 0x47 : "[Scroll]", 100e25b118aSDominic Spill 0x48 : "[Pause]", 101e25b118aSDominic Spill 0x49 : "[Insert]", 102e25b118aSDominic Spill 0x4A : "[Home]", 103e25b118aSDominic Spill 0x4B : "[PageUp]", 104e25b118aSDominic Spill 0x4C : "[Delete]", 105e25b118aSDominic Spill 0x4D : "[End]", 106e25b118aSDominic Spill 0x4E : "[PageDown]", 107e25b118aSDominic Spill 0x4F : "[RightArrow]", 108e25b118aSDominic Spill 0x50 : "[LeftArrow]", 109e25b118aSDominic Spill 0x51 : "[DownArrow]", 110e25b118aSDominic Spill 0x52 : "[UpArrow]", 111e25b118aSDominic Spill 0x53 : "[Keypad Num Lock and Clear]", 112e25b118aSDominic Spill 0x54 : "[Keypad /]", 113e25b118aSDominic Spill 0x55 : "[Keypad *]", 114e25b118aSDominic Spill 0x56 : "[Keypad -]", 115e25b118aSDominic Spill 0x57 : "[Keypad +]", 116e25b118aSDominic Spill 0x58 : "[Keypad Enter]\n", 117e25b118aSDominic Spill 0x59 : "[Keypad 1 and End]", 118e25b118aSDominic Spill 0x5A : "[Keypad 2 and Down Arrow]", 119e25b118aSDominic Spill 0x5B : "[Keypad 3 and PageDn]", 120e25b118aSDominic Spill 0x5C : "[Keypad 4 and Left Arrow]", 121e25b118aSDominic Spill 0x5D : "[Keypad 5]", 122e25b118aSDominic Spill 0x5E : "[Keypad 6 and Right Arrow]", 123e25b118aSDominic Spill 0x5F : "[Keypad 7 and Home]", 124e25b118aSDominic Spill 0x60 : "[Keypad 8 and Up Arrow]", 125e25b118aSDominic Spill 0x61 : "[Keypad 9 and PageUp]", 126e25b118aSDominic Spill 0x62 : "[Keypad 0 and Insert]", 127e25b118aSDominic Spill 0x63 : "[Keypad . and Delete]", 128e25b118aSDominic Spill 0x64 : "\\", 129e25b118aSDominic Spill 0x65 : "[WinKey]", 130e25b118aSDominic Spill 0x66 : "[Power9]", 131e25b118aSDominic Spill 0x67 : "[Keypad =]", 132e25b118aSDominic Spill 0x68 : "[F13]", 133e25b118aSDominic Spill 0x69 : "[F14]", 134e25b118aSDominic Spill 0x6A : "[F15]", 135e25b118aSDominic Spill 0x6B : "[F16]", 136e25b118aSDominic Spill 0x6C : "[F17]", 137e25b118aSDominic Spill 0x6D : "[F18]", 138e25b118aSDominic Spill 0x6E : "[F19]", 139e25b118aSDominic Spill 0x6F : "[F20]", 140e25b118aSDominic Spill 0x70 : "[F21]", 141e25b118aSDominic Spill 0x71 : "[F22]", 142e25b118aSDominic Spill 0x72 : "[F23]", 143e25b118aSDominic Spill 0x73 : "[F24]", 144e25b118aSDominic Spill 0x74 : "[Execute]", 145e25b118aSDominic Spill 0x75 : "[Help]", 146e25b118aSDominic Spill 0x76 : "[Menu]", 147e25b118aSDominic Spill 0x77 : "[Select]", 148e25b118aSDominic Spill 0x78 : "[Stop]", 149e25b118aSDominic Spill 0x79 : "[Again]", 150e25b118aSDominic Spill 0x7A : "[Undo]", 151e25b118aSDominic Spill 0x7B : "[Cut]", 152e25b118aSDominic Spill 0x7C : "[Copy]", 153e25b118aSDominic Spill 0x7D : "[Paste]", 154e25b118aSDominic Spill 0x7E : "[Find]", 155e25b118aSDominic Spill 0x7F : "[Mute]", 156e25b118aSDominic Spill 0x80 : "[Volume Up]", 157e25b118aSDominic Spill 0x81 : "[Volume Down]", 158e25b118aSDominic Spill 0x82 : "[Locking Caps Lock]", 159e25b118aSDominic Spill 0x83 : "[Locking Num Lock]", 160e25b118aSDominic Spill 0x84 : "[Locking Scroll Lock]", 161e25b118aSDominic Spill 0x85 : "[Keypad Comma]", 162e25b118aSDominic Spill 0x86 : "[Keypad Equal]", 163e25b118aSDominic Spill 0x87 : "[International1]", 164e25b118aSDominic Spill 0x88 : "[International2]", 165e25b118aSDominic Spill 0x89 : "[International3]", 166e25b118aSDominic Spill 0x8A : "[International4]", 167e25b118aSDominic Spill 0x8B : "[International5]", 168e25b118aSDominic Spill 0x8C : "[International6]", 169e25b118aSDominic Spill 0x8D : "[International7]", 170e25b118aSDominic Spill 0x8E : "[International8]", 171e25b118aSDominic Spill 0x8F : "[International9]", 172e25b118aSDominic Spill 0x90 : "[LANG1]", 173e25b118aSDominic Spill 0x91 : "[LANG2]", 174e25b118aSDominic Spill 0x92 : "[LANG3]", 175e25b118aSDominic Spill 0x93 : "[LANG4]", 176e25b118aSDominic Spill 0x94 : "[LANG5]", 177e25b118aSDominic Spill 0x95 : "[LANG6]", 178e25b118aSDominic Spill 0x96 : "[LANG7]", 179e25b118aSDominic Spill 0x97 : "[LANG8]", 180e25b118aSDominic Spill 0x98 : "[LANG9]", 181e25b118aSDominic Spill 0x99 : "[Alternate Erase]", 182e25b118aSDominic Spill 0x9A : "[SysReq/Attention]", 183e25b118aSDominic Spill 0x9B : "[Cancel]", 184e25b118aSDominic Spill 0x9C : "[Clear]", 185e25b118aSDominic Spill 0x9D : "[Prior]", 186e25b118aSDominic Spill 0x9E : "[Return]\n", 187e25b118aSDominic Spill 0x9F : "[Separator]", 188e25b118aSDominic Spill 0xA0 : "[Out]", 189e25b118aSDominic Spill 0xA1 : "[Oper]", 190e25b118aSDominic Spill 0xA2 : "[Clear/Again]", 191e25b118aSDominic Spill 0xA3 : "[CrSel/Props]", 192e25b118aSDominic Spill 0xA4 : "[ExSel]", 193e25b118aSDominic Spill 0xB0 : "[Keypad 00]", 194e25b118aSDominic Spill 0xB1 : "[Keypad 000]", 195e25b118aSDominic Spill 0xB2 : "[Thousands Separator]", 196e25b118aSDominic Spill 0xB3 : "[Decimal Separator]", 197e25b118aSDominic Spill 0xB4 : "[Currency Unit]", 198e25b118aSDominic Spill 0xB5 : "[Currency Sub-unit]", 199e25b118aSDominic Spill 0xB6 : "[Keypad (]", 200e25b118aSDominic Spill 0xB7 : "[Keypad )]", 201e25b118aSDominic Spill 0xB8 : "[Keypad {]", 202e25b118aSDominic Spill 0xB9 : "[Keypad }]", 203e25b118aSDominic Spill 0xBA : "[Keypad Tab]\t", 204e25b118aSDominic Spill 0xBB : "[Keypad Backspace]", 205e25b118aSDominic Spill 0xBC : "[Keypad A]", 206e25b118aSDominic Spill 0xBD : "[Keypad B]", 207e25b118aSDominic Spill 0xBE : "[Keypad C]", 208e25b118aSDominic Spill 0xBF : "[Keypad D]", 209e25b118aSDominic Spill 0xC0 : "[Keypad E]", 210e25b118aSDominic Spill 0xC1 : "[Keypad F]", 211e25b118aSDominic Spill 0xC2 : "[Keypad XOR]", 212e25b118aSDominic Spill 0xC3 : "[Keypad ^]", 213e25b118aSDominic Spill 0xC4 : "[Keypad %]", 214e25b118aSDominic Spill 0xC5 : "[Keypad <]", 215e25b118aSDominic Spill 0xC6 : "[Keypad >]", 216e25b118aSDominic Spill 0xC7 : "[Keypad &]", 217e25b118aSDominic Spill 0xC8 : "[Keypad &&]", 218e25b118aSDominic Spill 0xC9 : "[Keypad |]", 219e25b118aSDominic Spill 0xCA : "[Keypad ||]", 220e25b118aSDominic Spill 0xCB : "[Keypad :]", 221e25b118aSDominic Spill 0xCC : "[Keypad #]", 222e25b118aSDominic Spill 0xCD : "[Keypad Space]", 223e25b118aSDominic Spill 0xCE : "[Keypad @]", 224e25b118aSDominic Spill 0xCF : "[Keypad !]", 225e25b118aSDominic Spill 0xD0 : "[Keypad Memory Store]", 226e25b118aSDominic Spill 0xD1 : "[Keypad Memory Recall]", 227e25b118aSDominic Spill 0xD2 : "[Keypad Memory Clear]", 228e25b118aSDominic Spill 0xD3 : "[Keypad Memory Add]", 229e25b118aSDominic Spill 0xD4 : "[Keypad Memory Subtract]", 230e25b118aSDominic Spill 0xD5 : "[Keypad Memory Multiply]", 231e25b118aSDominic Spill 0xD6 : "[Keypad Memory Divide]", 232e25b118aSDominic Spill 0xD7 : "[Keypad +/-]", 233e25b118aSDominic Spill 0xD8 : "[Keypad Clear]", 234e25b118aSDominic Spill 0xD9 : "[Keypad Clear Entry]", 235e25b118aSDominic Spill 0xDA : "[Keypad Binary]", 236e25b118aSDominic Spill 0xDB : "[Keypad Octal]", 237e25b118aSDominic Spill 0xDC : "[Keypad Decimal]", 238e25b118aSDominic Spill 0xDD : "[Keypad Hexadecimal]", 239e25b118aSDominic Spill 0xE0 : "[LeftControl]", 240e25b118aSDominic Spill 0xE1 : "[LeftShift]", 241e25b118aSDominic Spill 0xE2 : "[LeftAlt]", 242e25b118aSDominic Spill 0xE3 : "[LeftWinKey]", 243e25b118aSDominic Spill 0xE4 : "[RightControl]", 244e25b118aSDominic Spill 0xE5 : "[RightShift]", 245e25b118aSDominic Spill 0xE6 : "[RightAlt]", 246e25b118aSDominic Spill 0xE7 : "[RightWinKey]" 247e25b118aSDominic Spill} 248e25b118aSDominic Spill 249e25b118aSDominic Spill# some keycodes represent different things when Shift is held down 250e25b118aSDominic SpillUSBHID_SHIFT_MAP = { 251e25b118aSDominic Spill 0x04 : "A", 252e25b118aSDominic Spill 0x05 : "B", 253e25b118aSDominic Spill 0x06 : "C", 254e25b118aSDominic Spill 0x07 : "D", 255e25b118aSDominic Spill 0x08 : "E", 256e25b118aSDominic Spill 0x09 : "F", 257e25b118aSDominic Spill 0x0A : "G", 258e25b118aSDominic Spill 0x0B : "H", 259e25b118aSDominic Spill 0x0C : "I", 260e25b118aSDominic Spill 0x0D : "J", 261e25b118aSDominic Spill 0x0E : "K", 262e25b118aSDominic Spill 0x0F : "L", 263e25b118aSDominic Spill 0x10 : "M", 264e25b118aSDominic Spill 0x11 : "N", 265e25b118aSDominic Spill 0x12 : "O", 266e25b118aSDominic Spill 0x13 : "P", 267e25b118aSDominic Spill 0x14 : "Q", 268e25b118aSDominic Spill 0x15 : "R", 269e25b118aSDominic Spill 0x16 : "S", 270e25b118aSDominic Spill 0x17 : "T", 271e25b118aSDominic Spill 0x18 : "U", 272e25b118aSDominic Spill 0x19 : "V", 273e25b118aSDominic Spill 0x1A : "W", 274e25b118aSDominic Spill 0x1B : "X", 275e25b118aSDominic Spill 0x1C : "Y", 276e25b118aSDominic Spill 0x1D : "Z", 277e25b118aSDominic Spill 0x1E : "!", 278e25b118aSDominic Spill 0x1F : "@", 279e25b118aSDominic Spill 0x20 : "#", 280e25b118aSDominic Spill 0x21 : "$", 281e25b118aSDominic Spill 0x22 : "%", 282e25b118aSDominic Spill 0x23 : "^", 283e25b118aSDominic Spill 0x24 : "&", 284e25b118aSDominic Spill 0x25 : "*", 285e25b118aSDominic Spill 0x26 : "(", 286e25b118aSDominic Spill 0x27 : ")", 287e25b118aSDominic Spill 0x2D : "_", 288e25b118aSDominic Spill 0x2E : "+", 289e25b118aSDominic Spill 0x2F : "{", 290e25b118aSDominic Spill 0x30 : "}", 291e25b118aSDominic Spill 0x31 : "|", 292e25b118aSDominic Spill 0x32 : "~", 293e25b118aSDominic Spill 0x33 : ":", 294e25b118aSDominic Spill 0x34 : "\"", 295e25b118aSDominic Spill 0x35 : "~", 296e25b118aSDominic Spill 0x36 : "<", 297e25b118aSDominic Spill 0x37 : ">", 298e25b118aSDominic Spill 0x38 : "?", 299e25b118aSDominic Spill 0x64 : "|" 300e25b118aSDominic Spill} 301e25b118aSDominic Spill 302e25b118aSDominic Spill# global variable to track currently depressed keys 303e25b118aSDominic Spillactive_keys = [] 304e25b118aSDominic Spill 305e25b118aSDominic Spilldef hid2ascii(scancode, shift): 306e25b118aSDominic Spill ''' 307e25b118aSDominic Spill Convert the specified scancode value to the ASCII equivalent using the 308e25b118aSDominic Spill USBHID_MAP list. 309e25b118aSDominic Spill ''' 310e25b118aSDominic Spill if shift: 311e25b118aSDominic Spill try: 312e25b118aSDominic Spill code = USBHID_SHIFT_MAP[scancode] 313e25b118aSDominic Spill return code 314e25b118aSDominic Spill except KeyError: 315e25b118aSDominic Spill pass 316e25b118aSDominic Spill try: 317e25b118aSDominic Spill code = USBHID_MAP[scancode] 318e25b118aSDominic Spill except KeyError: 319e25b118aSDominic Spill return "[Reserved]" 320e25b118aSDominic Spill return code 321e25b118aSDominic Spill 322e25b118aSDominic Spilldef usage(): 323*48fe012aSMike Ryan print("Usage: btaptap [-r pcapfile.pcap | -e ellisysfile.csv] [-c count] [-h]\n", file=sys.stderr) 324e25b118aSDominic Spill sys.exit(0) 325e25b118aSDominic Spill 326e25b118aSDominic Spilldef parse_l2cap_keydata(l2cappkt): 327e25b118aSDominic Spill global active_keys 328e25b118aSDominic Spill 329e25b118aSDominic Spill TRANS_HDR_IN_DATA = 0xA1 330e25b118aSDominic Spill REPORT_ID_KEYBOARD = 0x01 331e25b118aSDominic Spill CTRL = 1 332e25b118aSDominic Spill SHIFT = 2 333e25b118aSDominic Spill ALT = 4 334e25b118aSDominic Spill GUI = 8 335e25b118aSDominic Spill 336e25b118aSDominic Spill # Keyboard keystrokes are only seen in L2CAP packets at least 10 bytes long 337e25b118aSDominic Spill l2clen = (ord(l2cappkt[1]) << 8) | ord(l2cappkt[0]) 338e25b118aSDominic Spill if l2clen < 10: 339e25b118aSDominic Spill return 340e25b118aSDominic Spill 341e25b118aSDominic Spill # Keyboard keystrokes are only carried by Channel ID >= 0x40 342e25b118aSDominic Spill cid = (ord(l2cappkt[3]) << 8) | ord(l2cappkt[2]) 343e25b118aSDominic Spill if cid < 0x40: 344e25b118aSDominic Spill return 345e25b118aSDominic Spill # Ideally we would check for the particular CID for the HID_INTERRUPT 346e25b118aSDominic Spill # channel, but we don't handle the negotiation (and may not have even 347e25b118aSDominic Spill # seen it). 348e25b118aSDominic Spill 349e25b118aSDominic Spill # Transaction Header should indicate input data 350e25b118aSDominic Spill thdr = ord(l2cappkt[4]) 351e25b118aSDominic Spill if thdr != TRANS_HDR_IN_DATA: 352e25b118aSDominic Spill return 353e25b118aSDominic Spill 354e25b118aSDominic Spill # Report ID should indicate this is a keyboard 355e25b118aSDominic Spill rid = ord(l2cappkt[5]) 356e25b118aSDominic Spill if rid != REPORT_ID_KEYBOARD: 357e25b118aSDominic Spill return 358e25b118aSDominic Spill 359e25b118aSDominic Spill # This byte describes modifier key status (one bit per key) 360e25b118aSDominic Spill mod = ord(l2cappkt[6]) 361e25b118aSDominic Spill 362e25b118aSDominic Spill # We don't care whether left or right modifier keys are pressed, so we 363e25b118aSDominic Spill # combine the status bits. 364e25b118aSDominic Spill leftmod = mod & 0x0f 365e25b118aSDominic Spill rightmod = (mod & 0xf0) >> 4 366e25b118aSDominic Spill mod = leftmod | rightmod 367e25b118aSDominic Spill 368e25b118aSDominic Spill # up to six keys can be reported at once 369e25b118aSDominic Spill keycodes = [] 3705cf4c9cbSDominic Spill #for byte in range(8,14): 3715cf4c9cbSDominic Spill for byte in range(8,11): 372e25b118aSDominic Spill keystroke = ord(l2cappkt[byte]) 373e25b118aSDominic Spill if keystroke != 0x00: 374e25b118aSDominic Spill keycodes.append(keystroke) 375e25b118aSDominic Spill 376e25b118aSDominic Spill for keystroke in keycodes: 377e25b118aSDominic Spill # don't repeat keys that are still held down 378e25b118aSDominic Spill if active_keys.count(keystroke) == 0: 379e25b118aSDominic Spill 380e25b118aSDominic Spill if (mod & CTRL): 381e25b118aSDominic Spill sys.stdout.write("CTRL^") 382e25b118aSDominic Spill if (mod & ALT): 383e25b118aSDominic Spill sys.stdout.write("ALT^") 384e25b118aSDominic Spill if (mod & GUI): # e.g. Windows key 385e25b118aSDominic Spill sys.stdout.write("GUI^") 386e25b118aSDominic Spill 387e25b118aSDominic Spill sys.stdout.write(hid2ascii(keystroke, mod & SHIFT)) 388e25b118aSDominic Spill 389e25b118aSDominic Spill active_keys = keycodes 390e25b118aSDominic Spill 3915cf4c9cbSDominic Spilldef parse_ellisys_export(exportfile): 3925cf4c9cbSDominic Spill try: 3935cf4c9cbSDominic Spill cap = open(exportfile, "r") 3945cf4c9cbSDominic Spill except (OSError, IOError) as e: 395*48fe012aSMike Ryan print("Unable to open Ellisys capture file.", file=sys.stderr) 3965cf4c9cbSDominic Spill return 3975cf4c9cbSDominic Spill 3985cf4c9cbSDominic Spill # Check to make sure the CSV file header matches out expectations 3995cf4c9cbSDominic Spill hdr=cap.readline() 4005cf4c9cbSDominic Spill if (hdr != ELLISYS_CSV_HDR): 401*48fe012aSMike Ryan print("Invalid CSV file (does not match Ellisys export format)", file=sys.stderr) 4025cf4c9cbSDominic Spill return 4035cf4c9cbSDominic Spill 4045cf4c9cbSDominic Spill for packetline in cap.xreadlines(): 4055cf4c9cbSDominic Spill try: 4065cf4c9cbSDominic Spill (edepth, etime, ename, edata) = packetline.replace('"', '').strip().split(",") 4075cf4c9cbSDominic Spill except ValueError: 4085cf4c9cbSDominic Spill continue 4095cf4c9cbSDominic Spill 4105cf4c9cbSDominic Spill # We are only interedted in HID Input data 4115cf4c9cbSDominic Spill if (ename != ELLISYS_HID_INPUT): continue 4125cf4c9cbSDominic Spill 4135cf4c9cbSDominic Spill parse_ellisys_keydata(edata) 4145cf4c9cbSDominic Spill 4155cf4c9cbSDominic Spilldef parse_ellisys_keydata(payload): 4165cf4c9cbSDominic Spill TRANS_HDR_IN_DATA = "\xA1" 4175cf4c9cbSDominic Spill DEST_CHANNEL_ID = "\x06\x03" 4185cf4c9cbSDominic Spill 4195cf4c9cbSDominic Spill # Convert space-separated hex into string 4205cf4c9cbSDominic Spill payload = payload.replace(' ','').decode("hex") 4215cf4c9cbSDominic Spill 4225cf4c9cbSDominic Spill # The Ellisys CSV file format doesn't give us the L2CAP data, so we fake it here by adding 4235cf4c9cbSDominic Spill # a 2-byte length field, destination CID, and transaction header 4245cf4c9cbSDominic Spill packet = chr(len(payload)+1) + "\x00" + DEST_CHANNEL_ID + TRANS_HDR_IN_DATA + payload 4255cf4c9cbSDominic Spill parse_l2cap_keydata(packet) 4265cf4c9cbSDominic Spill 427e25b118aSDominic Spilldef parse_bb_keydata(packet): 428e25b118aSDominic Spill 429e25b118aSDominic Spill BTBBHDR_TYPE_MASK = 0x78 430e25b118aSDominic Spill BTBBHDR_TYPE_SHIFT = 3 431e25b118aSDominic Spill BTBBHDR_TYPE_DM1 = 3 432e25b118aSDominic Spill BTBBPAYLOADHDR_LLID_MASK = 0x03 433e25b118aSDominic Spill BTBBPAYLOADHDR_LLID_SHIFT = 0 434e25b118aSDominic Spill BTBBPAYLOADHDR_LEN_MASK = 0xF8 435e25b118aSDominic Spill BTBBPAYLOADHDR_LEN_SHIFT = 3 436e25b118aSDominic Spill LLID_L2CAP = 2 437e25b118aSDominic Spill 438e25b118aSDominic Spill # Keyboard keystrokes are only seen in frames at least 40 bytes long 439e25b118aSDominic Spill if len(packet) < 40: 440e25b118aSDominic Spill return 441e25b118aSDominic Spill 442e25b118aSDominic Spill # Keyboard keystrokes are only seen in DM1 frames 443e25b118aSDominic Spill btbbhdr = packet[20:23] 444e25b118aSDominic Spill type = (ord(btbbhdr[0]) & BTBBHDR_TYPE_MASK) >> BTBBHDR_TYPE_SHIFT 445e25b118aSDominic Spill if type != BTBBHDR_TYPE_DM1: 446e25b118aSDominic Spill return 447e25b118aSDominic Spill 448e25b118aSDominic Spill # Keyboard keystrokes are only seen in L2CAP packets 14 bytes long 449e25b118aSDominic Spill btbbpayloadhdr = ord(packet[23]) 450e25b118aSDominic Spill llid = btbbpayloadhdr & (BTBBPAYLOADHDR_LLID_MASK) >> BTBBPAYLOADHDR_LLID_SHIFT 451e25b118aSDominic Spill l2clen = (btbbpayloadhdr & BTBBPAYLOADHDR_LEN_MASK) >> BTBBPAYLOADHDR_LEN_SHIFT 452*48fe012aSMike Ryan #print("Debug btbbpayloadhdr 0x%02x, llid %d, l2clen %d"%(btbbpayloadhdr, llid, l2clen)) 453e25b118aSDominic Spill if llid != LLID_L2CAP or l2clen < 14: 454e25b118aSDominic Spill return 455e25b118aSDominic Spill 456e25b118aSDominic Spill parse_l2cap_keydata(packet[24:38]) 457e25b118aSDominic Spill 458e25b118aSDominic Spilldef parse_hci_keydata(packet): 459e25b118aSDominic Spill 460e25b118aSDominic Spill HCI_TYPE_ACL_DATA = 2 461e25b118aSDominic Spill 462e25b118aSDominic Spill # Keyboard keystrokes are only seen in frames at least 19 bytes long 463e25b118aSDominic Spill if len(packet) < 19: 464e25b118aSDominic Spill return 465e25b118aSDominic Spill 466e25b118aSDominic Spill # Keyboard keystrokes are only seen in ACL Data frames 467e25b118aSDominic Spill type = ord(packet[0]) 468e25b118aSDominic Spill if type != HCI_TYPE_ACL_DATA: 469e25b118aSDominic Spill return 470e25b118aSDominic Spill 471e25b118aSDominic Spill parse_l2cap_keydata(packet[5:]) 472e25b118aSDominic Spill 473e25b118aSDominic Spillif __name__ == '__main__': 474e25b118aSDominic Spill 475e25b118aSDominic Spill arg_pcapfile = None 4765cf4c9cbSDominic Spill arg_ellisysfile = None 477e25b118aSDominic Spill arg_count = -1 478e25b118aSDominic Spill packetcount = 0 479e25b118aSDominic Spill 480e25b118aSDominic Spill while len(sys.argv) > 1: 481e25b118aSDominic Spill op = sys.argv.pop(1) 482e25b118aSDominic Spill if op == '-r': 483e25b118aSDominic Spill arg_pcapfile = sys.argv.pop(1) 484e25b118aSDominic Spill if op == '-c': 485e25b118aSDominic Spill arg_count = int(sys.argv.pop(1)) 4865cf4c9cbSDominic Spill if op == '-e': 4875cf4c9cbSDominic Spill arg_ellisysfile = sys.argv.pop(1) 488e25b118aSDominic Spill if op == '-h': 489e25b118aSDominic Spill usage() 490e25b118aSDominic Spill sys.exit(0) 491e25b118aSDominic Spill 4925cf4c9cbSDominic Spill if (arg_ellisysfile == None and arg_pcapfile == None): 493*48fe012aSMike Ryan print("Must specify a libpcap capture or an Ellisys CSV file", file=sys.stderr) 494e25b118aSDominic Spill usage() 4955cf4c9cbSDominic Spill sys.exit(0) 496e25b118aSDominic Spill 4975cf4c9cbSDominic Spill if arg_pcapfile != None: 498e25b118aSDominic Spill cap = PcapReader(arg_pcapfile) 499e25b118aSDominic Spill 500e25b118aSDominic Spill while arg_count != packetcount: 501e25b118aSDominic Spill try: 502e25b118aSDominic Spill (pheader, packet) = cap.pnext() 503e25b118aSDominic Spill pkttime = pheader[0] 504e25b118aSDominic Spill packetcount+=1 505e25b118aSDominic Spill 506e25b118aSDominic Spill if cap.datalink() == DLT_EN10MB: 507e25b118aSDominic Spill parse_bb_keydata(packet) 508e25b118aSDominic Spill elif cap.datalink() == DLT_BLUETOOTH_HCI_H4: 509e25b118aSDominic Spill parse_hci_keydata(packet) 510e25b118aSDominic Spill else: 511*48fe012aSMike Ryan print("Unsupported libpcap data link layer: %d\n" % cap.datalink(), file=sys.stderr) 512e25b118aSDominic Spill except TypeError: # raised when pnext returns Null (end of capture) 513e25b118aSDominic Spill break 514e25b118aSDominic Spill 515e25b118aSDominic Spill cap.close() 5165cf4c9cbSDominic Spill 5175cf4c9cbSDominic Spill if arg_ellisysfile != None: 5185cf4c9cbSDominic Spill parse_ellisys_export(arg_ellisysfile) 5195cf4c9cbSDominic Spill 520e25b118aSDominic Spill print 521