data2sound, audible covert channel

Via reddit I have found this interesting piece of software, for now it is just a PoC showing in a simple way how to code data into sound. The approach works perfectly and it could be improved with not so much work to produce some of that “8bits music”.

The package contains two python scripts, data2sound.py to code the data into a WAV file and sound2data.py to revert the operation.

data2sound takes the file we want to be transfered to sounds and performs the following operations in order to produce the output wav file.

  • It writes a 440 tone used as header
  • Then starts coding each single byte in the input file. The scripts sends to the WAV file waves with the same amplituded but with the frequency changing along the value of each nibble computed (frequencies chosen are hardset), as can be seen in the next chunk of code:
      while True:
        data = infh.read(1)
        if data == "":
          break
     
        (m,) = struct.unpack("B", data)
     
        a = m & 0xF
        b = m >> 4
     
        for halfbyte in (a,b):
          frequency = frequencies[halfbyte]
          period = 2*math.pi/(fmt.samplesPerSec/float(frequency))
          for i in xrange(0, halfByteSampleLength):
            sample = amplitude * (1 + math.sin(period*float(i)))
            fh.write(struct.pack("h", int(sample)))
            fh.flush()
          j += 1

    The output wave can be seen in the following image, freq changes can be noticed in the image:

    Coded data

The script sound2data performs the inverse operation, it first looks for the 440 tone and if it is found applies FFT to calculate the frequency of the read waves and then matches one of the hardset frequencies as seen in the next chunk of code:

def get_maxfrequency(samples, norm, maxAcceptable = 1200):
  samplesArr = numpy.numarray.array(samples)
  transformedSamples = numpy.fft.fft(samplesArr)
 
  maxFreq = 0
  maxI = 0
  for i, freq in enumerate(transformedSamples):
    j = int(norm * i)
    a = math.sqrt(freq.real**2 + freq.imag**2)
    if a > maxFreq and i > 0 and j < maxAcceptable:
      maxFreq = a
      maxI = j
 
  return maxI

And below the loop decoding the read data:

  while True:
    try:
      halfByte = []
      for w in xrange(0,2):
        samples = []
        for i in xrange(0, halfByteSampleLength):
          (sample,) = read_and_unpack(fh, "h")
          samples.append(sample)
 
        freq = get_maxfrequency(samples, samplesPerSec / float(halfByteSampleLength))
        v = get_closest_index(frequencies, freq)
        halfByte.append(v)
 
      fullbyte = halfByte[0] | (halfByte[1] << 4)
      buffer += chr(fullbyte)
 
      j += 1
    except:
      break

I run the script to code a file with “ABCDEFGH” and I got this wave file.