/src/wireshark/epan/dissectors/packet-adb.c
Line | Count | Source |
1 | | /* packet-adb.c |
2 | | * Routines for Android Debug Bridge Transport Protocol |
3 | | * |
4 | | * Copyright 2014 Michal Labedzki for Tieto Corporation |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | #include "config.h" |
14 | | |
15 | | #include <epan/packet.h> |
16 | | #include <epan/prefs.h> |
17 | | #include <epan/expert.h> |
18 | | #include <epan/tfs.h> |
19 | | |
20 | | #include <wiretap/wtap.h> |
21 | | |
22 | | #include "packet-adb_service.h" |
23 | | #include "packet-usb.h" |
24 | | |
25 | | static int proto_adb; |
26 | | static int hf_command; |
27 | | static int hf_argument_0; |
28 | | static int hf_argument_1; |
29 | | static int hf_data_length; |
30 | | static int hf_data_crc32; |
31 | | static int hf_magic; |
32 | | static int hf_local_id; |
33 | | static int hf_remote_id; |
34 | | static int hf_version; |
35 | | static int hf_max_data; |
36 | | static int hf_zero; |
37 | | static int hf_sequence; |
38 | | static int hf_online; |
39 | | static int hf_auth_type; |
40 | | static int hf_data; |
41 | | static int hf_service; |
42 | | static int hf_data_fragment; |
43 | | static int hf_command_in_frame; |
44 | | static int hf_completed_in_frame; |
45 | | static int hf_service_start_in_frame; |
46 | | static int hf_close_local_in_frame; |
47 | | static int hf_close_remote_in_frame; |
48 | | static int hf_connection_info; |
49 | | |
50 | | static int ett_adb; |
51 | | static int ett_adb_arg0; |
52 | | static int ett_adb_arg1; |
53 | | static int ett_adb_crc; |
54 | | static int ett_adb_magic; |
55 | | |
56 | | static expert_field ei_invalid_magic; |
57 | | static expert_field ei_invalid_crc; |
58 | | static expert_field ei_invalid_data; |
59 | | |
60 | | static dissector_handle_t adb_handle; |
61 | | static dissector_handle_t adb_service_handle; |
62 | | |
63 | | static int proto_tcp; |
64 | | static int proto_usb; |
65 | | |
66 | | static wmem_tree_t *command_info; |
67 | | static wmem_tree_t *service_info; |
68 | | |
69 | | typedef struct service_data_t { |
70 | | uint32_t start_in_frame; |
71 | | |
72 | | uint32_t close_local_in_frame; |
73 | | uint32_t close_remote_in_frame; |
74 | | |
75 | | uint32_t local_id; |
76 | | uint32_t remote_id; |
77 | | |
78 | | const char *service; |
79 | | } service_data_t; |
80 | | |
81 | | typedef struct command_data_t { |
82 | | uint32_t command; |
83 | | |
84 | | uint32_t command_in_frame; |
85 | | uint32_t response_in_frame; |
86 | | |
87 | | uint32_t arg0; |
88 | | uint32_t arg1; |
89 | | |
90 | | uint32_t data_length; |
91 | | uint32_t crc32; |
92 | | |
93 | | uint32_t completed_in_frame; |
94 | | uint32_t reassemble_data_length; |
95 | | uint8_t *reassemble_data; |
96 | | uint32_t reassemble_error_in_frame; |
97 | | } command_data_t; |
98 | | |
99 | | static uint32_t max_in_frame = UINT32_MAX; |
100 | | |
101 | | static const value_string command_vals[] = { |
102 | | { 0x434e5953, "Synchronize" }, |
103 | | { 0x45534c43, "Close" }, |
104 | | { 0x45545257, "Write" }, |
105 | | { 0x48545541, "Authenticate" }, |
106 | | { 0x4e584e43, "Connect" }, |
107 | | { 0x4e45504f, "Open" }, |
108 | | { 0x59414b4f, "Okay" }, |
109 | | { 0, NULL } |
110 | | }; |
111 | | |
112 | | static const value_string magic_vals[] = { |
113 | | { 0xFFFFFFFF ^ 0x434e5953, "Synchronize" }, |
114 | | { 0xFFFFFFFF ^ 0x45534c43, "Close" }, |
115 | | { 0xFFFFFFFF ^ 0x45545257, "Write" }, |
116 | | { 0xFFFFFFFF ^ 0x48545541, "Authenticate" }, |
117 | | { 0xFFFFFFFF ^ 0x4e584e43, "Connect" }, |
118 | | { 0xFFFFFFFF ^ 0x4e45504f, "Open" }, |
119 | | { 0xFFFFFFFF ^ 0x59414b4f, "Okay" }, |
120 | | { 0, NULL } |
121 | | }; |
122 | | |
123 | | static const value_string auth_type_vals[] = { |
124 | | { 1, "Token" }, |
125 | | { 2, "Signature" }, |
126 | | { 3, "RSA Public Key" }, |
127 | | { 0, NULL } |
128 | | }; |
129 | | |
130 | 0 | #define A_SYNC 0x434e5953 |
131 | 0 | #define A_CLSE 0x45534c43 |
132 | 0 | #define A_WRTE 0x45545257 |
133 | 0 | #define A_AUTH 0x48545541 |
134 | 0 | #define A_CNXN 0x4e584e43 |
135 | 0 | #define A_OPEN 0x4e45504f |
136 | 0 | #define A_OKAY 0x59414b4f |
137 | | |
138 | 0 | #define ADB_TCP_PORT 5555 |
139 | | |
140 | | void proto_register_adb(void); |
141 | | void proto_reg_handoff_adb(void); |
142 | | |
143 | | static void |
144 | | save_command(uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t data_length, |
145 | | uint32_t crc32, service_data_t *service_data, int proto, void *data, |
146 | | packet_info *pinfo, service_data_t **returned_service_data, |
147 | | command_data_t **returned_command_data) |
148 | 0 | { |
149 | 0 | wmem_tree_key_t key[6]; |
150 | 0 | uint32_t interface_id; |
151 | 0 | uint32_t bus_id; |
152 | 0 | uint32_t device_address; |
153 | 0 | uint32_t side_id; |
154 | 0 | uint32_t frame_number; |
155 | 0 | command_data_t *command_data; |
156 | 0 | wmem_tree_t *wmem_tree; |
157 | 0 | int direction = P2P_DIR_UNKNOWN; |
158 | 0 | urb_info_t *urb = (urb_info_t *) data; |
159 | |
|
160 | 0 | frame_number = pinfo->num; |
161 | |
|
162 | 0 | if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) |
163 | 0 | interface_id = pinfo->rec->rec_header.packet_header.interface_id; |
164 | 0 | else |
165 | 0 | interface_id = 0; |
166 | |
|
167 | 0 | if (proto == proto_usb) { |
168 | 0 | urb = (urb_info_t *) data; |
169 | 0 | DISSECTOR_ASSERT(urb); |
170 | |
|
171 | 0 | direction = urb->direction; |
172 | |
|
173 | 0 | bus_id = urb->bus_id; |
174 | 0 | device_address = urb->device_address; |
175 | |
|
176 | 0 | key[0].length = 1; |
177 | 0 | key[0].key = &interface_id; |
178 | 0 | key[1].length = 1; |
179 | 0 | key[1].key = &bus_id; |
180 | 0 | key[2].length = 1; |
181 | 0 | key[2].key = &device_address; |
182 | 0 | key[3].length = 1; |
183 | 0 | key[3].key = &side_id; |
184 | 0 | key[4].length = 1; |
185 | 0 | key[4].key = &frame_number; |
186 | 0 | key[5].length = 0; |
187 | 0 | key[5].key = NULL; |
188 | 0 | } else { /* tcp */ |
189 | 0 | if (pinfo->destport == ADB_TCP_PORT) |
190 | 0 | direction = P2P_DIR_SENT; |
191 | 0 | else |
192 | 0 | direction = P2P_DIR_RECV; |
193 | |
|
194 | 0 | key[0].length = 1; |
195 | 0 | key[0].key = &interface_id; |
196 | 0 | key[1].length = 1; |
197 | 0 | key[2].length = 1; |
198 | 0 | if (direction == P2P_DIR_SENT) { |
199 | 0 | key[1].key = &pinfo->srcport; |
200 | 0 | key[2].key = &pinfo->destport; |
201 | 0 | } else { |
202 | 0 | key[1].key = &pinfo->destport; |
203 | 0 | key[2].key = &pinfo->srcport; |
204 | 0 | } |
205 | 0 | key[3].length = 1; |
206 | 0 | key[3].key = &side_id; |
207 | 0 | key[4].length = 1; |
208 | 0 | key[4].key = &frame_number; |
209 | 0 | key[5].length = 0; |
210 | 0 | key[5].key = NULL; |
211 | 0 | } |
212 | |
|
213 | 0 | if (direction == P2P_DIR_SENT) |
214 | 0 | if (cmd == A_CLSE) |
215 | 0 | side_id = arg1; /* OUT: local id */ |
216 | 0 | else |
217 | 0 | side_id = arg0; /* OUT: local id */ |
218 | 0 | else |
219 | 0 | side_id = arg1; /* IN: remote id */ |
220 | |
|
221 | 0 | if (cmd == A_OPEN) { |
222 | 0 | service_data = wmem_new(wmem_file_scope(), service_data_t); |
223 | |
|
224 | 0 | service_data->start_in_frame = pinfo->num; |
225 | 0 | service_data->close_local_in_frame = max_in_frame; |
226 | 0 | service_data->close_remote_in_frame = max_in_frame; |
227 | |
|
228 | 0 | service_data->local_id = arg0; |
229 | 0 | service_data->remote_id = arg1; |
230 | |
|
231 | 0 | service_data->service = "unknown"; |
232 | |
|
233 | 0 | wmem_tree_insert32_array(service_info, key, service_data); |
234 | 0 | } |
235 | |
|
236 | 0 | command_data = wmem_new(wmem_file_scope(), command_data_t); |
237 | |
|
238 | 0 | command_data->command = cmd; |
239 | 0 | command_data->arg0 = arg0; |
240 | 0 | command_data->arg1 = arg1; |
241 | |
|
242 | 0 | command_data->command_in_frame = pinfo->num; |
243 | 0 | command_data->response_in_frame = max_in_frame; |
244 | |
|
245 | 0 | command_data->crc32 = crc32; |
246 | 0 | command_data->data_length = data_length; |
247 | 0 | if (data_length == 0) |
248 | 0 | command_data->completed_in_frame = pinfo->num; |
249 | 0 | else |
250 | 0 | command_data->completed_in_frame = max_in_frame; |
251 | 0 | command_data->reassemble_data_length = 0; |
252 | 0 | command_data->reassemble_data = (uint8_t *) wmem_alloc(wmem_file_scope(), command_data->data_length); |
253 | 0 | command_data->reassemble_error_in_frame = 0; |
254 | |
|
255 | 0 | key[3].length = 1; |
256 | 0 | key[3].key = &frame_number; |
257 | 0 | key[4].length = 0; |
258 | 0 | key[4].key = NULL; |
259 | 0 | wmem_tree_insert32_array(command_info, key, command_data); |
260 | |
|
261 | 0 | if (direction == P2P_DIR_SENT) |
262 | 0 | if (command_data->command == A_CLSE) |
263 | 0 | side_id = command_data->arg1; /* OUT: local id */ |
264 | 0 | else |
265 | 0 | side_id = command_data->arg0; /* OUT: local id */ |
266 | 0 | else |
267 | 0 | side_id = command_data->arg1; /* IN: remote id */ |
268 | |
|
269 | 0 | key[3].length = 1; |
270 | 0 | key[3].key = &side_id; |
271 | 0 | key[4].length = 0; |
272 | 0 | key[4].key = NULL; |
273 | |
|
274 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(service_info, key); |
275 | 0 | if (wmem_tree) { |
276 | 0 | service_data = (service_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
277 | 0 | } |
278 | |
|
279 | 0 | if (cmd == A_OKAY) { |
280 | 0 | if (!service_data) { |
281 | 0 | if (direction == P2P_DIR_SENT) |
282 | 0 | side_id = command_data->arg0; /* OUT: local id */ |
283 | 0 | else |
284 | 0 | side_id = command_data->arg1; /* IN: remote id */ |
285 | |
|
286 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(service_info, key); |
287 | 0 | if (wmem_tree) { |
288 | 0 | service_data = (service_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
289 | 0 | } |
290 | 0 | } |
291 | |
|
292 | 0 | if (service_data && service_data->remote_id == 0 && direction == P2P_DIR_RECV) { |
293 | 0 | if (direction == P2P_DIR_SENT) { |
294 | 0 | service_data->remote_id = arg1; |
295 | 0 | } else { |
296 | 0 | service_data->remote_id = arg0; |
297 | 0 | } |
298 | |
|
299 | 0 | side_id = service_data->remote_id; |
300 | |
|
301 | 0 | key[4].length = 1; |
302 | 0 | key[4].key = &frame_number; |
303 | 0 | key[5].length = 0; |
304 | 0 | key[5].key = NULL; |
305 | |
|
306 | 0 | wmem_tree_insert32_array(service_info, key, service_data); |
307 | 0 | } |
308 | 0 | } else if (cmd == A_CLSE) { |
309 | 0 | if (service_data) { |
310 | 0 | if (direction == P2P_DIR_RECV && service_data->local_id == arg1) |
311 | 0 | service_data->close_local_in_frame = pinfo->num; |
312 | 0 | else if (direction == P2P_DIR_SENT && service_data->remote_id == arg1) |
313 | 0 | service_data->close_remote_in_frame = pinfo->num; |
314 | 0 | } |
315 | 0 | } |
316 | |
|
317 | 0 | DISSECTOR_ASSERT(returned_service_data && returned_command_data); |
318 | 0 | *returned_service_data = service_data; |
319 | 0 | *returned_command_data = command_data; |
320 | 0 | } |
321 | | |
322 | | static int |
323 | | dissect_adb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
324 | 0 | { |
325 | 0 | proto_item *main_item; |
326 | 0 | proto_tree *main_tree; |
327 | 0 | proto_item *arg0_item; |
328 | 0 | proto_tree *arg0_tree; |
329 | 0 | proto_item *arg1_item; |
330 | 0 | proto_tree *arg1_tree; |
331 | 0 | proto_item *magic_item; |
332 | 0 | proto_item *crc_item; |
333 | 0 | proto_tree *crc_tree = NULL; |
334 | 0 | proto_item *sub_item; |
335 | 0 | int offset = 0; |
336 | 0 | uint32_t command; |
337 | 0 | uint32_t arg0; |
338 | 0 | uint32_t arg1; |
339 | 0 | uint32_t data_length = 0; |
340 | 0 | uint32_t crc32 = 0; |
341 | 0 | urb_info_t *urb = NULL; |
342 | 0 | wmem_tree_key_t key[5]; |
343 | 0 | uint32_t interface_id; |
344 | 0 | uint32_t bus_id; |
345 | 0 | uint32_t device_address; |
346 | 0 | uint32_t side_id; |
347 | 0 | uint32_t frame_number; |
348 | 0 | bool is_command = true; |
349 | 0 | bool is_next_fragment = false; |
350 | 0 | bool is_service = false; |
351 | 0 | int proto; |
352 | 0 | int direction = P2P_DIR_UNKNOWN; |
353 | 0 | wmem_tree_t *wmem_tree; |
354 | 0 | command_data_t *command_data = NULL; |
355 | 0 | service_data_t *service_data = NULL; |
356 | |
|
357 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "ADB"); |
358 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
359 | |
|
360 | 0 | main_item = proto_tree_add_item(tree, proto_adb, tvb, offset, -1, ENC_NA); |
361 | 0 | main_tree = proto_item_add_subtree(main_item, ett_adb); |
362 | |
|
363 | 0 | frame_number = pinfo->num; |
364 | | |
365 | | /* XXX: Why? If interface is USB only first try is correct |
366 | | * (and seems strange...), in other cases standard check for |
367 | | * previous protocol is correct */ |
368 | 0 | proto = (int) GPOINTER_TO_INT(wmem_list_frame_data(/*wmem_list_frame_prev*/(wmem_list_tail(pinfo->layers)))); |
369 | 0 | if (proto != proto_usb) { |
370 | 0 | proto = (int) GPOINTER_TO_INT(wmem_list_frame_data(wmem_list_frame_prev(wmem_list_tail(pinfo->layers)))); |
371 | 0 | } |
372 | |
|
373 | 0 | if (proto == proto_usb) { |
374 | 0 | urb = (urb_info_t *) data; |
375 | 0 | DISSECTOR_ASSERT(urb); |
376 | |
|
377 | 0 | direction = urb->direction; |
378 | 0 | } else if (proto == proto_tcp) { |
379 | 0 | if (pinfo->destport == ADB_TCP_PORT) |
380 | 0 | direction = P2P_DIR_SENT; |
381 | 0 | else |
382 | 0 | direction = P2P_DIR_RECV; |
383 | 0 | } else { |
384 | 0 | return offset; |
385 | 0 | } |
386 | | |
387 | 0 | if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) |
388 | 0 | interface_id = pinfo->rec->rec_header.packet_header.interface_id; |
389 | 0 | else |
390 | 0 | interface_id = 0; |
391 | |
|
392 | 0 | if (proto == proto_usb) { |
393 | 0 | bus_id = urb->bus_id; |
394 | 0 | device_address = urb->device_address; |
395 | |
|
396 | 0 | key[0].length = 1; |
397 | 0 | key[0].key = &interface_id; |
398 | 0 | key[1].length = 1; |
399 | 0 | key[1].key = &bus_id; |
400 | 0 | key[2].length = 1; |
401 | 0 | key[2].key = &device_address; |
402 | 0 | key[3].length = 0; |
403 | 0 | key[3].key = NULL; |
404 | 0 | } else { /* tcp */ |
405 | 0 | key[0].length = 1; |
406 | 0 | key[0].key = &interface_id; |
407 | 0 | key[1].length = 1; |
408 | 0 | key[2].length = 1; |
409 | 0 | if (direction == P2P_DIR_SENT) { |
410 | 0 | key[1].key = &pinfo->srcport; |
411 | 0 | key[2].key = &pinfo->destport; |
412 | 0 | } else { |
413 | 0 | key[1].key = &pinfo->destport; |
414 | 0 | key[2].key = &pinfo->srcport; |
415 | 0 | } |
416 | 0 | key[3].length = 0; |
417 | 0 | key[3].key = NULL; |
418 | 0 | } |
419 | |
|
420 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(command_info, key); |
421 | 0 | if (wmem_tree) { |
422 | 0 | command_data = (command_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
423 | 0 | if (command_data && command_data->completed_in_frame >= frame_number && |
424 | 0 | command_data->command_in_frame <= frame_number) { |
425 | |
|
426 | 0 | if (command_data->command_in_frame != frame_number) { |
427 | 0 | is_command = false; |
428 | 0 | is_next_fragment = true; |
429 | 0 | } |
430 | |
|
431 | 0 | data_length = command_data->data_length; |
432 | 0 | crc32 = command_data->crc32; |
433 | |
|
434 | 0 | if (direction == P2P_DIR_SENT) { |
435 | 0 | if (command_data->command == A_CLSE) |
436 | 0 | side_id = command_data->arg1; /* OUT: local id */ |
437 | 0 | else |
438 | 0 | side_id = command_data->arg0; /* OUT: local id */ |
439 | 0 | } else { |
440 | 0 | side_id = command_data->arg1; /* IN: remote id */ |
441 | 0 | } |
442 | |
|
443 | 0 | key[3].length = 1; |
444 | 0 | key[3].key = &side_id; |
445 | 0 | key[4].length = 0; |
446 | 0 | key[4].key = NULL; |
447 | |
|
448 | 0 | wmem_tree = (wmem_tree_t *) wmem_tree_lookup32_array(service_info, key); |
449 | 0 | if (wmem_tree) { |
450 | 0 | service_data = (service_data_t *) wmem_tree_lookup32_le(wmem_tree, frame_number); |
451 | 0 | if (service_data && command_data->command == A_OPEN) { |
452 | 0 | is_service = true; |
453 | 0 | } |
454 | 0 | } |
455 | 0 | } |
456 | 0 | } |
457 | | |
458 | | /* Simple heuristics to check if packet is command or data */ |
459 | 0 | if ((command_data && command_data->completed_in_frame <= frame_number) || !command_data) { |
460 | 0 | if (tvb_reported_length(tvb) < 24) { |
461 | 0 | is_command = false; |
462 | 0 | } else if (tvb_reported_length(tvb) >= 24) { |
463 | 0 | command = tvb_get_letohl(tvb, offset); |
464 | |
|
465 | 0 | if (command != A_SYNC && command != A_CLSE && command != A_WRTE && |
466 | 0 | command != A_AUTH && command != A_CNXN && command != A_OPEN && command != A_OKAY) |
467 | 0 | is_command = false; |
468 | 0 | else if (command != (0xFFFFFFFF ^ tvb_get_letohl(tvb, offset + 20))) |
469 | 0 | is_command = false; |
470 | |
|
471 | 0 | if (is_command) { |
472 | 0 | data_length = tvb_get_letohl(tvb, offset + 12); |
473 | 0 | crc32 = tvb_get_letohl(tvb, offset + 16); |
474 | 0 | } |
475 | 0 | if (command == A_OPEN) is_service = true; |
476 | 0 | } |
477 | 0 | } |
478 | |
|
479 | 0 | if (service_data && !(command_data->command == A_OPEN && is_next_fragment)) { |
480 | 0 | sub_item = proto_tree_add_string(main_tree, hf_service, tvb, offset, 0, service_data->service); |
481 | 0 | proto_item_set_generated(sub_item); |
482 | 0 | } |
483 | |
|
484 | 0 | if (service_data) { |
485 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_service_start_in_frame, tvb, offset, 0, service_data->start_in_frame); |
486 | 0 | proto_item_set_generated(sub_item); |
487 | |
|
488 | 0 | if (service_data->close_local_in_frame < max_in_frame) { |
489 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_close_local_in_frame, tvb, offset, 0, service_data->close_local_in_frame); |
490 | 0 | proto_item_set_generated(sub_item); |
491 | 0 | } |
492 | |
|
493 | 0 | if (service_data->close_remote_in_frame < max_in_frame) { |
494 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_close_remote_in_frame, tvb, offset, 0, service_data->close_remote_in_frame); |
495 | 0 | proto_item_set_generated(sub_item); |
496 | 0 | } |
497 | 0 | } |
498 | |
|
499 | 0 | if (is_command) { |
500 | 0 | proto_tree_add_item(main_tree, hf_command, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
501 | 0 | command = tvb_get_letohl(tvb, offset); |
502 | 0 | offset += 4; |
503 | |
|
504 | 0 | col_append_str(pinfo->cinfo, COL_INFO, val_to_str_const(command, command_vals, "Unknown command")); |
505 | |
|
506 | 0 | arg0_item = proto_tree_add_item(main_tree, hf_argument_0, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
507 | 0 | arg0_tree = proto_item_add_subtree(arg0_item, ett_adb_arg0); |
508 | 0 | arg0 = tvb_get_letohl(tvb, offset); |
509 | 0 | offset += 4; |
510 | |
|
511 | 0 | arg1_item = proto_tree_add_item(main_tree, hf_argument_1, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
512 | 0 | arg1_tree = proto_item_add_subtree(arg1_item, ett_adb_arg1); |
513 | 0 | arg1 = tvb_get_letohl(tvb, offset); |
514 | 0 | offset += 4; |
515 | |
|
516 | 0 | switch (command) { |
517 | 0 | case A_CNXN: |
518 | 0 | proto_tree_add_item(arg0_tree, hf_version, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
519 | 0 | proto_tree_add_item(arg1_tree, hf_max_data, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
520 | |
|
521 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(version=%u.%u.%u, max_data=%u)", tvb_get_uint8(tvb, offset - 5), tvb_get_uint8(tvb, offset - 6), tvb_get_letohs(tvb, offset - 7), tvb_get_letohl(tvb, offset - 4)); |
522 | 0 | break; |
523 | 0 | case A_AUTH: |
524 | 0 | proto_tree_add_item(arg0_tree, hf_auth_type, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
525 | 0 | proto_tree_add_item(arg1_tree, hf_zero, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
526 | |
|
527 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(type=%s, 0)", val_to_str_const(tvb_get_letohl(tvb, offset - 8), auth_type_vals, "Unknown")); |
528 | 0 | break; |
529 | 0 | case A_OPEN: |
530 | 0 | proto_tree_add_item(arg0_tree, hf_local_id, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
531 | 0 | proto_tree_add_item(arg1_tree, hf_zero, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
532 | |
|
533 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(local=%u, 0)", tvb_get_letohl(tvb, offset - 8)); |
534 | 0 | break; |
535 | 0 | case A_WRTE: |
536 | 0 | proto_tree_add_item(arg0_tree, hf_local_id, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
537 | 0 | proto_tree_add_item(arg1_tree, hf_remote_id, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
538 | |
|
539 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(local=%u, remote=%u)", arg0, arg1); |
540 | 0 | break; |
541 | 0 | case A_CLSE: |
542 | 0 | case A_OKAY: |
543 | 0 | proto_tree_add_item(arg0_tree, hf_local_id, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
544 | 0 | proto_tree_add_item(arg1_tree, hf_remote_id, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
545 | |
|
546 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(local=%u, remote=%u)", tvb_get_letohl(tvb, offset - 8), tvb_get_letohl(tvb, offset - 4)); |
547 | 0 | break; |
548 | 0 | case A_SYNC: |
549 | 0 | proto_tree_add_item(arg0_tree, hf_online, tvb, offset - 8, 4, ENC_LITTLE_ENDIAN); |
550 | 0 | proto_tree_add_item(arg1_tree, hf_sequence, tvb, offset - 4, 4, ENC_LITTLE_ENDIAN); |
551 | |
|
552 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "(online=%s, sequence=%u)", tvb_get_letohl(tvb, offset - 8) ? "Yes": "No", tvb_get_letohl(tvb, offset - 4)); |
553 | 0 | break; |
554 | 0 | } |
555 | | |
556 | 0 | proto_tree_add_item(main_tree, hf_data_length, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
557 | 0 | offset += 4; |
558 | |
|
559 | 0 | if (data_length > 0) |
560 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " length=%u ", data_length); |
561 | |
|
562 | 0 | crc_item = proto_tree_add_item(main_tree, hf_data_crc32, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
563 | 0 | crc_tree = proto_item_add_subtree(crc_item, ett_adb_crc); |
564 | 0 | crc32 = tvb_get_letohl(tvb, offset); |
565 | 0 | offset += 4; |
566 | |
|
567 | 0 | magic_item = proto_tree_add_item(main_tree, hf_magic, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
568 | 0 | if ((tvb_get_letohl(tvb, offset) ^ 0xFFFFFFFF) != command) { |
569 | 0 | proto_tree *expert_tree; |
570 | |
|
571 | 0 | expert_tree = proto_item_add_subtree(magic_item, ett_adb_magic); |
572 | 0 | proto_tree_add_expert(expert_tree, pinfo, &ei_invalid_magic, tvb, offset, 4); |
573 | 0 | } |
574 | |
|
575 | 0 | if (!pinfo->fd->visited) |
576 | 0 | save_command(command, arg0, arg1, data_length, crc32, service_data, proto, data, pinfo, &service_data, &command_data); |
577 | 0 | offset += 4; |
578 | 0 | } |
579 | | |
580 | 0 | if (!pinfo->fd->visited && command_data) { |
581 | 0 | if (command_data->command_in_frame != frame_number) { |
582 | 0 | is_command = false; |
583 | 0 | is_next_fragment = true; |
584 | 0 | } |
585 | |
|
586 | 0 | data_length = command_data->data_length; |
587 | 0 | crc32 = command_data->crc32; |
588 | |
|
589 | 0 | if ((command_data->command_in_frame != frame_number && tvb_captured_length(tvb) == data_length) || |
590 | 0 | (command_data->command_in_frame == frame_number && tvb_captured_length(tvb) == data_length + 24) |
591 | 0 | ) { |
592 | 0 | command_data->reassemble_data_length = command_data->data_length; |
593 | 0 | command_data->completed_in_frame = frame_number; |
594 | 0 | } |
595 | 0 | } |
596 | |
|
597 | 0 | if (is_next_fragment && command_data) { |
598 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_command_in_frame, tvb, offset, 0, command_data->command_in_frame); |
599 | 0 | proto_item_set_generated(sub_item); |
600 | |
|
601 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_command, tvb, offset, 0, command_data->command); |
602 | 0 | proto_item_set_generated(sub_item); |
603 | |
|
604 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_data_length, tvb, offset, 0, command_data->data_length); |
605 | 0 | proto_item_set_generated(sub_item); |
606 | |
|
607 | 0 | crc_item = proto_tree_add_uint(main_tree, hf_data_crc32, tvb, offset, 0, command_data->crc32); |
608 | 0 | crc_tree = proto_item_add_subtree(crc_item, ett_adb_crc); |
609 | 0 | proto_item_set_generated(crc_item); |
610 | 0 | } |
611 | |
|
612 | 0 | if (command_data && command_data->completed_in_frame != frame_number) { |
613 | 0 | sub_item = proto_tree_add_uint(main_tree, hf_completed_in_frame, tvb, offset, 0, command_data->completed_in_frame); |
614 | 0 | proto_item_set_generated(sub_item); |
615 | 0 | } |
616 | | |
617 | |
|
618 | 0 | if (tvb_captured_length_remaining(tvb, offset) > 0 && (!is_command || data_length > 0)) { |
619 | 0 | uint32_t crc = 0; |
620 | 0 | uint32_t i_offset; |
621 | | |
622 | | /* First pass: store message payload (usually a single packet, but |
623 | | * potentially multiple fragments). */ |
624 | 0 | if (!pinfo->fd->visited && command_data && command_data->reassemble_data_length < command_data->data_length) { |
625 | 0 | unsigned chunklen = tvb_captured_length_remaining(tvb, offset); |
626 | 0 | if (chunklen > command_data->data_length - command_data->reassemble_data_length) { |
627 | 0 | chunklen = command_data->data_length - command_data->reassemble_data_length; |
628 | | /* This should never happen, but when it does, then either we |
629 | | * have a malicious application OR we failed to correctly match |
630 | | * this payload with a message header. */ |
631 | 0 | command_data->reassemble_error_in_frame = frame_number; |
632 | 0 | } |
633 | |
|
634 | 0 | tvb_memcpy(tvb, command_data->reassemble_data + command_data->reassemble_data_length, offset, chunklen); |
635 | 0 | command_data->reassemble_data_length += chunklen; |
636 | |
|
637 | 0 | if (command_data->reassemble_data_length >= command_data->data_length) |
638 | 0 | command_data->completed_in_frame = frame_number; |
639 | 0 | } |
640 | |
|
641 | 0 | if (command_data && frame_number == command_data->reassemble_error_in_frame) { |
642 | | /* data reassembly error was detected in the first pass. */ |
643 | 0 | proto_tree_add_expert(main_tree, pinfo, &ei_invalid_data, tvb, offset, -1); |
644 | 0 | } |
645 | |
|
646 | 0 | if ((!pinfo->fd->visited && command_data && command_data->reassemble_data_length < command_data->data_length) || data_length > (uint32_t) tvb_captured_length_remaining(tvb, offset)) { /* need reassemble */ |
647 | 0 | proto_tree_add_item(main_tree, hf_data_fragment, tvb, offset, -1, ENC_NA); |
648 | 0 | col_append_str(pinfo->cinfo, COL_INFO, "Data Fragment"); |
649 | 0 | offset = tvb_captured_length(tvb); |
650 | |
|
651 | 0 | if (service_data && command_data && command_data->reassemble_data_length >= command_data->data_length && frame_number == command_data->completed_in_frame) { |
652 | 0 | tvbuff_t *next_tvb; |
653 | 0 | adb_service_data_t adb_service_data; |
654 | |
|
655 | 0 | next_tvb = tvb_new_child_real_data(tvb, command_data->reassemble_data, command_data->reassemble_data_length, command_data->reassemble_data_length); |
656 | 0 | add_new_data_source(pinfo, next_tvb, "ADB Reassembled Data"); |
657 | |
|
658 | 0 | adb_service_data.service = service_data->service; |
659 | 0 | adb_service_data.direction = direction; |
660 | |
|
661 | 0 | adb_service_data.session_key_length = 3; |
662 | 0 | adb_service_data.session_key = (uint32_t *) wmem_alloc(pinfo->pool, adb_service_data.session_key_length * sizeof(uint32_t)); |
663 | 0 | adb_service_data.session_key[0] = interface_id; |
664 | |
|
665 | 0 | if (proto == proto_usb) { |
666 | 0 | adb_service_data.session_key[1] = urb->bus_id; |
667 | 0 | adb_service_data.session_key[2] = urb->device_address; |
668 | 0 | } else { /* tcp */ |
669 | 0 | if (direction == P2P_DIR_SENT) { |
670 | 0 | adb_service_data.session_key[1] = pinfo->srcport; |
671 | 0 | adb_service_data.session_key[2] = pinfo->destport; |
672 | 0 | } else { |
673 | 0 | adb_service_data.session_key[1] = pinfo->destport; |
674 | 0 | adb_service_data.session_key[2] = pinfo->srcport; |
675 | 0 | } |
676 | 0 | } |
677 | |
|
678 | 0 | call_dissector_with_data(adb_service_handle, next_tvb, pinfo, tree, &adb_service_data); |
679 | 0 | } |
680 | 0 | } else { /* full message */ |
681 | 0 | for (i_offset = 0; i_offset < data_length; ++i_offset) |
682 | 0 | crc += tvb_get_uint8(tvb, offset + i_offset); |
683 | |
|
684 | 0 | if (crc32 > 0 && crc32 != crc) |
685 | 0 | proto_tree_add_expert(crc_tree, pinfo, &ei_invalid_crc, tvb, offset, -1); |
686 | |
|
687 | 0 | if (is_service) { |
688 | 0 | proto_tree_add_item(main_tree, hf_service, tvb, offset, -1, ENC_ASCII); |
689 | 0 | if (!pinfo->fd->visited && service_data) { |
690 | 0 | service_data->service = (char *) tvb_get_stringz_enc(wmem_file_scope(), tvb, offset, NULL, ENC_ASCII); |
691 | 0 | } |
692 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Service: %s", tvb_get_stringz_enc(pinfo->pool, tvb, offset, NULL, ENC_ASCII)); |
693 | 0 | offset = tvb_captured_length(tvb); |
694 | 0 | } else if (command_data && command_data->command == A_CNXN) { |
695 | 0 | const uint8_t *info; |
696 | | |
697 | | /* |
698 | | * Format: "<systemtype>:<serialno>:<banner>". |
699 | | * Previously adb used "device::ro.product.name=...;...;\0" as |
700 | | * human-readable banner, but since platform/system/core commit |
701 | | * 1792c23cb8 (2015-05-18) it is a ";"-separated feature list. |
702 | | */ |
703 | |
|
704 | 0 | proto_tree_add_item_ret_string(main_tree, hf_connection_info, tvb, offset, -1, ENC_ASCII | ENC_NA, pinfo->pool, &info); |
705 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Connection Info: %s", info); |
706 | 0 | offset = tvb_captured_length(tvb); |
707 | 0 | } else { |
708 | 0 | col_append_str(pinfo->cinfo, COL_INFO, "Data"); |
709 | | |
710 | | /* Decode service payload */ |
711 | 0 | if (service_data) { |
712 | 0 | tvbuff_t *next_tvb; |
713 | 0 | adb_service_data_t adb_service_data; |
714 | |
|
715 | 0 | adb_service_data.service = service_data->service; |
716 | 0 | adb_service_data.direction = direction; |
717 | |
|
718 | 0 | adb_service_data.session_key_length = 3; |
719 | 0 | adb_service_data.session_key = (uint32_t *) wmem_alloc(pinfo->pool, adb_service_data.session_key_length * sizeof(uint32_t)); |
720 | 0 | adb_service_data.session_key[0] = interface_id; |
721 | |
|
722 | 0 | if (proto == proto_usb) { |
723 | 0 | adb_service_data.session_key[1] = urb->bus_id; |
724 | 0 | adb_service_data.session_key[2] = urb->device_address; |
725 | 0 | } else { /* tcp */ |
726 | 0 | if (direction == P2P_DIR_SENT) { |
727 | 0 | adb_service_data.session_key[1] = pinfo->srcport; |
728 | 0 | adb_service_data.session_key[2] = pinfo->destport; |
729 | 0 | } else { |
730 | 0 | adb_service_data.session_key[1] = pinfo->destport; |
731 | 0 | adb_service_data.session_key[2] = pinfo->srcport; |
732 | 0 | } |
733 | 0 | } |
734 | |
|
735 | 0 | next_tvb = tvb_new_subset_remaining(tvb, offset); |
736 | 0 | call_dissector_with_data(adb_service_handle, next_tvb, pinfo, tree, &adb_service_data); |
737 | |
|
738 | 0 | } else { |
739 | 0 | proto_item *data_item; |
740 | 0 | char *data_str; |
741 | |
|
742 | 0 | data_item = proto_tree_add_item(main_tree, hf_data, tvb, offset, data_length, ENC_NA); |
743 | 0 | data_str = tvb_format_text(pinfo->pool, tvb, offset, data_length); |
744 | 0 | proto_item_append_text(data_item, ": %s", data_str); |
745 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Raw: %s", data_str); |
746 | 0 | } |
747 | |
|
748 | 0 | offset = tvb_captured_length(tvb); |
749 | 0 | } |
750 | 0 | } |
751 | 0 | } |
752 | |
|
753 | 0 | return offset; |
754 | 0 | } |
755 | | |
756 | | void |
757 | | proto_register_adb(void) |
758 | 14 | { |
759 | 14 | module_t *module; |
760 | 14 | expert_module_t *expert_module; |
761 | | |
762 | 14 | static hf_register_info hf[] = { |
763 | 14 | { &hf_command, |
764 | 14 | { "Command", "adb.command", |
765 | 14 | FT_UINT32, BASE_HEX, VALS(command_vals), 0x00, |
766 | 14 | NULL, HFILL } |
767 | 14 | }, |
768 | 14 | { &hf_argument_0, |
769 | 14 | { "Argument 0", "adb.argument.0", |
770 | 14 | FT_UINT32, BASE_HEX, NULL, 0x00, |
771 | 14 | NULL, HFILL } |
772 | 14 | }, |
773 | 14 | { &hf_argument_1, |
774 | 14 | { "Argument 1", "adb.argument.1", |
775 | 14 | FT_UINT32, BASE_HEX, NULL, 0x00, |
776 | 14 | NULL, HFILL } |
777 | 14 | }, |
778 | 14 | { &hf_data_length, |
779 | 14 | { "Data Length", "adb.data_length", |
780 | 14 | FT_UINT32, BASE_DEC, NULL, 0x00, |
781 | 14 | NULL, HFILL } |
782 | 14 | }, |
783 | 14 | { &hf_data_crc32, |
784 | 14 | { "Data CRC32", "adb.data_crc32", |
785 | 14 | FT_UINT32, BASE_HEX, NULL, 0x00, |
786 | 14 | NULL, HFILL } |
787 | 14 | }, |
788 | 14 | { &hf_magic, |
789 | 14 | { "Magic", "adb.magic", |
790 | 14 | FT_UINT32, BASE_HEX, VALS(magic_vals), 0x00, |
791 | 14 | NULL, HFILL } |
792 | 14 | }, |
793 | 14 | { &hf_version, |
794 | 14 | { "Version", "adb.version", |
795 | 14 | FT_UINT32, BASE_HEX, NULL, 0x00, |
796 | 14 | NULL, HFILL } |
797 | 14 | }, |
798 | 14 | { &hf_max_data, |
799 | 14 | { "Max Data", "adb.max_data", |
800 | 14 | FT_UINT32, BASE_DEC, NULL, 0x00, |
801 | 14 | NULL, HFILL } |
802 | 14 | }, |
803 | 14 | { &hf_auth_type, |
804 | 14 | { "Type", "adb.auth_type", |
805 | 14 | FT_UINT32, BASE_HEX, VALS(auth_type_vals), 0x00, |
806 | 14 | NULL, HFILL } |
807 | 14 | }, |
808 | 14 | { &hf_online, |
809 | 14 | { "Online", "adb.online", |
810 | 14 | FT_BOOLEAN, BASE_NONE, TFS(&tfs_no_yes), 0x00, |
811 | 14 | NULL, HFILL } |
812 | 14 | }, |
813 | 14 | { &hf_sequence, |
814 | 14 | { "Sequence", "adb.sequence", |
815 | 14 | FT_UINT32, BASE_DEC, NULL, 0x00, |
816 | 14 | NULL, HFILL } |
817 | 14 | }, |
818 | 14 | { &hf_zero, |
819 | 14 | { "Zero", "adb.zero", |
820 | 14 | FT_UINT32, BASE_HEX, NULL, 0x00, |
821 | 14 | NULL, HFILL } |
822 | 14 | }, |
823 | 14 | { &hf_local_id, |
824 | 14 | { "Local ID", "adb.local_id", |
825 | 14 | FT_UINT32, BASE_DEC, NULL, 0x00, |
826 | 14 | NULL, HFILL } |
827 | 14 | }, |
828 | 14 | { &hf_remote_id, |
829 | 14 | { "Remote ID", "adb.remote_id", |
830 | 14 | FT_UINT32, BASE_DEC, NULL, 0x00, |
831 | 14 | NULL, HFILL } |
832 | 14 | }, |
833 | 14 | { &hf_data, |
834 | 14 | { "Data", "adb.data", |
835 | 14 | FT_NONE, BASE_NONE, NULL, 0x00, |
836 | 14 | NULL, HFILL } |
837 | 14 | }, |
838 | 14 | { &hf_service, |
839 | 14 | { "Service", "adb.service", |
840 | 14 | FT_STRING, BASE_NONE, NULL, 0x00, |
841 | 14 | NULL, HFILL } |
842 | 14 | }, |
843 | 14 | { &hf_data_fragment, |
844 | 14 | { "Data Fragment", "adb.data_fragment", |
845 | 14 | FT_NONE, BASE_NONE, NULL, 0x00, |
846 | 14 | NULL, HFILL } |
847 | 14 | }, |
848 | 14 | { &hf_service_start_in_frame, |
849 | 14 | { "Service Start in Frame", "adb.service_start_in_frame", |
850 | 14 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
851 | 14 | NULL, HFILL } |
852 | 14 | }, |
853 | 14 | { &hf_close_local_in_frame, |
854 | 14 | { "Local Service Close in Frame", "adb.close_local_in_frame", |
855 | 14 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
856 | 14 | NULL, HFILL } |
857 | 14 | }, |
858 | 14 | { &hf_close_remote_in_frame, |
859 | 14 | { "Remote Service Close in Frame", "adb.close_remote_in_frame", |
860 | 14 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
861 | 14 | NULL, HFILL } |
862 | 14 | }, |
863 | 14 | { &hf_command_in_frame, |
864 | 14 | { "Command in Frame", "adb.command_in_frame", |
865 | 14 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
866 | 14 | NULL, HFILL } |
867 | 14 | }, |
868 | 14 | { &hf_completed_in_frame, |
869 | 14 | { "Completed in Frame", "adb.completed_in_frame", |
870 | 14 | FT_FRAMENUM, BASE_NONE, NULL, 0x00, |
871 | 14 | NULL, HFILL } |
872 | 14 | }, |
873 | 14 | { &hf_connection_info, |
874 | 14 | { "Info", "adb.connection_info", |
875 | 14 | FT_STRING, BASE_NONE, NULL, 0x00, |
876 | 14 | NULL, HFILL } |
877 | 14 | } |
878 | 14 | }; |
879 | | |
880 | 14 | static int *ett[] = { |
881 | 14 | &ett_adb, |
882 | 14 | &ett_adb_arg0, |
883 | 14 | &ett_adb_arg1, |
884 | 14 | &ett_adb_crc, |
885 | 14 | &ett_adb_magic |
886 | 14 | }; |
887 | | |
888 | 14 | static ei_register_info ei[] = { |
889 | 14 | { &ei_invalid_magic, { "adb.expert.invalid_magic", PI_PROTOCOL, PI_WARN, "Invalid Magic", EXPFILL }}, |
890 | 14 | { &ei_invalid_crc, { "adb.expert.crc_error", PI_PROTOCOL, PI_ERROR, "CRC32 Error", EXPFILL }}, |
891 | 14 | { &ei_invalid_data, { "adb.expert.data_error", PI_PROTOCOL, PI_ERROR, "Mismatch between message payload size and data length", EXPFILL }}, |
892 | 14 | }; |
893 | | |
894 | 14 | command_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
895 | 14 | service_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
896 | | |
897 | 14 | proto_adb = proto_register_protocol("Android Debug Bridge", "ADB", "adb"); |
898 | 14 | adb_handle = register_dissector("adb", dissect_adb, proto_adb); |
899 | | |
900 | 14 | proto_register_field_array(proto_adb, hf, array_length(hf)); |
901 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
902 | 14 | expert_module = expert_register_protocol(proto_adb); |
903 | 14 | expert_register_field_array(expert_module, ei, array_length(ei)); |
904 | | |
905 | 14 | module = prefs_register_protocol(proto_adb, NULL); |
906 | 14 | prefs_register_static_text_preference(module, "version", |
907 | 14 | "ADB protocol version is compatible prior to: adb 1.0.31", |
908 | 14 | "Version of protocol supported by this dissector."); |
909 | 14 | } |
910 | | |
911 | | void |
912 | | proto_reg_handoff_adb(void) |
913 | 14 | { |
914 | 14 | adb_service_handle = find_dissector_add_dependency("adb_service", proto_adb); |
915 | | |
916 | 14 | dissector_add_for_decode_as_with_preference("tcp.port", adb_handle); |
917 | 14 | dissector_add_for_decode_as("usb.device", adb_handle); |
918 | 14 | dissector_add_for_decode_as("usb.product", adb_handle); |
919 | 14 | dissector_add_for_decode_as("usb.protocol", adb_handle); |
920 | | |
921 | 14 | proto_tcp = proto_get_id_by_filter_name("tcp"); |
922 | 14 | proto_usb = proto_get_id_by_filter_name("usb"); |
923 | 14 | } |
924 | | |
925 | | /* |
926 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
927 | | * |
928 | | * Local variables: |
929 | | * c-basic-offset: 4 |
930 | | * tab-width: 8 |
931 | | * indent-tabs-mode: nil |
932 | | * End: |
933 | | * |
934 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
935 | | * :indentSize=4:tabSize=8:noTabs=true: |
936 | | */ |