Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Now that we have hidden data in bitmaps, MIDI tracks and .NET assemblies, you might miss one important file format. You might miss the files that can hide lots of bytes without becoming larger, and can be generated in a few seconds, so that you don't have to store the original files on your disk. It is time to add Wave Audio to the list.

This article uses code from A full-duplex audio player in C# using the waveIn/waveOut APIs.

The Wave File Format

Have you ever looked at a Wave file in a HEX editor? It starts like that, and continues with unreadable binary data:

Every RIFF file starts with the text "RIFF", followed by the Int32 length of the entire file:

The next fields say that this RIFF file contains Wave data and open the format chunk:

The length of the following format chunk must be 16 for PCM files:

Now the format is being specified by a WAVEFORMATEX structure:

The format chunk can be followed by some extra information. Then the interesting parts begin with the data chunk.

The data chunk contains all the Wave samples. That means the rest of the file is pure audio data. Little changes might be hearable, but won't destroy the file.

Hiding the Message

Hiding a message in Wave samples is very similar to hiding it in the pixels of a bitmap. Again, we use a key stream to skip a number of carrier units (samples/pixels), grab one carrier unit, put one bit of the message into the lowest bit of the carrier unit, and write the changed unit to the destination stream. When the entire message has been hidden like that, we copy the rest of the carrier stream.

public void Hide(Stream messageStream, Stream keyStream){
    
    byte[] waveBuffer = new byte;
    byte message, bit, waveByte;
    int messageBuffer; //receives the next byte of the message or -1

    int keyByte; //distance of the next carrier sample

    
    //loop over the message, hide each byte

    while( (messageBuffer=messageStream.ReadByte()) >= 0 ){
        //read one byte of the message stream

        message = (byte)messageBuffer;
        
        //for each bit in [message]

        for(int bitIndex=0; bitIndex<8; bitIndex++){
            
            //read a byte from the key

            keyByte = GetKeyValue(keyStream);
            
            //skip a couple of samples

            for(int n=0; n<keyByte-1; n++){
                //copy one sample from the clean stream to the carrier stream

                sourceStream.Copy(
                    waveBuffer, 0,
                    waveBuffer.Length, destinationStream);
            }

            //read one sample from the wave stream

            sourceStream.Read(waveBuffer, 0, waveBuffer.Length);
            waveByte = waveBuffer;
            
            //get the next bit from the current message byte...

            bit = (byte)(((message & (byte)(1 << bitIndex)) > 0) ? 1 : 0);
                
            //...place it in the last bit of the sample

            if((bit == 1) && ((waveByte % 2) == 0)){
                waveByte += 1;
            }else if((bit == 0) && ((waveByte % 2) == 1)){
                waveByte -= 1;
            }

            waveBuffer = waveByte;

            //write the result to destinationStream

            destinationStream.Write(waveBuffer, 0, bytesPerSample);
        }
    }

    //copy the rest of the wave without changes

    //...

}

Extracting the Message

Again, we use the key stream to locate the right samples, just as we did while hiding the message. Then we read the last bit of the sample and shift it into the current byte of the message. When the byte is complete, we write it into the message stream and continue with the next one.

public void Extract(Stream messageStream, Stream keyStream){

    byte[] waveBuffer = new byte;
    byte message, bit, waveByte;
    int messageLength = 0; //expected length of the message

    int keyByte; //distance of the next carrier sample

    
    while( (messageLength==0 || messageStream.Length<messageLength) ){
        //clear the message-byte

        message = 0;
        
        //for each bit in [message]

        for(int bitIndex=0; bitIndex<8; bitIndex++){

            //read a byte from the key

            keyByte = GetKeyValue(keyStream);
            
            //skip a couple of samples

            for(int n=0; n<keyByte; n++){
                //read one sample from the wave stream

                sourceStream.Read(waveBuffer, 0, waveBuffer.Length);
            }
            waveByte = waveBuffer;
            
            //get the last bit of the sample...

            bit = (byte)(((waveByte % 2) == 0) ? 0 : 1);

            //...write it into the message-byte

            message += (byte)(bit << bitIndex);
        }

        //add the re-constructed byte to the message

        messageStream.WriteByte(message);
        
        if(messageLength==0 && messageStream.Length==4){
            //first 4 bytes contain the message's length

            //...

        }
    }
}

