Jump to content

Music In Pictures (music stenography)


careless223
 Share

Recommended Posts

I'm sure this is nothing new but I found this neat trick on another forum where a user had uploaded an image that had hidden music in it. It was essentially music stenography.

Here is the procedure.

1. Take your music file and rename it to a ".raw".

2. Open it in photoshop or paint shop pro.

3. If it asks you to specify a size, the X height must equal the exact byte size. (This varies though and in photoshop you can just use the guess function and it will most likely work.)

4. Save your creation as a ".png" with no compression.

To listen to it you need to do the reverse.

1. Download the file to your desktop and open it with photoshop or paint shop pro.

2. Save the file as a ".raw".

3. Rename to ".mp3" or whatever the original extension was.

Here is an example of what you get. Go ahead try to decode it.

Ashley_Witt_-_Hak5_1Theme1.png

Link to comment
Share on other sites

That's pretty cool. Although I haven't tried it, can this be done with any file?

Link to comment
Share on other sites

For Gimp users!!1! :P

You can use this command (in windows) and you can play your pictures in winamp

copy /b my_picture.jpg + my_song.mp3 my_new_picture.jpg

and in linux:

cat picture.gif sound.mp3 > combined.gif
Link to comment
Share on other sites

PNG is non-lossy, there is no reason it disable compression. I made a utility a few years ago to use this method to use image shack as a file hosting system, with files split into multiple images to get around the image size limit.

Link to comment
Share on other sites

Ok. Tried this with executables, but no go. They just corrupt the files. So, I instead stuffed it in a rar file first, then renamed the rar to raw, then did the image import in PhotoShop and saved it as a PNG. Did the reverse and renamed back to rar, and then opened the exe just fine.

This is nice, because you can now compress an exe file even smaller in the process without losing it in the converting process. Doing it in a rar, I didn't get prompted for a header size or dimensions. It did it automatically for me. When I tried it with renamed exe alone, they prompt you for all the info, which seemed to corrupt the final output.

Link to comment
Share on other sites

Ok. Tried this with executables, but no go. They just corrupt the files. So, I instead stuffed it in a rar file first, then renamed the rar to raw, then did the image import in PhotoShop and saved it as a PNG. Did the reverse and renamed back to rar, and then opened the exe just fine.

This is nice, because you can now compress an exe file even smaller in the process without losing it in the converting process. Doing it in a rar, I didn't get prompted for a header size or dimensions. It did it automatically for me. When I tried it with renamed exe alone, they prompt you for all the info, which seemed to corrupt the final output.

You can do it with an EXE, I don't know about this method but my utility I made which did the same thing worked with anything. The only reason I could think of might make it not work using this method is if the image dimensions meant that it had extra data on the end (would probably look like a line of black pixels) especially if the EXE length was prime maybe. If you add extraneous data to the end of an EXE it won't work, but a RAR will. My utility had a metafile with file length so it didn't read in extra data and MD5 hashes to make sure it wasn't corrupt. I might still have it somewhere.

Link to comment
Share on other sites

I think what this is doing (after listening to the gimp post) is that you are actually zipping two files into a new file. So, what you do in Windows it go into command prompt, take your Song, take your picture, and put them into a lossless compression format (0% compression) or make it lossless. then rename the file. This is how some viruses are put into exes as trojans. when the person starts the application, it will open up both executables inside the 'executable' you clicked.

Link to comment
Share on other sites

I think what this is doing (after listening to the gimp post) is that you are actually zipping two files into a new file. So, what you do in Windows it go into command prompt, take your Song, take your picture, and put them into a lossless compression format (0% compression) or make it lossless. then rename the file. This is how some viruses are put into exes as trojans. when the person starts the application, it will open up both executables inside the 'executable' you clicked.

The method iisonly posted is nothing to do with zipping, it just concatenates two files and the software that opens them is quite tolerant of extra unnecessary data and ignores it.

The actual method used to inject viruses into EXEs usually not how you described, it's quite a bit more complicated, generally either fitting the extra instructions into exist code caves in the original EXE or extending or adding a section by editing the PE header and section table and putting the extra code in that. You can't just concatenate two EXEs because the length won't match up with the data in the PE header and windows will just tell you it's not a valid win32 application.

Link to comment
Share on other sites

yayyy0.png

just did one

Well, I converted it back, but this file was corrupt for some reason and didn't open as an mp3. Were you able to successfully convert it back to an mp3 and play it? I opened it in an editor, and it looks like it tripled each character, so I don't think it was created properly.

Link to comment
Share on other sites

Well, I converted it back, but this file was corrupt for some reason and didn't open as an mp3. Were you able to successfully convert it back to an mp3 and play it? I opened it in an editor, and it looks like it tripled each character, so I don't think it was created properly.

yep, did you select this option when saving the raw file

29029350au7.jpg

has to be non interleaved to retain the original code or it will be corrupt

Link to comment
Share on other sites

I couldn't find my old image shack file storage thing, but I felt like rewriting the image encoding part, the old one had input checking, split files into multiple images and handled all the uploading and downloading from imageshack too, but this is just an encoder/decoder. It uses 32bit PNGs unlike when you import files into photoshop as RAWs. This PNG is actually 25% smaller (400kB) than the MP3 file it was encoded from due to the PNG RLE compression, which just shows how shitty MP3 is as a codec. It seems that having a wide image results in better compression than a long image, but too wide seems to cause problems.

