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}'