Recording a Wave

Keeping the original clean carriers can be dangerous. Somebody who has already got a carrier file with a secret message in it, and manages to get the original file without the hidden message, can easily compare the two files, count the distance in bytes between two non-equal samples, and quickly reconstruct the key.

That is why we have to delete and destroy our clean carrier files after we've used them once, or record a wave on the fly. Thanks to Ianier Munoz' WaveInRecorder, it is no problem to record Wave data and hide the message in it before saving anything to a disk. There is no original file, so we do not need to care about one. In the main form, the user can choose between using an existing Wave file or recording a sound right then. If he wants to record a unique, not reproducible sound, he can plug in a microphone and speak/play/... whatever he likes:

if(rdoSrcFile.Checked){
    //use a .wav file as the carrier

    //do not complain later on, you have been warned

    sourceStream = new FileStream(txtSrcFile.Text, FileMode.Open);
}else{
    //record a carrier wave

    frmRecorder recorder = new frmRecorder(countSamplesRequired);
    recorder.ShowDialog(this);
    sourceStream = recorder.RecordedStream;
}

frmRecorder is a small GUI for the WaveIn Recorder that counts the recorded samples and enables a Stop button when the sound is long enough to hide the specified message.

The new sound is stored in a MemoryStream and passed to WaveUtility. From now on, it does not matter where the stream came from, WaveUtility makes no difference between sounds read from a file or recorded on the fly.

WaveUtility utility = new WaveUtility(sourceStream, destinationStream);
utility.Hide(messageStream, keyStream);
You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalhow can we know whether the given input wav file is a normal file or stego file??
techminds
3:36 13 Mar '10  
If we insert another message into the stego file can we get both the files when we extract them
GeneralRe: how can we know whether the given input wav file is a normal file or stego file??
Corinna John
10:53 13 Mar '10  
No, because both files will very likely be embedded in the same samples. That means, the second file overwrites (parts of) the first one.
This statement is false.

GeneralRe: how can we know whether the given input wav file is a normal file or stego file??
techminds
16:18 13 Mar '10  
thanQ. . Smile Smile
GeneralUsing the embedded wave file again for Embedding
techminds
0:40 13 Mar '10  
I used the stego audio file that is obtained by embedding the text file again to embed another file it is embedded but i am unable to extract the both can you tell me what to do?? and the solution to solve it
GeneralRe: Using the embedded wave file again for Embedding
Corinna John
10:51 13 Mar '10  
You can extract only the last file. Every file you embed overwrites important parts of the last one.
This statement is false.

GeneralWave Format
techminds
20:18 3 Mar '10  
I studied the project and it is good and i learned a lot from it. Can you please explain me by giving a brief description about Wave Format and the classes
1) Wave Stream
2) Wave Utility
3) Wave Native
4) Wave In

and the use of SumKeyArray in the Wave Utility class
GeneralAudio Steganography
Nor Farhana
21:07 7 Nov '09  
hello...can u help me in audio steganography..i have a big problem to understand how to hide audio .wav format to audio also in .wav format using LSB substitution technique..and coding using MATLAB software..if u know please help me..thank u very much..
GeneralMultiple watermarks
brucelee05
8:18 21 Feb '08  
Hi,

Does anyone know how to create multiple watermarks in an audio file. This is one of methods to increase the robustness of watermarks. Are there any algorithm for this?

Thank you.
Generalhow many character that can stored in one file..?
merdiansigit
19:48 5 Jan '08  
A character (UTF-8) needs eight bits, so you need eight wave samples per character.
countCharacters = wave.samplesPerSecond * wave.durationInSeconds / 8 for an 8bit/mono wave.

if using 16 bit/sample?
countCharacters = wave.samplesPerSecond * wave.durationInSeconds / 16
Am i right?

In your program wave.samplesPerSecond = 22050
Why you using this value to initialize wave format?

Where we can get the value wave.durationInSeconds?
Thanks before Smile
GeneralAUDIO STEGANOGRAPHY [modified]
pranav4u07
1:56 27 Dec '07  
Hi there,



My name is pranav..I considered myself as a newbie in programming .. ...I’m a final year student and I’m doing a project called STEGANOGRAPHY IN AUDIO..

