Click here to Skip to main content
12,451,873 members (69,826 online)
Click here to Skip to main content
Add your own
alternative version

Stats

256.4K views
9.5K downloads
89 bookmarked
Posted

Steganography VIII - Hiding Data in Wave Audio Files

, 9 Apr 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
How to hide data of any kind inside a sound.

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[bytesPerSample];
    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[bytesPerSample-1];
            
            //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[bytesPerSample-1] = 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[bytesPerSample];
    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[bytesPerSample-1];
            
            //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); 

Revisions

  • 2012-04-09: Fixed some bugs in WaveUtility and frmRecorder

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Corinna John
Software Developer
Germany Germany
Corinna lives in Hannover/Germany (CeBIT City) and works as a Delphi developer, though her favorite language is C#.

You may also be interested in...

Comments and Discussions

 
QuestionWHAT IS THE KEY? Pin
Member 1231231523-Mar-16 22:43
memberMember 1231231523-Mar-16 22:43 
Questioncode Pin
Member 1190477212-Aug-15 7:14
memberMember 1190477212-Aug-15 7:14 
Questioncurruption of the final audio, checksum Pin
Member 1177795022-Jun-15 1:55
memberMember 1177795022-Jun-15 1:55 
Questionthe key > < Pin
Member 1157596529-Apr-15 6:23
memberMember 1157596529-Apr-15 6:23 
Questionkey file ? Pin
quangphat Đinh28-Nov-14 20:31
memberquangphat Đinh28-Nov-14 20:31 
QuestionProblem in running the code Pin
Member 1001408930-May-13 18:37
memberMember 1001408930-May-13 18:37 
QuestionDivide by zero error Pin
john_faf_04114-May-13 11:03
memberjohn_faf_04114-May-13 11:03 
Questionaudio steganography Pin
chandan522-Mar-13 0:52
memberchandan522-Mar-13 0:52 
Questionmeaning Pin
nmdfb z./28-Jan-13 22:10
membernmdfb z./28-Jan-13 22:10 
Questionquestion Pin
ishagarg26-Jun-12 9:57
memberishagarg26-Jun-12 9:57 
QuestionHow to run this Pin
bagabo12-May-12 7:32
memberbagabo12-May-12 7:32 
QuestionI need help.. Pin
heru dwi4-May-12 8:09
memberheru dwi4-May-12 8:09 
QuestionI download the source code and trying to run but an error occured "stegnowave.exe not found" i trying again and again but same thing occur or i download the source code 3 times but same thing occur pls help what i do i need its urgent pls help Pin
hindustan123453-May-12 23:51
memberhindustan123453-May-12 23:51 
Generalnull reference error during execution Pin
rakeshkolhe13-Apr-12 19:41
memberrakeshkolhe13-Apr-12 19:41 
Answeraudio steganography Pin
swatisinghal6-Apr-12 2:51
memberswatisinghal6-Apr-12 2:51 
QuestionAudio and video steganography errors Pin
Member 848774526-Mar-12 8:33
memberMember 848774526-Mar-12 8:33 
AnswerRe: Audio and video steganography errors Pin
Corinna John26-Mar-12 9:43
memberCorinna John26-Mar-12 9:43 
GeneralRe: Audio and video steganography errors Pin
Member 848774527-Mar-12 23:51
memberMember 848774527-Mar-12 23:51 
GeneralRe: Audio and video steganography errors Pin
Member 848774531-Mar-12 0:06
memberMember 848774531-Mar-12 0:06 
AnswerAudio and video steganography errors Pin
Member 86155808-Apr-12 22:15
memberMember 86155808-Apr-12 22:15 
GeneralRe: Audio and video steganography errors Pin
Corinna John9-Apr-12 8:31
memberCorinna John9-Apr-12 8:31 
Questionhow to run these code to implement the output Pin
rishi anand pandey25-Mar-12 16:02
memberrishi anand pandey25-Mar-12 16:02 
Generalgreeting Pin
mushyondori22-Jan-12 9:19
membermushyondori22-Jan-12 9:19 
QuestionRecording Not Working Pin
Ashwani Talreja26-Jun-11 6:35
memberAshwani Talreja26-Jun-11 6:35 
AnswerRe: Recording Not Working Pin
Corinna John8-Jul-11 9:26
memberCorinna John8-Jul-11 9:26 
Questionhow can we do it for mp3? Pin
nithinravi5352-Jan-11 22:16
groupnithinravi5352-Jan-11 22:16 
Questionhow can we to the same for mp3 file, Pin
napster922-Dec-10 18:57
membernapster922-Dec-10 18:57 
AnswerRe: how can we to the same for mp3 file, Pin
royzerr1-Feb-11 18:42
memberroyzerr1-Feb-11 18:42 
Questionruntime exception [modified] Pin
abdcefghij15-Dec-10 19:38
memberabdcefghij15-Dec-10 19:38 
AnswerRe: runtime exception Pin
abdcefghij20-Dec-10 0:59
memberabdcefghij20-Dec-10 0:59 
AnswerRe: runtime exception Pin
Corinna John22-Dec-10 1:58
memberCorinna John22-Dec-10 1:58 
GeneralMy vote of 5 Pin
Ashis Laha23-Nov-10 15:58
memberAshis Laha23-Nov-10 15:58 
QuestionExtract message without the key file? Pin
Member 307313623-Sep-10 10:40
memberMember 307313623-Sep-10 10:40 
AnswerRe: Extract message without the key file? Pin
Corinna John23-Sep-10 11:36
memberCorinna John23-Sep-10 11:36 
GeneralRuntime exception Pin
aqwsderfgtyhjuiklop18-Aug-10 10:23
memberaqwsderfgtyhjuiklop18-Aug-10 10:23 
GeneralRe: Runtime exception Pin
Corinna John18-Aug-10 10:53
memberCorinna John18-Aug-10 10:53 
GeneralRe: Runtime exception Pin
aqwsderfgtyhjuiklop18-Aug-10 14:02
memberaqwsderfgtyhjuiklop18-Aug-10 14:02 
GeneralThank you Pin
prof.deedee22-Jul-10 12:20
memberprof.deedee22-Jul-10 12:20 
QuestionRe: Thank you Pin
yami_k21-Apr-12 19:49
memberyami_k21-Apr-12 19:49 
Generallittle help Pin
prof.deedee22-Jul-10 3:31
memberprof.deedee22-Jul-10 3:31 
GeneralRe: little help Pin
Corinna John22-Jul-10 3:58
memberCorinna John22-Jul-10 3:58 
GeneralRe: little help Pin
prof.deedee22-Jul-10 5:13
memberprof.deedee22-Jul-10 5:13 
Generalhelp me please Pin
prof.deedee24-Jun-10 11:47
memberprof.deedee24-Jun-10 11:47 
GeneralRe: help me please Pin
Corinna John24-Jun-10 21:07
memberCorinna John24-Jun-10 21:07 
QuestionHow to extract the embeded data Pin
Dr.Rashiq5-Apr-10 10:06
memberDr.Rashiq5-Apr-10 10:06 
AnswerRe: How to extract the embeded data Pin
Corinna John6-Apr-10 21:21
memberCorinna John6-Apr-10 21:21 
GeneralRe: How to extract the embeded data Pin
Member 1157596529-Apr-15 7:07
memberMember 1157596529-Apr-15 7:07 
Questionhow are the key bytes decided? Pin
Dolly Mike4-Apr-10 20:06
memberDolly Mike4-Apr-10 20:06 
QuestionWhat s the algorithm tat s being used in this? Pin
Dolly Mike3-Apr-10 2:26
memberDolly Mike3-Apr-10 2:26 
AnswerRe: What s the algorithm tat s being used in this? Pin
Corinna John3-Apr-10 6:00
memberCorinna John3-Apr-10 6:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160826.1 | Last Updated 9 Apr 2012
Article Copyright 2004 by Corinna John
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid