Radio Signal
Radio Signal
Platform: hackerlab | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2025-12-23 | Status: Solved Techniques: frequency_detection, image_processing, zero_crossing
Summary
Task: WAV file with unknown radio station audio. Solution: Identified SSTV Robot 36 mode signal, implemented custom zero-crossing decoder to extract hidden image containing the flag.
Radio Signal — hackerlab
Description
A WAV file with an audio recording of an "unknown radio station" was provided. The goal is to extract hidden information.
Analysis
- Spectral analysis: When viewing the spectrogram of the audio file (e.g., in Audacity or using Python), a characteristic signal in the 1200-2300 Hz range is observed. The signal structure (sync pulses and frequency variations) indicates the use of SSTV (Slow Scan Television).
- Mode identification: The recording duration and data packet structure correspond to Robot 36 mode (color image 320x240, transmitted in approximately 36-37 seconds).
- Tool issues: Standard decoders (such as
sstvorpysstv) may produce poor results or not work at all due to specific noise or timing deviations in the signal.
Solution
For quality decoding, a custom script was implemented using the zero-crossing method to determine the instantaneous signal frequency. This method is resistant to certain types of distortion and allows accurate recovery of the luminance component.
Decoding algorithm:
- Read WAV file and normalize the signal.
- Determine instantaneous frequency through intervals between zero-line crossings.
- Map frequencies (1500-2300 Hz) to pixel brightness values (0-255).
- Synchronize by horizontal pulses (usually 1200 Hz).
- Assemble the 320x240 image.
Implementation example (conceptual):
python read-only • copy allowedimport numpy as np from scipy.io import wavfile from PIL import Image def decode_sstv(filename): fs, data = wavfile.read(filename) if len(data.shape) > 1: data = data[:, 0] # Zero crossing detection for frequency determination zero_crossings = np.where(np.diff(np.sign(data)))[0] periods = np.diff(zero_crossings) freqs = fs / (2 * periods) # Robot 36 parameters width, height = 320, 240 img = Image.new('L', (width, height)) # Simplified frequency to pixel mapping (Y-channel) # 1500 Hz = Black, 2300 Hz = White pixels = np.clip((freqs - 1500) / 800 * 255, 0, 255).astype(np.uint8) # Line assembly logic with sync pulse handling... # (omitted for brevity) return img # After decoding, orientation correction was needed # img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.FLIP_TOP_BOTTOM)
Image processing:
After initial decoding of the luminance component (Y-channel), a noisy image was obtained. Main improvement steps:
- Orientation correction: The image was mirrored and flipped.
- Contrast enhancement: Using adaptive threshold filtering to highlight text.
- Flag reading: Text in leet-speak style was visible on the image.