← Back to Writeups
CUSTOMMediumReversing

Patona

XESXOR9/2/20262 min read
#reversing#custom#medium#am_demodulation#apt_decoding#envelope_extraction

Patona

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: am_demodulation, apt_decoding, envelope_extraction

Summary

Walkthrough of the CUSTOM Reversing Patona. Task: Decode a flag from flag.raw file generated by an APT encoder binary. Solution: Reverse-engineered the NOAA APT signal format, extracted envelope from AM-modulated signal using 3 samples per pixe

Description

Analysis of the patona binary revealed that it implements an APT (Automatic Picture Transmission) encoder used by NOAA satellites. The binary takes a PGM image with a width of 909 pixels and encodes it into an AM-modulated signal with a 2400 Hz carrier frequency.

Analysis

Each line of the output file flag.raw contains 2080 "pixels", each encoded with 3 samples, resulting in 6240 samples per line. The total file size of 1,210,560 bytes corresponds to 194 lines (1210560 / 6240). ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

A Python script was written to solve this:

  1. Read flag.raw as 8-bit samples.
  2. Center the signal around 127.5.
  3. Calculate the envelope by averaging absolute deviations in groups of 3 samples.
  4. Extract the Image A region (samples 86-995 in each line of 2080).
python
import numpy as np
from PIL import Image

# Constants from analysis
SAMPLES_PER_PIXEL = 3
PIXELS_PER_LINE = 2080
SAMPLES_PER_LINE = PIXELS_PER_LINE * SAMPLES_PER_PIXEL # 6240
IMAGE_A_START = 86
IMAGE_A_END = 995

def solve():
    # Read the raw signal
    with open('flag.raw', 'rb') as f:
        data = f.read()
    
    # Convert to numpy array of float32 for processing
    samples = np.frombuffer(data, dtype=np.uint8).astype(np.float32)
    
    # Center the signal (AM modulation around 127.5)
    samples -= 127.5
    
    # Reshape to lines and pixels
    num_lines = len(samples) // SAMPLES_PER_LINE
    samples = samples[:num_lines * SAMPLES_PER_LINE]
    lines = samples.reshape((num_lines, PIXELS_PER_LINE, SAMPLES_PER_PIXEL))
    
    # Calculate envelope by averaging absolute deviations in groups of 3 samples
    # This effectively demodulates the AM signal
    envelope = np.mean(np.abs(lines), axis=2)
    
    # Normalize for image saving
    envelope = (envelope - envelope.min()) / (envelope.max() - envelope.min()) * 255
    envelope = envelope.astype(np.uint8)
    
    # Extract Image A region
    image_a = envelope[:, IMAGE_A_START:IMAGE_A_END]
    
    # Save the result
    img = Image.fromarray(image_a)
    img.save('correct_img_a.png')
    print(f"Decoded image saved to correct_img_a.png. Shape: {image_a.shape}")
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

if __name__ == "__main__":
    solve()

The flag is clearly visible in the decoded image. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Key Takeaways / Lessons

  • Mastering am_demodulation, apt_decoding, envelope_extraction was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2025-12-27 • CUSTOM • Patona • Difficulty: Medium