Aller au contenu principal

Trop d'IQ

Transformée de Fourier discrète, ça se reverse...

Description du challenge

  • Nom du CTF : 404CTF 2025
  • Catégorie : Hardware
  • Difficulté : Intro
  • Date : Mai 2025

On nous donne le challenge suivant :

Challenge

On nous donne le fichier chall.iq à analyser : fichier

Analyse préliminaire

On nous informe que le challenge consiste à démoduler un signal passé par une transformée de Fourier discrète (DFT). On peut donc appliquer une transformée de Fourier inverse (IDFT) pour récupérer le signal original.

On sait aussi que la fréquence d'échantillonage est de 44,1 kHz et que le fichier est au format IQ Complex128.

Démodulation avec Python

On peut utiliser les bibliothèques numpy et scipy pour effectuer la transformée de Fourier inverse. On suppose aussi que le signal est modulé FM :

import numpy as np
from scipy import signal
from scipy.io import wavfile
from scipy.fft import ifft

def demodulate_fm_iq(iq_file_path, output_wav_path="output_audio.wav", sample_rate=44100):
"""
Démodule un fichier IQ contenant un signal FM transformé par DFT.

Args:
iq_file_path (str): Chemin vers le fichier IQ
output_wav_path (str): Chemin où sauvegarder le fichier WAV démodulé
sample_rate (int): Taux d'échantillonnage du signal (Hz)
"""

# Lecture du fichier IQ (format complex128)
try:
iq_data = np.fromfile(iq_file_path, dtype=np.complex128)
print(f"Données IQ chargées: {len(iq_data)} échantillons")
except Exception as e:
print(f"Erreur lors de la lecture du fichier IQ: {e}")
return

# Conversion des données du domaine fréquentiel au domaine temporel
print("Application de la transformée de Fourier inverse...")

# Application de la transformée de Fourier inverse
time_domain = ifft(iq_data)

# Pour un signal audio standard, on peut utiliser directement la partie réelle
# ou prendre l'amplitude du signal complexe
audio = np.real(time_domain)

# Normalisation de l'audio
audio = audio / np.max(np.abs(audio))

# Filtre passe-bas pour éliminer le bruit haute fréquence
print("Application d'un filtre passe-bas...")
cutoff = 15000 / (sample_rate / 2)
b, a = signal.butter(8, cutoff, 'lowpass')
audio_filtered = signal.filtfilt(b, a, audio)

# Normalisation pour le format WAV
audio_normalized = np.int16(audio_filtered * 32767)

# Sauvegarde en WAV
print(f"Enregistrement du fichier audio: {output_wav_path}")
wavfile.write(output_wav_path, sample_rate, audio_normalized)

# Également essayer la partie imaginaire (au cas où)
audio_imag = np.imag(time_domain)
audio_imag = audio_imag / np.max(np.abs(audio_imag))
audio_imag_filtered = signal.filtfilt(b, a, audio_imag)
audio_imag_normalized = np.int16(audio_imag_filtered * 32767)
wavfile.write("output_imag.wav", sample_rate, audio_imag_normalized)

print("Démodulation terminée!")

return audio_filtered

if __name__ == "__main__":
audio = demodulate_fm_iq("chall2.iq", "output.wav")
FLAG

404CTF{45D0587}