Serial Port Sniffing

Tools and techniques for capturing and analyzing serial port communication (UART, RS-232, RS-485).

Overview

Serial port sniffing captures data transmitted over serial connections for debugging, reverse engineering, or monitoring.

Common Use Cases:

  • Debugging embedded systems
  • Reverse engineering protocols
  • Monitoring industrial equipment
  • IoT device analysis
  • Hardware hacking

Serial Communication Basics

UART Parameters

1Baud Rate: 9600, 19200, 38400, 57600, 115200, etc.
2Data Bits: 5, 6, 7, 8
3Parity:    None, Even, Odd, Mark, Space
4Stop Bits: 1, 1.5, 2
5Flow Control: None, Hardware (RTS/CTS), Software (XON/XOFF)

Common Configurations

19600 8N1:  9600 baud, 8 data bits, No parity, 1 stop bit
2115200 8N1: Most common for embedded systems
319200 7E1: 19200 baud, 7 data bits, Even parity, 1 stop bit

Hardware Sniffing Methods

1. Y-Cable (Passive Sniffing)

Wiring:

1Device A          Sniffer          Device B
2--------          -------          --------
3TX (Pin 3) -----> RX1 (Pin 2) ---> RX (Pin 2)
4RX (Pin 2) <----- RX2 (Pin 3) <--- TX (Pin 3)
5GND (Pin 5) <---> GND (Pin 5) <--> GND (Pin 5)

Advantages:

  • Non-intrusive
  • No timing impact
  • Captures both directions

Disadvantages:

  • Requires two UART ports on sniffer
  • Cannot modify traffic

2. Logic Analyzer

1Channels:
2- CH0: Device A TX
3- CH1: Device B TX (Device A RX)
4- GND: Common ground
5
6Capture both TX lines simultaneously

Popular Logic Analyzers:

  • Saleae Logic (8-channel, up to 100 MHz)
  • DSLogic (USB logic analyzer)
  • Sigrok-compatible devices

3. USB-to-Serial Adapter

1# Connect USB-to-serial adapter to target TX line
2# Adapter RX -> Target TX
3# Adapter GND -> Target GND
4
5# Read on Linux
6cat /dev/ttyUSB0
7
8# Read on Windows
9# Use PuTTY, RealTerm, or similar

Software Tools

1. screen (Linux/macOS)

 1# Basic connection
 2screen /dev/ttyUSB0 115200
 3
 4# With specific settings (8N1)
 5screen /dev/ttyUSB0 115200,cs8,-parenb,-cstopb
 6
 7# Exit: Ctrl+A, then K (kill)
 8
 9# Log to file
10screen -L -Logfile serial.log /dev/ttyUSB0 115200

2. minicom (Linux)

 1# Install
 2sudo apt-get install minicom
 3
 4# Configure
 5sudo minicom -s
 6
 7# Connect
 8minicom -D /dev/ttyUSB0 -b 115200
 9
10# Enable logging: Ctrl+A, L
11# Exit: Ctrl+A, X

Configuration file (~/.minirc.dfl):

1pu port             /dev/ttyUSB0
2pu baudrate         115200
3pu bits             8
4pu parity           N
5pu stopbits         1
6pu rtscts           No
7pu xonxoff          No

3. picocom (Linux)

 1# Install
 2sudo apt-get install picocom
 3
 4# Connect
 5picocom -b 115200 /dev/ttyUSB0
 6
 7# With logging
 8picocom -b 115200 --logfile serial.log /dev/ttyUSB0
 9
10# Exit: Ctrl+A, Ctrl+X

4. PuTTY (Windows/Linux)

1Connection Type: Serial
2Serial line: COM3 (Windows) or /dev/ttyUSB0 (Linux)
3Speed: 115200
4
5Session -> Logging:
6- Session logging: All session output
7- Log file name: C:\serial.log

5. RealTerm (Windows)

Features:

  • Hex display
  • Timestamp logging
  • Multiple display formats
  • Capture to file

