- Jh123x: Blog, Code, Fun and everything in between./
- My Blog Posts and Stories/
- GreyCTF 2023 Finals: Musical Talent/
GreyCTF 2023 Finals: Musical Talent
Table of Contents
This is the author’s writeup for the challenges Musical Talent
in the misc category.
This describes the intended solution when I made the challenge.
Challenge description #
Can you appreciate the hidden message in my song? This is only for the musically & mathematically talented.
- Junhua
A file called result.mid
was provided
My Notes #
- This is meant to be a different music related challenge from the typical DSP challenges.
- This was inspired by KeyTap: acoustic keyboard eavesdropping, source is provided here
The file #
The file is an old .mid
file. This is a standard that describes a communication protocol for music. It consists of a set of instructions for pitch and tempo that uses less space than an equivalent audio recording.
After opening the recoding, we see that it consists of multiple notes, each playing for a fixed length of time. There are also no notes played together at the same time.
Let us read this into a program to be processed further.
We can make use of mingus which supports different Midi file operations using python.
We can make use of mingus.midi.midi_file_in.MIDI_to_Composition
which converts the midi file into mingus container objects.
After reading the midi file, we can extract the notes into a frequency table before we process them. Once we sort them, we can map the most common notes to the most common letters in the english language and slowly adjust them from there.
The solution script is shown below.
import os
from typing import List, Tuple
from mingus.containers import NoteContainer, Track
from mingus.midi.midi_file_in import MIDI_to_Composition
path = os.path.join(os.path.dirname(__file__), "dist", "result.mid")
# Parsing with mingus
readResult :Tuple[List[Track], int] = MIDI_to_Composition(path)
results, _ = readResult
# results now contain the notes, compute the frequencies of the notes and the ordering
freq = {}
acc = []
for track in results:
noteContainer : NoteContainer
for _, _, noteContainer in track.get_notes():
for note in noteContainer:
note_name = f"{note.name}{note.octave}"
freq[note_name] = 1 if note_name not in freq else freq[note_name] + 1
acc.append(note_name)
# Sort the frequencies
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
# Map each of them to a letter and use frequency analysis to solve it.
letters = " etaiosnrlcdhfmupygbw,.vkxz'q0j164725_839-\\\{}"
# Map the frequencies to letters (Most common should be space)
mapping = {}
for i, (note, _) in enumerate(freq):
mapping[note] = letters[i]
# Map the notes to letters
final_passage = []
for note in acc:
final_passage.append(mapping[note])
passage = ''.join(final_passage)
with open('test.txt', 'w') as f:
f.write(passage)