En analysant daddy-morse.py
on voit qu’il faut envoyer la chaîne CAN I GET THE FLAG
pour récuperer le flag.
Les caractéristiques du signal à envoyer sont les suivantes:
-
fréquence d’échantillonnage : 24kHz
-
durée d’un . : 1 milliseconde
-
durée d’un - : 5 millisecondes
-
espacement entre deux lettres : 5 millisecondes
-
espacement entre deux mots : 20 millisecondes
-
espacement entre deux symboles d’une letttre: 1 milliseconde
-
les données sont de type np.complex64, seule partie réelle
s[0]
est utilisée et il faut que sa valeur soit supérieure à 0.1
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 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, avec numpy.full
pour remplir avec du signal (1+1j
) et numpy.zeros
pour générer du silence.
import numpy as np
MORSE_DOT = np.full(int(TIMING_DOT*SAMP_RATE), 1+1j, dtype=np.complex64)
MORSE_DASH = np.full(int(TIMING_DASH*SAMP_RATE), 1+1j, dtype=np.complex64)
MORSE_SEP_LETTER = np.zeros(int(TIMING_SEP_LETTER*SAMP_RATE), dtype=np.complex64)
MORSE_SEP_SYMBOL = np.zeros(int(TIMING_DOT*SAMP_RATE), dtype=np.complex64)
MORSE_SPACE = np.zeros(int((TIMING_SPACE-TIMING_SEP_LETTER)*SAMP_RATE), dtype=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
TIMING_DOT = 1/1000
TIMING_DASH = 5/1000
TIMING_SEP_LETTER = 5/1000
TIMING_SPACE = 20/1000
MORSE_DOT = np.full(int(TIMING_DOT*SAMP_RATE), 1+1j, dtype=np.complex64)
MORSE_DASH = np.full(int(TIMING_DASH*SAMP_RATE), 1+1j, dtype=np.complex64)
MORSE_SEP_LETTER = np.zeros(int(TIMING_SEP_LETTER*SAMP_RATE), dtype=np.complex64)
MORSE_SEP_SYMBOL = np.zeros(int(TIMING_DOT*SAMP_RATE), dtype=np.complex64)
MORSE_SPACE = np.zeros(int((TIMING_SPACE-TIMING_SEP_LETTER)*SAMP_RATE), dtype=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-daddy-morse/docker-compose.public.yml -O docker-compose.yml
fi
if [ ! -f client.py ]; then
wget https://hackropole.fr/challenges/fcsc2022-hardware-daddy-morse/public/client.py -O client.py
fi
docker-compose up -d
while ! nc -z localhost 4000; do sleep 1; done
python3 006_Daddy-morse.py CAN I GET THE FLAG
python3 client.py
docker-compose down
Le résultat est le suivant:
Creating network "006_daddy_morse_default" with the default driver
Creating 006_daddy_morse_daddy-morse_1 ... done
Le message est: CAN I GET THE FLAG
[+] Opening connection to localhost on port 4000: Done
b'Well done: FCSC{xxxxxxxxx}\n'
[*] Closed connection to localhost port 4000
Stopping 006_daddy_morse_daddy-morse_1 ... done
Removing 006_daddy_morse_daddy-morse_1 ... done
Removing network 006_daddy_morse_default