|
8 | 8 | Default USB frames & Basic implementation
|
9 | 9 | """
|
10 | 10 |
|
11 |
| -# TODO: support USB headers for Linux and Darwin (usbmon/netmon) |
| 11 | +# TODO: support USB headers for Darwin (netmon) |
12 | 12 | # https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-usb.c # noqa: E501
|
13 | 13 |
|
14 | 14 | import re
|
15 | 15 | import subprocess
|
| 16 | +from enum import Enum, IntEnum |
16 | 17 |
|
17 | 18 | from scapy.config import conf
|
18 | 19 | from scapy.consts import WINDOWS
|
19 | 20 | from scapy.compat import chb, plain_str
|
20 |
| -from scapy.data import MTU, DLT_USBPCAP |
| 21 | +from scapy.data import DLT_USB_LINUX, DLT_USB_LINUX_MMAPPED, DLT_USBPCAP, MTU |
21 | 22 | from scapy.error import warning
|
22 |
| -from scapy.fields import ByteField, XByteField, ByteEnumField, LEShortField, \ |
23 |
| - LEShortEnumField, LEIntField, LEIntEnumField, XLELongField, \ |
24 |
| - LenField |
| 23 | +from scapy.fields import ByteEnumField, ByteField, CharEnumField, \ |
| 24 | + ConditionalField, EnumField, LEIntEnumField, LEIntField, LELongField, \ |
| 25 | + LenField, LEShortEnumField, LEShortField, MultipleTypeField, \ |
| 26 | + PacketLenField, StrLenField, XByteField, XLELongField |
25 | 27 | from scapy.interfaces import NetworkInterface, InterfaceProvider, \
|
26 | 28 | network_name, IFACES
|
| 29 | + |
| 30 | +from scapy.interfaces import IFACES, InterfaceProvider, NetworkInterface, network_name |
27 | 31 | from scapy.packet import Packet, bind_top_down
|
28 | 32 | from scapy.supersocket import SuperSocket
|
29 | 33 | from scapy.utils import PcapReader
|
|
89 | 93 | class USBpcap(Packet):
|
90 | 94 | name = "USBpcap URB"
|
91 | 95 | fields_desc = [ByteField("headerLen", None),
|
92 |
| - ByteField("res", 0), |
93 |
| - XLELongField("irpId", 0), |
94 |
| - LEIntEnumField("usbd_status", 0x0, _usbd_status_codes), |
95 |
| - LEShortEnumField("function", 0, _urb_functions), |
96 |
| - XByteField("info", 0), |
97 |
| - LEShortField("bus", 0), |
98 |
| - LEShortField("device", 0), |
99 |
| - XByteField("endpoint", 0), |
100 |
| - ByteEnumField("transfer", 0, _transfer_types), |
| 96 | + ByteField("res", 0), |
| 97 | + XLELongField("irpId", 0), |
| 98 | + LEIntEnumField("usbd_status", 0x0, _usbd_status_codes), |
| 99 | + LEShortEnumField("function", 0, _urb_functions), |
| 100 | + XByteField("info", 0), |
| 101 | + LEShortField("bus", 0), |
| 102 | + LEShortField("device", 0), |
| 103 | + XByteField("endpoint", 0), |
| 104 | + ByteEnumField("transfer", 0, _transfer_types), |
101 | 105 | LenField("dataLength", None, fmt="<I")]
|
102 | 106 |
|
103 | 107 | def post_build(self, p, pay):
|
@@ -284,3 +288,198 @@ def close(self):
|
284 | 288 | self.usbpcap_proc.kill()
|
285 | 289 |
|
286 | 290 | conf.USBsocket = USBpcapSocket
|
| 291 | + |
| 292 | + |
| 293 | +class EndpointNumber(ByteField): |
| 294 | + def any2i(self, pkt, x): |
| 295 | + if isinstance(x, tuple): |
| 296 | + return self.h2i(pkt, x) |
| 297 | + |
| 298 | + if isinstance(x, int): |
| 299 | + return x |
| 300 | + |
| 301 | + return super(EndpointNumber, self).any2i(pkt, x) |
| 302 | + |
| 303 | + def h2i(self, pkt, x): |
| 304 | + is_input, endpoint = x |
| 305 | + return endpoint | (int(is_input) << 7) |
| 306 | + |
| 307 | + def i2h(self, pkt, x): |
| 308 | + return bool(x >> 7), x & 0x7F |
| 309 | + |
| 310 | + def i2repr(self, pkt, val): |
| 311 | + is_input, endpoint = self.i2h(pkt, val) |
| 312 | + return "%s 0x%x" % (u"\u2190" if is_input else u"\u2192", endpoint) |
| 313 | + |
| 314 | + |
| 315 | +# we have to do it since when using `PacketLenField`: |
| 316 | +# `.parent` is not populated, so we cannot use `MultipleTypeField` |
| 317 | +# alternatives are always evaluated no matter what |
| 318 | +# no way to provide parameters even with severe perversions |
| 319 | +setup_common_fields = [ |
| 320 | + LEIntField("interval", None), |
| 321 | + LEIntField("start_frame", None), |
| 322 | + LEIntField("copy_of_urb_transfer_flags", None), |
| 323 | + LEIntField("iso_descriptors_count", None), |
| 324 | +] |
| 325 | + |
| 326 | + |
| 327 | +class SetupSetup(Packet): |
| 328 | + name = "Setup" |
| 329 | + |
| 330 | + class PcapUsbSetup(Packet): |
| 331 | + """USB setup header as defined in USB specification. |
| 332 | + Appears at the front of each Control S-type packet in DLT_USB captures. |
| 333 | + """ |
| 334 | + |
| 335 | + name = "Setup" |
| 336 | + fields_desc = [ |
| 337 | + XByteField("request_type", None), # 1 |
| 338 | + XByteField("request", None), # 1 |
| 339 | + LEShortField("value", None), # 2 |
| 340 | + LEShortField("index", None), # 2 |
| 341 | + LEShortField("length", None), # 2 |
| 342 | + ] |
| 343 | + |
| 344 | + fields_desc = [ |
| 345 | + PacketLenField("s", None, PcapUsbSetup, length_from=lambda pkt: 8), |
| 346 | + ] + setup_common_fields |
| 347 | + |
| 348 | + |
| 349 | +class SetupIsocr(Packet): |
| 350 | + name = "Setup" |
| 351 | + |
| 352 | + class IsoRec(Packet): |
| 353 | + """Information from the URB for Isochronous transfers. |
| 354 | +
|
| 355 | + .. seealso:: |
| 356 | + Source - https://github.com/the-tcpdump-group/libpcap/blob/ba0ef0353ed9f9f49a1edcfb49fefaf12dec54de/pcap/usb.h#L70 |
| 357 | + """ |
| 358 | + |
| 359 | + name = "IsoRec" |
| 360 | + fields_desc = [ |
| 361 | + LEIntField("error_count", None), # 4 |
| 362 | + LEIntField("descriptors_count", None), # 4 |
| 363 | + ] |
| 364 | + |
| 365 | + fields_desc = [ |
| 366 | + PacketLenField("s", None, IsoRec, length_from=lambda pkt: 8), |
| 367 | + ] + setup_common_fields |
| 368 | + |
| 369 | + |
| 370 | +class USBMon(Packet): |
| 371 | + """A native pcap header of `usbmon <https://www.kernel.org/doc/Documentation/usb/usbmon.txt>`__ part of libpcap and Linux kernel. |
| 372 | +
|
| 373 | + .. seealso:: |
| 374 | + Source - https://github.com/the-tcpdump-group/libpcap/blob/ba0ef0353ed9f9f49a1edcfb49fefaf12dec54de/pcap/usb.h#L94 |
| 375 | +
|
| 376 | +
|
| 377 | + .. seealso:: |
| 378 | + Source - https://www.kernel.org/doc/Documentation/usb/usbmon.txt |
| 379 | +
|
| 380 | +
|
| 381 | + .. seealso:: |
| 382 | + Source - https://www.kernel.org/doc/html/latest/driver-api/usb/URB.html |
| 383 | +
|
| 384 | +
|
| 385 | + .. seealso:: |
| 386 | + Source - https://wiki.wireshark.org/USB |
| 387 | + """ |
| 388 | + |
| 389 | + HEADER_SIZE = None |
| 390 | + name = "USBMonHeader" |
| 391 | + |
| 392 | + class EventType(Enum): |
| 393 | + completion = b"C" |
| 394 | + error = b"E" |
| 395 | + submit = b"S" |
| 396 | + |
| 397 | + class TransferType(IntEnum): |
| 398 | + isochronous = 0 |
| 399 | + interrupt = 1 |
| 400 | + control = 2 |
| 401 | + bulk = 3 |
| 402 | + |
| 403 | + class SetupFlag(Enum): |
| 404 | + relevant = b"\0" |
| 405 | + irrelevant = b"-" |
| 406 | + |
| 407 | + class DataFlag(Enum): |
| 408 | + urb = b"\0" |
| 409 | + incoming = b"<" |
| 410 | + outgoing = b">" |
| 411 | + error = b"E" |
| 412 | + |
| 413 | + class TimeStamp(Packet): |
| 414 | + name = "TimeStamp" |
| 415 | + |
| 416 | + @staticmethod |
| 417 | + def _getSize(*_args): |
| 418 | + return 12 |
| 419 | + |
| 420 | + fields_desc = [ |
| 421 | + LELongField("seconds", None), # 8 |
| 422 | + LEIntField("microseconds", None), # 4 |
| 423 | + ] |
| 424 | + |
| 425 | + @property |
| 426 | + def needs_setup(self): |
| 427 | + SetupFlag = self.__class__.SetupFlag |
| 428 | + return SetupFlag(self.setup_flag) == SetupFlag.relevant |
| 429 | + |
| 430 | + @property |
| 431 | + def is_isochr(self): |
| 432 | + TransferType = self.__class__.TransferType |
| 433 | + return TransferType(self.transfer_type) == TransferType.isochronous |
| 434 | + |
| 435 | + HEADER_STATIC_PART_SIZE = ( |
| 436 | + 8 + 1 + 1 + 1 + 1 + 2 + 1 + 1 |
| 437 | + + TimeStamp._getSize() + # pylint:disable=protected-access |
| 438 | + 4 + 4 + 4 |
| 439 | + ) |
| 440 | + |
| 441 | + def _getOptionalPartSize(pkt=None): |
| 442 | + return 24 |
| 443 | + |
| 444 | + def _getPaddingSize(pkt): |
| 445 | + res = pkt.__class__.HEADER_SIZE - __class__._getOptionalPartSize() * int(pkt.needs_setup) - __class__.HEADER_STATIC_PART_SIZE # pylint:disable=protected-access |
| 446 | + return res |
| 447 | + |
| 448 | + fields_desc = [ |
| 449 | + LELongField("urb_id", None), # 8 |
| 450 | + CharEnumField("event_type", b"\0", EventType), # 1 |
| 451 | + EnumField("transfer_type", 0, TransferType, "<B"), # 1 |
| 452 | + EndpointNumber("endpoint_number", 0), # 1 |
| 453 | + XByteField("device_address", None), # 1 |
| 454 | + LEShortField("bus_id", None), # 2 |
| 455 | + CharEnumField("setup_flag", b"\0", SetupFlag), # 1 |
| 456 | + CharEnumField("data_flag", b"\0", DataFlag), # 1 |
| 457 | + # just PacketField doesn't work and breaks everything after it |
| 458 | + PacketLenField("timestamp", None, TimeStamp, length_from=TimeStamp._getSize), # 12, pylint:disable=protected-access |
| 459 | + LEIntEnumField("status", 0x0, _usbd_status_codes), # 4 |
| 460 | + LEIntField("urb_size", 0), # 4 |
| 461 | + LenField("data_size", 0, fmt="<i"), # 4 |
| 462 | + ConditionalField( |
| 463 | + MultipleTypeField( |
| 464 | + [ |
| 465 | + # just PacketField doesn't work and breaks everything after it |
| 466 | + (PacketLenField("setup", None, SetupIsocr, length_from=_getOptionalPartSize), lambda pkt: pkt.is_isochr,), |
| 467 | + ], |
| 468 | + PacketLenField("setup", None, SetupSetup, length_from=_getOptionalPartSize), |
| 469 | + ), |
| 470 | + lambda pkt: pkt.needs_setup, |
| 471 | + ), # 24 |
| 472 | + StrLenField("padding", None, length_from=_getPaddingSize), |
| 473 | + ] |
| 474 | + |
| 475 | + |
| 476 | +class USBMonSimple(USBMon): |
| 477 | + HEADER_SIZE = 48 |
| 478 | + |
| 479 | + |
| 480 | +class USBMonMMapped(USBMon): |
| 481 | + HEADER_SIZE = 64 |
| 482 | + |
| 483 | + |
| 484 | +conf.l2types.register(DLT_USB_LINUX, USBMonSimple) |
| 485 | +conf.l2types.register(DLT_USB_LINUX_MMAPPED, USBMonMMapped) |
0 commit comments