Configuration:

1Port: COM3
2Baud: 115200
3Data Bits: 8
4Parity: None
5Stop Bits: 1
6Hardware Flow Control: None

6. interceptty (Linux)

Intercept serial communication between two programs:

 1# Install
 2git clone https://github.com/geoffmeyers/interceptty
 3cd interceptty && make
 4
 5# Usage: Create virtual serial ports
 6./interceptty -s 'ispeed 115200 ospeed 115200' \
 7              /dev/ttyUSB0 /dev/pts/2
 8
 9# Now connect your application to /dev/pts/2
10# All traffic is logged to stdout

7. Python pySerial

 1import serial
 2import time
 3from datetime import datetime
 4
 5def sniff_serial(port, baudrate=115200, logfile='serial.log'):
 6    """Sniff serial port and log to file"""
 7    ser = serial.Serial(
 8        port=port,
 9        baudrate=baudrate,
10        bytesize=serial.EIGHTBITS,
11        parity=serial.PARITY_NONE,
12        stopbits=serial.STOPBITS_ONE,
13        timeout=1
14    )
15    
16    print(f"Sniffing {port} at {baudrate} baud...")
17    print(f"Logging to {logfile}")
18    
19    with open(logfile, 'ab') as f:
20        try:
21            while True:
22                if ser.in_waiting > 0:
23                    data = ser.read(ser.in_waiting)
24                    timestamp = datetime.now().isoformat()
25                    
26                    # Log with timestamp
27                    log_entry = f"[{timestamp}] ".encode() + data + b'\n'
28                    f.write(log_entry)
29                    f.flush()
30                    
31                    # Print to console
32                    print(f"[{timestamp}] {data.hex()} | {data}")
33                
34                time.sleep(0.01)
35        except KeyboardInterrupt:
36            print("\nStopping...")
37        finally:
38            ser.close()
39
40# Usage
41sniff_serial('/dev/ttyUSB0', 115200)

8. Advanced Python Sniffer (Dual Port)

 1import serial
 2import threading
 3import time
 4from datetime import datetime
 5
 6class DualSerialSniffer:
 7    def __init__(self, port1, port2, baudrate=115200):
 8        self.port1 = serial.Serial(port1, baudrate, timeout=0.1)
 9        self.port2 = serial.Serial(port2, baudrate, timeout=0.1)
10        self.running = False
11    
12    def sniff_port(self, ser, name, logfile):
13        """Sniff single port"""
14        with open(logfile, 'ab') as f:
15            while self.running:
16                if ser.in_waiting > 0:
17                    data = ser.read(ser.in_waiting)
18                    timestamp = datetime.now().isoformat()
19                    
20                    # Log
21                    log_entry = f"[{timestamp}] {name}: ".encode() + data + b'\n'
22                    f.write(log_entry)
23                    f.flush()
24                    
25                    # Console output
26                    print(f"[{timestamp}] {name}: {data.hex()}")
27                    print(f"  ASCII: {data.decode('ascii', errors='replace')}")
28                
29                time.sleep(0.01)
30    
31    def start(self):
32        """Start sniffing both ports"""
33        self.running = True
34        
35        # Create threads for each port
36        t1 = threading.Thread(
37            target=self.sniff_port,
38            args=(self.port1, "Port1", "port1.log")
39        )
40        t2 = threading.Thread(
41            target=self.sniff_port,
42            args=(self.port2, "Port2", "port2.log")
43        )
44        
45        t1.start()
46        t2.start()
47        
48        try:
49            while True:
50                time.sleep(1)
51        except KeyboardInterrupt:
52            print("\nStopping...")
53            self.running = False
54            t1.join()
55            t2.join()
56        finally:
57            self.port1.close()
58            self.port2.close()
59
60# Usage
61sniffer = DualSerialSniffer('/dev/ttyUSB0', '/dev/ttyUSB1', 115200)
62sniffer.start()

Protocol Analysis

