← Back to Writeups
CUSTOMMediumHardware

Открытка от деда (Grandfather's Postcard)

XESXOR9/2/20264 min read
#hardware#custom#medium#bit_sampling#uart_decoding#vcd_parsing

Открытка от деда (Grandfather's Postcard)

Platform: duckerz | Category: Hardware | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-09 | Status: Solved Techniques: bit_sampling, uart_decoding, vcd_parsing

Summary

Files: postcard.vcd

Открытка от деда (Grandfather's Postcard) — duckerz

Description

Мой дедушка очень принципиальный и отказывается отправлять мне сообщения в WhatsApp, так что общаемся с помощью логического анализатора… Он передаёт данные через UART на скорости 9600 бод, 8 бит данных — без всяких твоих современных мессенджеров.

(Translation: My grandfather is very principled and refuses to send me messages on WhatsApp, so we communicate using a logic analyzer... He transmits data via UART at 9600 baud, 8 data bits - without any of your modern messengers.) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Files: postcard.vcd

Analysis

VCD Format

VCD (Value Change Dump) is a standard format for recording logic analyzer signals. The file contains:

  • Header with metadata (timescale, variables)
  • Timestamps with signal changes

File structure:

$timescale 1ns $end
$var wire 1 ! D0 $end
...
#0
1!
#104166
0!
...

UART Parameters

From the task description:

  • Baud rate: 9600
  • Data bits: 8
  • Parity: none (standard)
  • Stop bits: 1 (standard)
  • Bit duration: 1,000,000,000 / 9600 ≈ 104166.67 ns ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

UART Protocol

  1. Idle state — HIGH (1)
  2. Start bit — LOW (0), falling edge signals frame start
  3. 8 data bits — transmitted LSB first (least significant bit first)
  4. Stop bit — HIGH (1)
    ___     ___ ___ ___ ___ ___ ___ ___ ___ ___
       |   |D0 |D1 |D2 |D3 |D4 |D5 |D6 |D7 |   |
       |___|   |   |   |   |   |   |   |   |   |
       START                               STOP

Solution

Decoding Algorithm

  1. Parse VCD file, extract signal transitions (time, value)
  2. Find falling edges (1→0) — these are start bits
  3. For each start bit, sample 8 data bits in the middle of each bit period
  4. Sampling time for bit N: start_time + bit_duration * (1.5 + N)
  5. Verify stop bit = HIGH
  6. Assemble bytes into a string ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Python Solution

python
#!/usr/bin/env python3
"""
UART decoder for VCD files
duckerz CTF - Grandfather's Postcard
"""

def parse_vcd(filename):
    """Parse VCD file and extract signal transitions."""
    transitions = []
    current_time = 0
    
    with open(filename, 'r') as f:
        for line in f:
            line = line.strip()
            if line.startswith('#'):
                # Timestamp line
                current_time = int(line[1:])
            elif line == '0!':
                transitions.append((current_time, 0))
            elif line == '1!':
                transitions.append((current_time, 1))
    
    return transitions
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍


def build_signal(transitions):
    """Build a function that returns signal value at any time."""
    def get_value(t):
        value = 1  # UART idle is HIGH
        for time, v in transitions:
            if time > t:
                break
            value = v
        return value
    return get_value


def decode_uart(transitions, baud_rate=9600):
    """Decode UART signal from transitions."""
    bit_duration = 1_000_000_000 / baud_rate  # ns per bit
    get_signal = build_signal(transitions)
    
    # Find falling edges (start bits)
    falling_edges = []
    prev_value = 1
    for time, value in transitions:
        if time == 0:
            prev_value = value
            continue
        if prev_value == 1 and value == 0:
            falling_edges.append(time)
        prev_value = value
    
    decoded = []
    last_frame_end = 0
    
    for start_time in falling_edges:
        # Skip if this edge is within a previous frame
        if start_time < last_frame_end:
            continue
        
        # Sample 8 data bits in the middle of each bit period
        byte_value = 0
        for bit_idx in range(8):
            # Sample at 1.5 + bit_idx bit periods from start
            sample_time = start_time + bit_duration * (1.5 + bit_idx)
            bit_value = get_signal(sample_time)
            byte_value |= (bit_value << bit_idx)  # LSB first
        
        # Verify stop bit
        stop_time = start_time + bit_duration * 9.5
        if get_signal(stop_time) == 1:
            decoded.append(chr(byte_value))
            last_frame_end = start_time + bit_duration * 10
    
    return ''.join(decoded)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍


if __name__ == '__main__':
    transitions = parse_vcd('postcard.vcd')
    message = decode_uart(transitions)
    print(f"Decoded message: {message}")

Alternative Tools

  1. GTKWave — open VCD file, visually analyze the signal
  2. PulseView/sigrok — use built-in UART decoder:
    • Open VCD file
    • Add UART protocol
    • Configure: 9600 baud, 8N1
    • Get decoded data

Important Notes

  1. VCD stores only transitions — not continuous samples
  2. First transition (time=0) — initialization, not data
  3. UART transmits LSB first — least significant bit first
  4. Sampling in the middle of bit — provides jitter tolerance ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