Jason Steele
Jason Steele

Reputation: 1606

Wireshark unable to open btsnoop file

I followed the instructions in the answer to Bluetooth HCI snoop log not generated to create a btsnoop file using btsnooz.py from my Android bugreport file. When I opened the resulting btsnoop.log file in Wireshark I got the error The file "btsnoop.log" isn't a capture file in a format Wireshark understands.

The adb bugreport was done against an S7 Edge running Android 8.0.0.

A copy of the btsnoop.log file can be found here https://drive.google.com/file/d/1Y3544DrhPbI9YxktL6rSWkpAe-YeoPn4/view?usp=sharing

How I can analyze this file?

Upvotes: 3

Views: 6429

Answers (2)

Edward Millen
Edward Millen

Reputation: 224

I've just run into the same issue, and have managed to modify btsnooz.py to work correctly with Python 3 on Windows (and I expect Linux etc too, as I didn't do anything specifically with line endings, just encodings etc to match Python 2 behaviour). I've also made it output to a file called "btsnoop.log" in the current directory (overwriting any that might already exist) rather than trying to write to stdout.

I have included my modified code below - just save it as "btsnooz.py" and run python btsnooz.py <name of bugreport file>.txt, using Python 3

#!/usr/bin/env python
"""
This script extracts btsnooz content from bugreports and generates
a valid btsnoop log file which can be viewed using standard tools
like Wireshark.
btsnooz is a custom format designed to be included in bugreports.
It can be described as:
base64 {
  file_header
  deflate {
    repeated {
      record_header
      record_data
    }
  }
}
where the file_header and record_header are modified versions of
the btsnoop headers.
"""
import base64
import fileinput
import struct
import sys
import zlib
# Enumeration of the values the 'type' field can take in a btsnooz
# header. These values come from the Bluetooth stack's internal
# representation of packet types.
TYPE_IN_EVT = 0x10
TYPE_IN_ACL = 0x11
TYPE_IN_SCO = 0x12
TYPE_IN_ISO = 0x17
TYPE_OUT_CMD = 0x20
TYPE_OUT_ACL = 0x21
TYPE_OUT_SCO = 0x22
TYPE_OUT_ISO = 0x2d
def type_to_direction(type):
    """
  Returns the inbound/outbound direction of a packet given its type.
  0 = sent packet
  1 = received packet
  """
    if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO, TYPE_IN_ISO]:
        return 1
    return 0
def type_to_hci(type):
    """
  Returns the HCI type of a packet given its btsnooz type.
  """
    if type == TYPE_OUT_CMD:
        return '\x01'
    if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
        return '\x02'
    if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
        return '\x03'
    if type == TYPE_IN_EVT:
        return '\x04'
    if type == TYPE_IN_ISO or type == TYPE_OUT_ISO:
        return '\x05'
    raise RuntimeError("type_to_hci: unknown type (0x{:02x})".format(type))
def decode_snooz(snooz):
    """
  Decodes all known versions of a btsnooz file into a btsnoop file.
  """
    version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
    if version != 1 and version != 2:
        sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
        exit(1)
    # Oddly, the file header (9 bytes) is not compressed, but the rest is.
    decompressed = zlib.decompress(snooz[9:])
    fp.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea'.encode("latin-1"))
    if version == 1:
        decode_snooz_v1(decompressed, last_timestamp_ms)
    elif version == 2:
        decode_snooz_v2(decompressed, last_timestamp_ms)
def decode_snooz_v1(decompressed, last_timestamp_ms):
    """
  Decodes btsnooz v1 files into a btsnoop file.
  """
    # An unfortunate consequence of the file format design: we have to do a
    # pass of the entire file to determine the timestamp of the first packet.
    first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
    offset = 0
    while offset < len(decompressed):
        length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
        offset += 7 + length - 1
        first_timestamp_ms -= delta_time_ms
    # Second pass does the actual writing out to stdout.
    offset = 0
    while offset < len(decompressed):
        length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
        first_timestamp_ms += delta_time_ms
        offset += 7
        fp.write(struct.pack('>II', length, length))
        fp.write(struct.pack('>II', type_to_direction(type), 0))
        fp.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
        fp.write(type_to_hci(type).encode("latin-1"))
        fp.write(decompressed[offset:offset + length - 1])
        offset += length - 1
def decode_snooz_v2(decompressed, last_timestamp_ms):
    """
  Decodes btsnooz v2 files into a btsnoop file.
  """
    # An unfortunate consequence of the file format design: we have to do a
    # pass of the entire file to determine the timestamp of the first packet.
    first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
    offset = 0
    while offset < len(decompressed):
        length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
        offset += 9 + length - 1
        first_timestamp_ms -= delta_time_ms
    # Second pass does the actual writing out to stdout.
    offset = 0
    while offset < len(decompressed):
        length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
        first_timestamp_ms += delta_time_ms
        offset += 9
        fp.write(struct.pack('>II', packet_length, length))
        fp.write(struct.pack('>II', type_to_direction(snooz_type), 0))
        fp.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
        fp.write(type_to_hci(snooz_type).encode("latin-1"))
        fp.write(decompressed[offset:offset + length - 1])
        offset += length - 1
fp = None
def main():
    if len(sys.argv) > 2:
        sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
        exit(1)
    iterator = fileinput.input(openhook=fileinput.hook_encoded("latin-1"))
    found = False
    base64_string = ""
    for line in iterator:
        if found:
            if line.find('--- END:BTSNOOP_LOG_SUMMARY') != -1:
                global fp
                fp = open("btsnoop.log", "wb")
                decode_snooz(base64.standard_b64decode(base64_string))
                fp.close
                sys.exit(0)
            base64_string += line.strip()
        if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
            found = True
    if not found:
        sys.stderr.write('No btsnooz section found in bugreport.\n')
        sys.exit(1)
if __name__ == '__main__':
    main()

Upvotes: 0

dandreye
dandreye

Reputation: 41

While it doesn't exactly answer the question perhaps it'll help find the answer (I'm not allowed to comment yet). Your trace starts with FFFE6200740073006E006F006F007000, where FFFE looks like UTF-16LE BOM and then goes "btsnoop" interleaved with zeroes. I got exactly that when running that script btsnooz.py "from under" python-2.7 that had been installed on my W10x64 laptop in the past - like this: C:\python27-x64\python.exe btsnooz.py bugreport-2021-01-09-22-52-09.txt >bugreport.pcap

I then tried running it directly like this instead, hoping that W10 has some built-in python of its own: .\btsnooz.py bugreport-2021-01-09-22-52-09.txt >bugreport.pcap

and got a twice smaller file that starts with "btsnoop" like valid ones do. Although I'm having a different issue with it (Wireshark can only decode the first few packets) it looks like a step forward as method #1 of running btsnooz.py above appears to corrupt the output file somehow.

Here's what btsnooz.py should write into the beginning of the file as follows from its code, so anything else there means incorrect execution of the script: sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')

HTH...

Update: Apparently that python script btsnooz.py is implied for use on Linux: just verified it on Ubuntu with python v2.7 and it produces correct traces parsed by Wireshark w/o any such errors. When used on Windows it seems to write 0D0A instead of every 0A, causing above described Wireshark parsing errors. Replacing those 0D0A "back" with 0A in a hex editor fixed those errors in my case.

Upvotes: 4

Related Questions