Hex Dump Analysis

 1def analyze_hex_dump(data):
 2    """Analyze captured serial data"""
 3    print("Offset   Hex                                          ASCII")
 4    print("-" * 70)
 5    
 6    for i in range(0, len(data), 16):
 7        chunk = data[i:i+16]
 8        
 9        # Hex representation
10        hex_str = ' '.join(f'{b:02x}' for b in chunk)
11        hex_str = hex_str.ljust(48)
12        
13        # ASCII representation
14        ascii_str = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
15        
16        print(f"{i:08x}  {hex_str}  {ascii_str}")
17
18# Example
19data = b'\x01\x02\x03Hello World\x0d\x0a\xff\xfe'
20analyze_hex_dump(data)

Pattern Detection

 1import re
 2
 3def detect_patterns(data):
 4    """Detect common patterns in serial data"""
 5    patterns = {
 6        'AT Commands': rb'AT[+\w]*',
 7        'Hex Addresses': rb'0x[0-9A-Fa-f]+',
 8        'JSON': rb'\{[^}]+\}',
 9        'Checksums': rb'[\*#][0-9A-Fa-f]{2}',
10        'Delimiters': rb'[\r\n]+',
11    }
12    
13    results = {}
14    for name, pattern in patterns.items():
15        matches = re.findall(pattern, data)
16        if matches:
17            results[name] = matches
18    
19    return results
20
21# Example
22data = b'AT+CGMI\r\nOK\r\n{"temp":25.5}\r\n*A5\r\n'
23patterns = detect_patterns(data)
24for name, matches in patterns.items():
25    print(f"{name}: {matches}")

Baud Rate Detection

 1def detect_baud_rate(data, common_rates=None):
 2    """
 3    Attempt to detect baud rate by looking for valid ASCII
 4    
 5    Note: This is heuristic and may not always work
 6    """
 7    if common_rates is None:
 8        common_rates = [9600, 19200, 38400, 57600, 115200]
 9    
10    scores = {}
11    for rate in common_rates:
12        # Count valid ASCII characters
13        valid_chars = sum(1 for b in data if 32 <= b < 127 or b in [9, 10, 13])
14        score = valid_chars / len(data) if data else 0
15        scores[rate] = score
16    
17    # Return rate with highest score
18    best_rate = max(scores, key=scores.get)
19    return best_rate, scores[best_rate]
20
21# Example
22data = b'Hello World\r\n'
23rate, confidence = detect_baud_rate(data)
24print(f"Detected baud rate: {rate} (confidence: {confidence:.2%})")

Advanced Techniques

1. Logic Analyzer with Sigrok

 1# Install
 2sudo apt-get install sigrok-cli pulseview
 3
 4# List devices
 5sigrok-cli --scan
 6
 7# Capture UART
 8sigrok-cli -d fx2lafw --config samplerate=1M \
 9           --channels 0=TX,1=RX \
10           --protocol-decoders uart:baudrate=115200 \
11           --samples 1M \
12           -o capture.sr
13
14# Analyze with PulseView
15pulseview capture.sr

2. Wireshark for Serial

Using socat to create virtual serial ports:

1# Create virtual serial port pair
2socat -d -d pty,raw,echo=0 pty,raw,echo=0
3
4# Connect one end to target, other to Wireshark
5# Use extcap interface or pipe data

3. Bus Pirate

 1# Enter UART mode
 2m  # Mode menu
 33  # UART
 4
 5# Configure
 6# Baud: 115200
 7# Data bits: 8
 8# Parity: None
 9# Stop bits: 1
10
11# Macro to sniff
12(1)  # Transparent UART bridge
13
14# Exit: #

