TL;DR: How to pipe properly over UART the output of a remote tcpdump
to a local wireshark
?
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.
Direct pipe
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.
ppp
software on the remote device, so you could setup a network connection and then transfer over that?netcat
ornc
on the embedded device?busybox
usually providesnc
.