modified on Thursday, December 27, 2007 7:05:48 AM

Questionsteganography in audio
Damnape
9:57 14 Nov '07  
Hi there,



My name is josh..I considered myself as a newbie in programming .. ...I’m a final year student and I’m doing a project called STEGANOGRAPHY IN AUDIO..



I’m having a problem to make a decision on which is the suitable software to be used in my project...Either using NETBEANs IDE or MICROSOFT VISUAL C++6.0...HELP me please…D'Oh!






Regards’



AnswerRe: steganography in audio
Corinna John
11:51 14 Nov '07  
Hi Josh,
there is not best environment for programming. Choose the software you like best, or the language you want to learn first. Audio stego means a lot of math with integer arrays, you can do that with every programming language/environment.

____________________________________
There is no proof for this sentence.

GeneralRobustness Attack
lonelywind1982
18:10 4 Sep '07  
How can i increasing Robustness of LSB Algorithm to avoid Robustness Attack.
General.mp3, .jpeg in C#.NET
Software_Specialist
12:52 24 Apr '07  
Hi corinna
Have you created any application with .jpeg and .mp3 carriers in C#.NET for steganography. There are not any links for C#.NET. I dont have much idea with it so is it quite hard to implement that over .NET or its not. You must have tried to implement it over .jpeg and .mp3 carriers...
Thanks
SS
GeneralRe: .mp3, .jpeg in C#.NET
Software_Specialist
6:22 3 May '07  
Hallo Corrina,
Wie geht es dir ?
These days i am in bavaria for another few months.
i am waiting for your great reply.
Danke
tschuess
Generalsize
21:56 21 Mar '07  
what is the size limit of text file and key file

ghjgj

GeneralRe: size
Corinna John
9:58 22 Mar '07  
There is no size lilmit for the key file.
The length of the text is stored in a four byte value, so the maximum length is 4.294.967.295 bytes.

____________________________________
There is no proof for this sentence.

Generalsize
5:16 21 Mar '07  
what is the size limit of the text file... where will be stored key file?

ghjgj

GeneralRe: size
Corinna John
6:15 21 Mar '07  
wrote:
what is the size limit of the text file

It depends on the size of the wave file: countOfSamples/8 Bytes.

wrote:
where will be stored key file

Where ever you put it. The key file is your secret, you need it to extract the text. So, store it in a safe place. Smile

____________________________________
There is no proof for this sentence.

GeneralVS2005 Compiler Warning
Moomansun
21:28 7 Sep '06  
The dispose method will generate a compiler warning under VS2005:

Warning 1
'SteganoWave.WaveStream.Dispose()' hides inherited member 'System.IO.Stream.Dispose()'.
Use the new keyword if hiding was intended.
WaveStream.cs 217 16 SteganoWave


Change this method:

public void Dispose()
{
if (m_Stream != null)
m_Stream.Close();
GC.SuppressFinalize(this);
}


Add the 'new' keyword before the type and name:

public new void Dispose()
{
if (m_Stream != null)
m_Stream.Close();
GC.SuppressFinalize(this);
}
Generalwav
maduram
7:51 8 Feb '06  
could any send me a wav file which we would use for hiding message

shelley
GeneralRe: wav
Corinna John
0:15 9 Feb '06  
You can get sounds everywhere. If your provider has blocked www.google.com, just try this site:
http://www.moviesounds.com/

_________________________________
Please inform me about my English mistakes, as I'm still trying to learn your language!

Generalhow many character that can stored in one file?
pinhard
9:09 22 Jan '06  
how many character that can stored in one file? and is every bit of the message stored in LSB of the wav data?

sorry about my poor english, I need that information to built my study
GeneralRe: how many character that can stored in one file?
Corinna John
9:51 22 Jan '06  
A character (UTF-8) needs eight bits, so you need eight wave samples per character.
countCharacters = wave.samplesPerSecond * wave.durationInSeconds / 8 for an 8bit/mono wave.

_________________________________
Please inform me about my English mistakes, as I'm still trying to learn your language!

Generalcomparing wave files
kahuri
7:15 4 May '05  
Does anyone know how u can compare two wave files to know if they contain the same sound or if one is a subset of the other

James


Last Updated 6 May 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010