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