Solution de U03 pour Mommy Morse

hardware radio

5 janvier 2025

En analysant mommy-morse.py on voit qu’il faut envoyer la chaîne CAN I GET THE FLAG pour récuperer le flag. Le fonctionnement est similaire à l’exercice daddy-shark sauf que le signal est modulé.

Les caractéristiques du signal à envoyer sont les suivantes:

Nous analysons le script client.py, il lit un fichier signal.iq et l’envoie au serveur. Nous important le fichier signal.iq dans audacity par le menu file->Import->Raw Data) avec les paramètres Signed 32-bits PCM, Default endianness, 2 channels (stereo), 24000Hz, nous retrouvons le message HELLO codé en Morse et nous retrouvons les timings présents dans l’énoncé, nous notons le déphasage de 90° entre la partie réelle et la partie imafinaire du signal.

Nous ne nous occupons pas du protocole de transmission avec le serveur, nous allons simplement générer un fichier signal.iq et utiliser client.py pour le transmettre au serveur.

Nous définissons des objets numpy.array correspondant aux différents éléments composant un message en Morse. Nous générons pour chaque signal les deux sinusoïdes décalées de 90°:

import numpy as np
MORSE_DOT = (np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2 + 0.5*np.pi)+1j*np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_DASH = (np.sin(np.arange(SAMP_RATE * TIMING_DASH) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2 + 0.5*np.pi)+1j*np.sin(np.arange(SAMP_RATE * TIMING_DASH) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_SEP_LETTER = (np.sin(np.arange(SAMP_RATE * TIMING_SEP_LETTER) * FREQ_SILENCE / SAMP_RATE * np.pi * 2 + 0.5*np.pi) + 1j*np.sin(np.arange(SAMP_RATE * TIMING_SEP_LETTER) * FREQ_SILENCE / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_SEP_SYMBOL = (np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SILENCE / SAMP_RATE * np.pi * 2 + 0.5*np.pi) + 1j*np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SILENCE / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_SPACE = (np.sin(np.arange(SAMP_RATE * (TIMING_SPACE-TIMING_SEP_LETTER)) * FREQ_SILENCE / SAMP_RATE * np.pi * 2 + 0.5*np.pi) + 1j*np.sin(np.arange(SAMP_RATE * (TIMING_SPACE-TIMING_SEP_LETTER)) * FREQ_SILENCE / SAMP_RATE * np.pi * 2)).astype(np.complex64)

Afin de respecter les timings à 10% nous devons faire attention à ne pas cumuler les espacements entre symboles, entre lettres et les silences correspondants aux caractère espace. Pour ceci nous utilisons des drapeaux indiquant si c’est le première caractère du message pour le premier symbole d’un caractère afin de n’insérer les espacement que quand c’est nécessaire.

Ceci nous donne le programme suivant:

import numpy as np
import sys

SAMP_RATE = 24e3
FREQ_SIGNAL = 5000
FREQ_SILENCE = 1000
TIMING_DOT = 1/1000
TIMING_DASH = 5/1000
TIMING_SEP_LETTER = 5/1000
TIMING_SPACE = 20/1000

MORSE_DOT = (np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2 + 0.5*np.pi)+1j*np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_DASH = (np.sin(np.arange(SAMP_RATE * TIMING_DASH) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2 + 0.5*np.pi)+1j*np.sin(np.arange(SAMP_RATE * TIMING_DASH) * FREQ_SIGNAL / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_SEP_LETTER = (np.sin(np.arange(SAMP_RATE * TIMING_SEP_LETTER) * FREQ_SILENCE / SAMP_RATE * np.pi * 2 + 0.5*np.pi) + 1j*np.sin(np.arange(SAMP_RATE * TIMING_SEP_LETTER) * FREQ_SILENCE / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_SEP_SYMBOL = (np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SILENCE / SAMP_RATE * np.pi * 2 + 0.5*np.pi) + 1j*np.sin(np.arange(SAMP_RATE * TIMING_DOT) * FREQ_SILENCE / SAMP_RATE * np.pi * 2)).astype(np.complex64)
MORSE_SPACE = (np.sin(np.arange(SAMP_RATE * (TIMING_SPACE-TIMING_SEP_LETTER)) * FREQ_SILENCE / SAMP_RATE * np.pi * 2 + 0.5*np.pi) + 1j*np.sin(np.arange(SAMP_RATE * (TIMING_SPACE-TIMING_SEP_LETTER)) * FREQ_SILENCE / SAMP_RATE * np.pi * 2)).astype(np.complex64)

alphabet = { 'A':'.-', 'B':'-...',
            'C':'-.-.', 'D':'-..', 'E':'.',
            'F':'..-.', 'G':'--.', 'H':'....',
            'I':'..', 'J':'.---', 'K':'-.-',
            'L':'.-..', 'M':'--', 'N':'-.',
            'O':'---', 'P':'.--.', 'Q':'--.-',
            'R':'.-.', 'S':'...', 'T':'-',
            'U':'..-', 'V':'...-', 'W':'.--',
            'X':'-..-', 'Y':'-.--', 'Z':'--..',
            '1':'.----', '2':'..---', '3':'...--',
            '4':'....-', '5':'.....', '6':'-....',
            '7':'--...', '8':'---..', '9':'----.',
            '0':'-----', ',':'--..--', '.':'.-.-.-',
            '?':'..--..', '/':'-..-.', '-':'-....-',
            '(':'-.--.', ')':'-.--.-'}

def morse_encode(msg):
    res = np.empty(0, dtype=np.complex64)

    is_first_letter = 1    
    for letter in msg:
        letter = letter.upper()

        if letter == " ":
            res = np.concatenate((res, MORSE_SPACE))
            continue

        if not is_first_letter:
            res = np.concatenate((res, MORSE_SEP_LETTER))
        is_first_letter = 0

        if letter in alphabet:
            is_first_symbol = 1
            for symbol in alphabet[letter]:
                if not is_first_symbol:
                    res = np.concatenate((res, MORSE_SEP_SYMBOL))
                is_first_symbol = 0
                if symbol == '-':
                    res = np.concatenate((res, MORSE_DASH))
                else:
                    res = np.concatenate((res, MORSE_DOT))
        else:
            print(f"Error '{letter}'")
            return "error"

    return res


def main():

    message = " ".join(sys.argv[1:])
    print(f"Le message est: {message}")
    res = morse_encode(message)

    res.astype('complex64').tofile('signal.iq')

if __name__ == "__main__":
    main()

Nous utilisons le script suivant pour synchroniser les opérations et réaliser notre test, nous utilisons commande nc pour attendre le temps nécessaire pour laisser au container le temps de démarrer:

#!/bin/bash

set -e

if [ ! -f docker-compose.yml ]; then
    wget https://hackropole.fr/challenges/fcsc2022-hardware-mommy-morse/docker-compose.public.yml -O docker-compose.yml
fi

if [ ! -f client.py ]; then
    wget https://hackropole.fr/challenges/fcsc2022-hardware-mommy-morse/public/client.py -O client.py
fi

docker-compose up -d

while ! nc -z localhost 4000; do sleep 1; done

python3 006_Mommy-morse.py CAN I GET THE FLAG
python3 client.py

docker-compose down

Le résultat est le suivant:

Creating network "006_mommy_morse_default" with the default driver
Creating 006_mommy_morse_mommy-morse_1 ... done
Le message est: CAN I GET THE FLAG
[+] Opening connection to localhost on port 4000: Done
b'Well done: FCSC{xxxxxxxx}\n'
[*] Closed connection to localhost port 4000
Stopping 006_mommy_morse_mommy-morse_1 ... done
Removing 006_mommy_morse_mommy-morse_1 ... done
Removing network 006_mommy_morse_default

C’est malin, maintenant j’ai la chanson “Baby Shark” en tête, c’est presque pire que “It’s a Small World”.