4. Arduino as Serial Sniffer

 1// Arduino sketch for dual-port sniffing
 2void setup() {
 3  Serial.begin(115200);   // USB connection to PC
 4  Serial1.begin(9600);    // Target device
 5  
 6  Serial.println("Serial sniffer ready");
 7}
 8
 9void loop() {
10  // Forward from target to PC
11  if (Serial1.available()) {
12    char c = Serial1.read();
13    Serial.print("RX: 0x");
14    Serial.print(c, HEX);
15    Serial.print(" '");
16    Serial.print(c);
17    Serial.println("'");
18  }
19  
20  // Optional: Forward from PC to target
21  if (Serial.available()) {
22    char c = Serial.read();
23    Serial1.write(c);
24    Serial.print("TX: 0x");
25    Serial.print(c, HEX);
26    Serial.print(" '");
27    Serial.print(c);
28    Serial.println("'");
29  }
30}

Common Protocols

AT Commands (Modems, GSM)

1AT          -> OK
2AT+CGMI     -> Manufacturer
3AT+CGMM     -> Model
4AT+CGSN     -> Serial number
5ATD1234567  -> Dial number

NMEA (GPS)

 1$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
 2  |     |      |         |          | |  |   |      |     |
 3  |     |      |         |          | |  |   |      |     Checksum
 4  |     |      |         |          | |  |   |      Geoidal separation
 5  |     |      |         |          | |  |   Altitude
 6  |     |      |         |          | |  HDOP
 7  |     |      |         |          | Satellites
 8  |     |      |         |          Fix quality
 9  |     |      |         Longitude
10  |     |      Latitude
11  |     Time (UTC)
12  Message type

Modbus RTU

1Frame: [Device ID][Function][Data][CRC]
2Example: 01 03 00 00 00 0A C5 CD
3  01: Device address
4  03: Read holding registers
5  00 00: Starting address
6  00 0A: Quantity (10 registers)
7  C5 CD: CRC-16

Troubleshooting

Permission Issues (Linux)

1# Add user to dialout group
2sudo usermod -a -G dialout $USER
3
4# Or change permissions (temporary)
5sudo chmod 666 /dev/ttyUSB0
6
7# List serial devices
8ls -l /dev/tty*

Finding Serial Devices

1# Linux
2dmesg | grep tty
3ls /dev/ttyUSB* /dev/ttyACM*
4
5# macOS
6ls /dev/cu.*
7
8# Windows (PowerShell)
9Get-WmiObject Win32_SerialPort | Select-Object Name,DeviceID

Baud Rate Mismatch

Symptoms:

  • Garbage characters
  • Random symbols
  • No readable data

Solution:

1# Try common baud rates
2for baud in 9600 19200 38400 57600 115200; do
3    echo "Trying $baud..."
4    timeout 2 cat /dev/ttyUSB0 | head -n 5
5done

Buffer Overruns

 1# Increase buffer size
 2ser = serial.Serial(
 3    port='/dev/ttyUSB0',
 4    baudrate=115200,
 5    timeout=0.1
 6)
 7
 8# Read frequently to prevent buffer overflow
 9while True:
10    if ser.in_waiting > 0:
11        data = ser.read(ser.in_waiting)
12        process_data(data)
13    time.sleep(0.001)  # Small delay

Security Considerations

 1⚠️ WARNING:
 2- Serial sniffing may violate laws/regulations
 3- Only sniff devices you own or have permission to monitor
 4- Industrial systems: Consult with system owner
 5- Medical devices: May be regulated (FDA, etc.)
 6- Automotive: May void warranty
 7
 8✅ LEGAL USE:
 9- Your own embedded projects
10- Authorized security testing
11- Educational purposes with owned equipment
12- Debugging your own products

Best Practices

 1✅ DO:
 2- Document baud rate and settings
 3- Use timestamps in logs
 4- Capture both TX and RX
 5- Save raw data before processing
 6- Note hardware connections
 7- Use proper ESD protection
 8
 9❌ DON'T:
10- Connect 5V to 3.3V devices directly
11- Forget common ground connection
12- Ignore voltage levels
13- Mix up TX/RX connections
14- Sniff high-voltage RS-232 without protection

Further Reading

Related Snippets