Wireshark: captura remota sobre o UART

2

TL; DR: Como canalizar corretamente através da UART a saída de umtcpdump remoto para um local wireshark ?

Eu tento capturar pacotes que fluem através de um dispositivo incorporado para o qual não tenho a capacidade de instalar nada. Felizmente, existe um getty aberto na interface serial e o tcpdump instalado. Infelizmente, sem SSH, sem dumpcap, sem tshark.

Tubo direto

Eu tentei primeiro configurar o tty e passar os dados para o wireshark através de 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

O Wireshark reclama que a entrada não é válida no formato da libpcap, certamente porque o comando é ecoado de volta e eu não consegui me livrar disso.

Usando PySerial bruto

Então, decidi criar um script python para controlar como a tubulação funcionaria:

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))

Deixando de lado alguns problemas com a forma como o script deve terminar corretamente, isso não funcionou tão bem quanto eu imaginava. O Wireshark está feliz por algum tempo, mas logo a entrada fica corrompida e a gravação pára. Acho que isso acontece porque o tty no host ainda converte alguns caracteres especiais, provavelmente o avanço de linha ou o retorno de carro.

Ficando estúpido: hexdump em PySerial

Então eu sei que isso é ruim, mas como eu não tinha outras ideias, foi isso que eu criei:

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))
Infelizmente, apesar de funcionar um pouco mais do que a versão anterior, quando os quadros são um pouco grandes demais, o wireshark solta um problema dizendo que o quadro é muito grande, com um comprimento que é realmente ridículo (como -1562980309832) e novamente a gravação pára.

Por favor, ajude! :)

Você pode notar que tentei jogar com a opção -s do tcpdump, mas não funcionou, mesmo com valores baixos.

Eu também tentei piping de picocom, sem sucesso.

Então, se você tem alguma idéia, qualquer software de tunelamento UART que funcione, qualquer observação sobre meu (incompetente) uso de stty, ou qualquer melhoria em meus scripts python, eu ficaria muito feliz!

O Wireshark é 2.2.5, o tcpdump é 4.5.0 com a libpcap 1.5.0.

    
por Cilyan 17.04.2017 / 17:24

1 resposta

2

Finalmente, eu realmente funcionei. Esta não é a configuração perfeita, mas pelo menos funciona, então talvez possa ajudar alguém no futuro.

Eu usei um script Python em cima de PySerial para iniciar o tcpdump sobre o UART e usar o hexdump para que os dados binários possam atravessar o link sem ser modificado pelas regras de transcrição tty. Em seguida, o script Python converte os dados de volta e canaliza para o wireshark. O script abaixo é o resultado, comparado com o da questão, eu adicionei a opção -v ao hexdump para que ele não tente comprimir as linhas que são as mesmas.

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 -Cv\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))
    
por 18.04.2017 / 20:46