testyp8.png

The one improvement I made is storing the file length of the original file in the PNG metadata, so there is no need to have an extra file to store the file length in order to be able to strip off any padding necessary to make the data fit in the image. The only downside of this is some image host or utility which doesn't respect PNG metadata could strip the data necessary to turn it back into a file, but imageshack at least leaves your files alone. I considered putting the file length in the first pixel or something, but I like it this way.

Here's the code if anyone wants it, it needs Python 2.5 with the Python Imaging Library 1.1.6. Also a Windows binary here if you can't be bothered to install those.

#!/usr/bin/env python

import Image, PngImagePlugin
import random
import sys, getopt

class BinPng():
    def __init__(self, png_path = None, bin_path = None, constraint = None, longest = None):
        self.bin_data = None
        self.png = None
        self.metadata = None
        self.constraint = constraint
        self.longest = longest

        if (png_path):
            self.load_png(png_path)
        if (bin_path):
            self.load_bin(bin_path)

    def load_bin(self, bin_path):
        f = open(bin_path, 'rb')
        data = f.read()
        f.close()

        self.bin_data = data

    def load_png(self, png_path):
        self.png = Image.open(png_path)

    def bin_to_png(self):
        if (not self.bin_data):
            return False

        data = self.bin_data

        #make a PNG metadata field to store the actual file length so we can strip off the padding later
        self.metadata = PngImagePlugin.PngInfo()
        self.metadata.add_text('length', str(len(data)))

        data = self.pad_data(data, self.constraint)
        size = self.get_dimensions(len(data), self.constraint, self.longest)

        self.png = Image.fromstring('RGBA', size, data)

    def png_to_bin(self):
        if (not self.png):
            return False

        self.bin_data = self.png.tostring()[:int(self.png.info['length'])]

    def write_bin(self, file_path, auto_convert = True):
        if (auto_convert == True):
            self.png_to_bin()

        f = open(file_path, 'wb')
        f.write(self.bin_data)
        f.close()

    def write_png(self, file_path, auto_convert = True):
        if (auto_convert == True):
            self.bin_to_png()

        self.png.save(file_path, 'PNG', pnginfo = self.metadata)

    def fermat_primality(self, n):
        checks = 0
        #do 20 fermat checks, should be good enough for this porpoise
        while checks < 20:
            a = random.randint(1, n-1)
            if pow(a, n-1, n) != 1:
                return False
            checks += 1
        return True

    def pad_data(self, data, dimension_constraint = None):
        length = len(data)
        #pad data to make it fit in 32bit pixels
        extra = '\x00' * (4 - length % 4)
        length += (4 - length % 4)

        if (dimension_constraint and dimension_constraint < length/4):
            extra += '\x00' * 4 * (dimension_constraint - length/4 % dimension_constraint)
            length += 4 * (dimension_constraint - length/4 % dimension_constraint)
            return data + extra

        while (self.fermat_primality(length/4)):
            #pad probable prime pixel lengths to get nicer dimensions
            extra += '\x00' * 4
            length += 4
        return data + extra

    def get_dimensions(self, length, dimension_constraint = None, longest = None):
        length /= 4

        if (dimension_constraint and dimension_constraint < length):
            ceil = dimension_constraint
        else:
            ceil = int(length**0.5)

        while (ceil > 0):
            if (length % ceil == 0):
                if (length / ceil > ceil):
                    size = (length / ceil, ceil)
                else:
                    size = (ceil, length / ceil)

                if (longest == 'x' or not longest):
                    return size
                else:
                    return (size[1], size[0])

            ceil -= 1

if __name__ == '__main__':
    usage = '-e <file>\tEncode this file to PNG\n' + \
            '-d <file>\tDecode from this PNG\n' + \
            '-o <file>\tFile to write to\n\n' + \
            'Optional options only used for encoding:\n' + \
            '-c <number>\tConstrain one dimension to this length, defaults to 1024\n' + \
            '-l <x or y>\tMake the X or Y dimension the longest, defaults to Y'

    try:
        opts, args = getopt.getopt(sys.argv[1:], 'he:d:o:l:c:')
    except getopt.GetoptError, e:
        print str(e)
        print usage
        sys.exit()

    operation = None
    in_file = None
    out_file = None
    l = 'y'
    c = 1024

    for opt, arg in opts:
        if (opt == '-h'):
            print usage
            sys.exit()
        elif (opt == '-e'):
            operation = 'e'
            in_file = arg
        elif (opt == '-d'):
            operation = 'd'
            in_file = arg
        elif (opt == '-o'):
            out_file = arg
        elif (opt == '-l'):
            l = arg.lower()
        elif (opt == '-c'):
            c = int(arg)

    if (operation == 'e'):
        BinPng(bin_path = in_file, longest = l, constraint = c).write_png(out_file)
    elif (operation == 'd'):
        BinPng(png_path = in_file).write_bin(out_file)
    elif (not operation):
        print usage

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...