TL;DR: How to pipe properly over UART the output of a remote
tcpdump to a local
I try to capture packets that flow through an embedded device to which I don't have the ability to install anything. Fortunately, there is a getty opened on the serial interface, and tcpdump installed. Sadly, no SSH, no dumpcap, no tshark.
I first tried to configure the tty and pass the data to wireshark through pipes.
stty -F /dev/ttyUSB0 raw stty -F /dev/ttyUSB0 -echo -echoe -echok cat /dev/ttyUSB0 | wireshark -k -i - # On another terminal: echo "tcpdump -U -s0 -i eth0 -w - 2>/dev/null" > /dev/ttyUSB0
Wireshark complains that the input is not valid libpcap format, certainly because the command gets echoed back and I didn't manage to get rid of that.
Using raw PySerial
So I decided to create a python script to control how the piping would work:
import serial import sys import subprocess import fcntl def main(args): with serial.Serial('/dev/ttyUSB0', 115200, timeout=0) as ser: length = ser.write(b"tcpdump -U -s0 -i eth0 -w - 2> /dev/null\n") + 1 # Discard the echoed command line while length > 0: discard = ser.read(length) length -= len(discard) # Spawn wireshark wireshark = subprocess.Popen( ["wireshark", "-k", "-i", "-"], stdin=subprocess.PIPE ) # Pipe data from serial to wireshark's input while True: data = ser.read(256) wireshark.stdin.write(data) try: wireshark.stdin.flush() except BrokenPipeError as e: break if len(data) > 0: print(data) # Send "Ctrl+C" to tcpdump ser.write(b"\x03") wireshark.wait() return 0 if __name__ == '__main__': import sys sys.exit(main(sys.argv))
Leaving aside some problems with how the script should end properly, this didn't work as well as I imagined. Wireshark is happy for some time, but quite soon the input gets corrupt and the recording stops. I think this is because the tty on the host still converts some special characters, probably the line feed or carriage return.
Getting stupid: hexdump over PySerial
So I know this is lame, but as I didn't have other ideas, this is what I came up with:
import serial import sys import subprocess import binascii def main(args): with serial.Serial('/dev/ttyUSB0', 115200, timeout=5) as ser: # Spawn tcpdump on the host and convert the raw output to stupid hex format # We need hexdump -C because that's the only format that doesn't mess up with the endianess length = ser.write(b"tcpdump -U -s256 -i eth0 -w - 2> /dev/null | hexdump -C\n") # Discard command line that is echoed discard = ser.readline() # Spawn wireshark wireshark = subprocess.Popen( ["wireshark", "-k", "-i", "-"], stdin=subprocess.PIPE ) while True: # Process each line separately data = ser.readline().decode('ascii') elements = data.split() # Remove the address and ascii convertion of hexdump and spaces hexa = "".join(elements[1:17]) # Convert back hex to binary real_data = binascii.unhexlify(hexa) # Feed to the shark wireshark.stdin.write(real_data) try: wireshark.stdin.flush() except BrokenPipeError as e: break # Stop tcpdump ser.write(b"\x03") wireshark.wait() return 0 if __name__ == '__main__': import sys sys.exit(main(sys.argv))
Alas, though it works for a bit longer than the previous version, when the frames are a bit too big, wireshark pops a problem saying that the frame is too big, with a length that is indeed ridiculous (like -1562980309832), and again the recording stops.
Please, help! :)
You can note that I tried to play with the
-s option of tcpdump, but it didn't work, even with low amounts.
I also tried piping from picocom, to no avail.
So, if you have any idea, any UART tunnelling software that would work, any remark on my (incompetent) use of stty, or any improvement to my python scripts, I would be very happy !
Wireshark is 2.2.5, tcpdump is 4.5.0 with libpcap 1.5.0.