VolgaCTF 2015 MIDI
@mrexcessive WHA
The problem
Op.12 No.7. Link to .midi file
Hmmm what do they mean ?
The solution
Loaded it into a midi player online in case something obvious
http://onlinesequencer.net/import2/14b0424e7484dac370b0cbc8bb822e47?title=MIDI_output.mid
I used to program MIDI stuff on Atari ST - back in the days of 68000 - lovely processor !
Research a bit... reminders.
# midi file format
http://www.ccarh.org/courses/253/handout/smf
# midi messages
http://www.midi.org/techspecs/midimessages.php
OK write some python to read it in and parse Midi has variable length value fields... that's the only real complication. import struct def ParseMidiFile(fname): global data f = open(fname,"rb") data = f.read() f.close() (header_label,) = struct.unpack("4s",data[0:4]) assert(header_label == "MThd") (header_length,header_format,header_num_tracks,header_division) = \ struct.unpack(">IHHH",data[4:14]) print "Header.length %i" % header_length print " .format %i" % header_format print " .num tracks %i" % header_num_tracks print " .division %i" % header_division idx = 14 # read track chunk start (track_chunk_label,track_length) = struct.unpack(">4sI",data[idx:idx+8]) assert(track_chunk_label == "MTrk") print "Track.length %i" % track_length idx += 8 # read all on-track events timeval = 0 noteson = 0 while idx < len(data): (idx,timevalnew) = GetVVal(idx) # note variable length time values timedelta = timevalnew - timeval timesep = "IDX,dt = x%04x, %04i" % (idx,timedelta) timeval = timevalnew (idx,b) = GetByte(idx) if b == 0xff: #process meta event (idx,b) = GetByte(idx) assert(b == 0x2f) # otherwise something went wrong (for this particular data)... this is end of track break elif b in [0x80,0x90]: chan = b & 0xf assert(chan == 0) # only one channel or strange... (idx,notenum) = GetByte(idx) char_notenum = Printable(notenum) (idx,velocity) = GetByte(idx) char_velocity = Printable(velocity) if b == 0x80: what = " ON" noteson = noteson * 12 + (notenum - 0x30) else: what = "OFF" print "%s %s %02x %s %02x %s" % (timesep, what, notenum, char_notenum, velocity, char_velocity) print "NotesOn = [%i]" % noteson def Printable(b): if chr(b) in printables: return chr(b) else: return "." def GetByte(idx): b = ord(data[idx]) idx += 1 return (idx,b) def GetVVal(idx): # read variable length value, see http://www.ccarh.org/courses/253/handout/smf/ global data # returns (newidx,value) v = 0 stop = False while not stop: b = ord(data[idx]) idx += 1 if b & 0x80 <> 0: # top bit set v += b & 0x7f v *= 128 else: v += b stop = True return (idx,v)
Check the code..
Read the Note on events from the MIDI
Note that the Notes are in a range size 12 the note numbers are all in range x30-x3b
The clue is Opus 12 number 7...
So I thought maybe take the hexdigit low-nibble values ('0'..'b') mod 12 and multiply this up into a number...
n=(((d0 * 12) + d1)*12 + d2)*12 + ...... collected those... turned it to hex and it looks printable.
s = hex(89725914036136583200302417724718113017272901028608688284737689938085730897620786877947729416479134352940492083134939698709007681802564221) >>> s '0x7b72616e646f6d6c795f67656e6572617465645f70696563655f6f665f6d757369635f69735f6265747465725f7468656e5f6269656265727dL' >>> s = s[2:-1] >>> s.decode("hex") '{randomly_generated_piece_of_music_is_better_then_bieber}'