Click here to Skip to main content
12,246,754 members (53,542 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

88.4K views
9.3K downloads
67 bookmarked
Posted

Steganography: Simple Implementation in C#

, 23 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Embed/extract a text in/from an image

Introduction

Steganography is the art and science of hiding information by embedding messages within others. Steganography works by replacing bits of useless or unused data in regular computer files with bits of different, invisible information. This hidden information can be plain text, cipher text, or even images [*] .

I've implemented two main methods for that purpose: embedText and extractText. You can find the full and documented code with a running example attached to this tip.

Executing the Code

If you download the attached sample project, it allows you to open an image, write your text or import a text file, optionally encrypt your text before starting processing, embed the text in the image, and finally, save the result image in either PNG or BMP formats. Then you can reopen that image and extract the text from it.

Caution: Don't save the result image in a lossy format (like JPEG); your data will be lost. Saving it as PNG is pretty good.

Explaining the Code

As mentioned before, Steganography works by replacing bits of useless or unused data in regular computer files with bits of our important data. In our case, our data will be the plain text that we need to hide, and the unused data is the least significant bits (LSBs) in the image pixels.

What is the LSB?

In order to find the decimal value of a binary value, we sum all 1s in the binary value but multiplied by 2n, where n is the position/index of the 1 from the right, starting from zero. For example, to convert 01100101 to decimal, then starting from the right, the equivalent decimal value is 1x20 + 1x22 + 1x25 + 1x26 = 1 + 4 + 32 + 64 = 101

The least significant bit (LSB) is the bit that when flipped from 0 to 1 or from 1 to 0, then no significant change will occur on the total value. It's the bit on the rightmost, that when flipped, the value will be only affected by 1 to be 100 instead of 101. This means that the image will not be significantly affected when we reserve this bit for our purpose. Where the most significant bit (MSB) is bit on the leftmost, that when flipped, the value will be affected by 128 (1x27) to be 229 instead of 101.

When we try to hide our data in an image (as in our case), then we need enough budget of LSBs to hide our data in. These bits are located in the image pixels. Since each pixel has three elements (R, G, and B that represent the Red, Green, and Blue elements of the pixel consecutively, assuming non-transparent image), each of these elements can have a value between 0 and 255. Now, assume that the image was 300 pixels width by 400 pixels height, then we'll have 300 x 400 x 3 = 360000 LSBs. And as each character can be represented by 8 bits, then that image can hide 360000 / 8 = 45000 characters!

Step by Step

  1. Hiding the text inside the image
    • Loop through the pixels of the image. In each iteration, get the RGB values separated each in a separate integer.
    • For each of R, G, and B, make the LSB equals to 0. These bits will be used in hiding characters.
    • Get the current character and convert it to integer. Then hide its 8 bits in R1, G1, B1, R2, G2, B2, R3, G3, where the numbers refer to the numbers of the pixels. In each LSB of these elements (from R1 to G3), hide the bits of the character consecutively.
    • When the 8 bits of the character are processed, jump to the next character, and repeat the process until the whole text is processed.
    • The text can be hidden in a small part of the image according to the length of that text. So, there must be something to indicate that here we reached the end of the text. The indicator is simply 8 consecutive zeros. This will be needed when extracting the text from the image.
  2. Extracting the text from the image

    It's more simple than hiding. Just pass through the pixels of the image until you find 8 consecutive zeros. As you are passing, pick the LSB from each pixel element (R, G, B) and attach it into an empty value. When the 8 bits of this value are done, convert it back to character, then add that character to the result text you are seeking.

The Source Code

class SteganographyHelper
{
    public enum State
    {
        Hiding,
        Filling_With_Zeros
    };

    public static Bitmap embedText(string text, Bitmap bmp)
    {
        // initially, we'll be hiding characters in the image
        State state = State.Hiding;

        // holds the index of the character that is being hidden
        int charIndex = 0;

        // holds the value of the character converted to integer
        int charValue = 0;

        // holds the index of the color element (R or G or B) that is currently being processed
        long pixelElementIndex = 0;

        // holds the number of trailing zeros that have been added when finishing the process
        int zeros = 0;

        // hold pixel elements
        int R = 0, G = 0, B = 0;

        // pass through the rows
        for (int i = 0; i < bmp.Height; i++)
        {
            // pass through each row
            for (int j = 0; j < bmp.Width; j++)
            {
                // holds the pixel that is currently being processed
                Color pixel = bmp.GetPixel(j, i);

                // now, clear the least significant bit (LSB) from each pixel element
                R = pixel.R - pixel.R % 2;
                G = pixel.G - pixel.G % 2;
                B = pixel.B - pixel.B % 2;

                // for each pixel, pass through its elements (RGB)
                for (int n = 0; n < 3; n++)
                {
                    // check if new 8 bits has been processed
                    if (pixelElementIndex % 8 == 0)
                    {
                        // check if the whole process has finished
                        // we can say that it's finished when 8 zeros are added
                        if (state == State.Filling_With_Zeros && zeros == 8)
                        {
                            // apply the last pixel on the image
                            // even if only a part of its elements have been affected
                            if ((pixelElementIndex - 1) % 3 < 2)
                            {
                                bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
                            }

                            // return the bitmap with the text hidden in
                            return bmp;
                        }

                        // check if all characters has been hidden
                        if (charIndex >= text.Length)
                        {
                            // start adding zeros to mark the end of the text
                            state = State.Filling_With_Zeros;
                        }
                        else
                        {
                            // move to the next character and process again
                            charValue = text[charIndex++];
                        }
                    }

                    // check which pixel element has the turn to hide a bit in its LSB
                    switch (pixelElementIndex % 3)
                    {
                        case 0:
                            {
                                if (state == State.Hiding)
                                {
                                    // the rightmost bit in the character will be (charValue % 2)
                                    // to put this value instead of the LSB of the pixel element
                                    // just add it to it
                                    // recall that the LSB of the pixel element had been cleared
                                    // before this operation
                                    R += charValue % 2;

                                    // removes the added rightmost bit of the character
                                    // such that next time we can reach the next one
                                    charValue /= 2;
                                }
                            } break;
                        case 1:
                            {
                                if (state == State.Hiding)
                                {
                                    G += charValue % 2;

                                    charValue /= 2;
                                }
                            } break;
                        case 2:
                            {
                                if (state == State.Hiding)
                                {
                                    B += charValue % 2;

                                    charValue /= 2;
                                }

                                bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
                            } break;
                    }

                    pixelElementIndex++;

                    if (state == State.Filling_With_Zeros)
                    {
                        // increment the value of zeros until it is 8
                        zeros++;
                    }
                }
            }
        }

        return bmp;
    }

    public static string extractText(Bitmap bmp)
    {
        int colorUnitIndex = 0;
        int charValue = 0;

        // holds the text that will be extracted from the image
        string extractedText = String.Empty;

        // pass through the rows
        for (int i = 0; i < bmp.Height; i++)
        {
            // pass through each row
            for (int j = 0; j < bmp.Width; j++)
            {
                Color pixel = bmp.GetPixel(j, i);

                // for each pixel, pass through its elements (RGB)
                for (int n = 0; n < 3; n++)
                {
                    switch (colorUnitIndex % 3)
                    {
                        case 0:
                            {
                                // get the LSB from the pixel element (will be pixel.R % 2)
                                // then add one bit to the right of the current character
                                // this can be done by (charValue = charValue * 2)
                                // replace the added bit (which value is by default 0) with
                                // the LSB of the pixel element, simply by addition
                                charValue = charValue * 2 + pixel.R % 2;
                            } break;
                        case 1:
                            {
                                charValue = charValue * 2 + pixel.G % 2;
                            } break;
                        case 2:
                            {
                                charValue = charValue * 2 + pixel.B % 2;
                            } break;
                    }

                    colorUnitIndex++;

                    // if 8 bits has been added,
                    // then add the current character to the result text
                    if (colorUnitIndex % 8 == 0)
                    {
                        // reverse? of course, since each time the process occurs
                        // on the right (for simplicity)
                        charValue = reverseBits(charValue);

                        // can only be 0 if it is the stop character (the 8 zeros)
                        if (charValue == 0)
                        {
                            return extractedText;
                        }

                        // convert the character value from int to char
                        char c = (char)charValue;

                        // add the current character to the result text
                        extractedText += c.ToString();
                    }
                }
            }
        }

        return extractedText;
    }

    public static int reverseBits(int n)
    {
        int result = 0;

        for (int i = 0; i < 8; i++)
        {
            result = result * 2 + n % 2;

            n /= 2;
        }

        return result;
    }
}

License

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

Share

About the Author

Hamzeh soboh
Engineer seeking a job
Jordan Jordan
Mobile Applications and .Net Developer (seeking a job)
hamztaxerror@yahoo.com

You may also be interested in...

Comments and Discussions

 
QuestionHow to use mentioned extracting code in main class Pin
Artak Mehrabyan3-Mar-16 9:25
memberArtak Mehrabyan3-Mar-16 9:25 
QuestionMy Vote of 5! Pin
Ashraf Azmi1-Dec-15 16:16
memberAshraf Azmi1-Dec-15 16:16 
QuestionStore the image in the database and later extract it. Pin
Member 1116586328-Nov-15 14:32
memberMember 1116586328-Nov-15 14:32 
QuestionValues of R, G and B after clearing LSB from each pixel. Pin
Member 1206852518-Oct-15 17:06
memberMember 1206852518-Oct-15 17:06 
QuestionHow can i hide data in only Red bytes from image? Pin
Member 1169539124-May-15 12:08
memberMember 1169539124-May-15 12:08 
QuestionBack end Pin
Member 1151394319-Mar-15 21:00
memberMember 1151394319-Mar-15 21:00 
GeneralMy vote of 4 Pin
Nidhin David10-Mar-15 8:07
memberNidhin David10-Mar-15 8:07 
QuestionWhy need this statement ? Pin
Member 1059159428-Dec-14 6:21
memberMember 1059159428-Dec-14 6:21 
Questionimplement the project Pin
Member 1126675129-Nov-14 5:53
memberMember 1126675129-Nov-14 5:53 
AnswerRe: implement the project Pin
Hamzeh soboh29-Nov-14 6:45
professionalHamzeh soboh29-Nov-14 6:45 
GeneralRe: implement the project Pin
Member 1126675129-Nov-14 7:42
memberMember 1126675129-Nov-14 7:42 
GeneralRe: implement the project Pin
Hamzeh soboh30-Nov-14 6:01
professionalHamzeh soboh30-Nov-14 6:01 
GeneralRe: implement the project Pin
Member 1126675130-Nov-14 7:13
memberMember 1126675130-Nov-14 7:13 
QuestionMy vote of 5 Pin
GuyThiebaut26-Oct-14 0:41
memberGuyThiebaut26-Oct-14 0:41 
AnswerRe: My vote of 5 Pin
Hamzeh soboh26-Oct-14 1:15
professionalHamzeh soboh26-Oct-14 1:15 
Questionalgorithm Pin
Member 1109015017-Sep-14 11:36
memberMember 1109015017-Sep-14 11:36 
GeneralMy vote of 5 Pin
Member 109536072-Sep-14 21:58
memberMember 109536072-Sep-14 21:58 
QuestionAdding support to those images with indexed pixel formats Pin
Johnny Worker11-Aug-14 0:21
memberJohnny Worker11-Aug-14 0:21 
Questionreally liked your project but want to add jpg modification Pin
Asaf Even Tzur28-Jun-14 7:54
memberAsaf Even Tzur28-Jun-14 7:54 
AnswerRe: really liked your project but want to add jpg modification Pin
Hamzeh soboh28-Jun-14 11:53
professionalHamzeh soboh28-Jun-14 11:53 
QuestionMake it faster Pin
Jozsef Szalai3-May-14 6:46
memberJozsef Szalai3-May-14 6:46 
GeneralMy vote of 3 Pin
EngDRJ28-Apr-14 12:31
memberEngDRJ28-Apr-14 12:31 
GeneralRe: My vote of 3 Pin
Hamzeh soboh28-Apr-14 21:00
professionalHamzeh soboh28-Apr-14 21:00 
GeneralRe: My vote of 3 Pin
EngDRJ30-Apr-14 0:57
memberEngDRJ30-Apr-14 0:57 
GeneralMy vote of five Pin
WalterThai26-Apr-14 6:32
professionalWalterThai26-Apr-14 6:32 
GeneralMy vote of 5 Pin
LostTheMarbles23-Apr-14 23:27
professionalLostTheMarbles23-Apr-14 23:27 
QuestionSimple and very good explanation Pin
AnandChavali23-Apr-14 20:35
memberAnandChavali23-Apr-14 20:35 
GeneralNice Pin
SOHAM_GANDHI23-Apr-14 4:48
memberSOHAM_GANDHI23-Apr-14 4:48 
GeneralMy vote of 1 Pin
deepankarbhatnagar23-Apr-14 0:24
professionaldeepankarbhatnagar23-Apr-14 0:24 
GeneralRe: My vote of 1 Pin
phil.o23-Apr-14 3:22
professionalphil.o23-Apr-14 3:22 
GeneralRe: My vote of 1 Pin
Hamzeh soboh23-Apr-14 5:52
professionalHamzeh soboh23-Apr-14 5:52 
Questionsteps & technique Pin
Akanksha02n14-Apr-14 20:10
memberAkanksha02n14-Apr-14 20:10 
AnswerRe: steps & technique Pin
Hamzeh soboh18-Apr-14 21:29
professionalHamzeh soboh18-Apr-14 21:29 
GeneralRe: steps & technique Pin
Akanksha02919-Apr-14 4:51
professionalAkanksha02919-Apr-14 4:51 
GeneralRe: steps & technique Pin
Hamzeh soboh19-Apr-14 11:25
professionalHamzeh soboh19-Apr-14 11:25 
Questionneed explanation for code Pin
Priyanka Joshi9-Apr-14 1:05
memberPriyanka Joshi9-Apr-14 1:05 
AnswerRe: need explanation for code Pin
Hamzeh soboh18-Apr-14 21:29
professionalHamzeh soboh18-Apr-14 21:29 
GeneralRe: need explanation for code Pin
Priyanka Joshi18-Apr-14 22:46
memberPriyanka Joshi18-Apr-14 22:46 
GeneralRe: need explanation for code Pin
Hamzeh soboh19-Apr-14 2:42
professionalHamzeh soboh19-Apr-14 2:42 
QuestionCode Explanation? Pin
Nelson Perez23-Feb-14 18:38
memberNelson Perez23-Feb-14 18:38 
AnswerRe: Code Explanation? Pin
Hamzeh soboh18-Apr-14 21:29
professionalHamzeh soboh18-Apr-14 21:29 
Questiontechnique Pin
Member 104471925-Dec-13 5:01
memberMember 104471925-Dec-13 5:01 
AnswerRe: technique Pin
Hamzeh soboh14-Dec-13 0:20
professionalHamzeh soboh14-Dec-13 0:20 
SuggestionImage missing Pin
Rohan Leuva2-Dec-13 1:03
professionalRohan Leuva2-Dec-13 1:03 
GeneralRe: Image missing Pin
Hamzeh soboh2-Dec-13 2:14
professionalHamzeh soboh2-Dec-13 2:14 
QuestionAlgorithm Pin
rasel8927-Nov-13 18:15
memberrasel8927-Nov-13 18:15 
AnswerRe: Algorithm Pin
Hamzeh soboh28-Nov-13 21:20
professionalHamzeh soboh28-Nov-13 21:20 
GeneralRe: Algorithm Pin
rasel8929-Nov-13 20:44
memberrasel8929-Nov-13 20:44 
GeneralRe: Algorithm Pin
Hamzeh soboh30-Nov-13 0:36
professionalHamzeh soboh30-Nov-13 0:36 
QuestionHidden Data Pin
amit121212424-Oct-13 0:59
memberamit121212424-Oct-13 0:59 

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
Web01 | 2.8.160426.1 | Last Updated 23 Apr 2014
Article Copyright 2013 by Hamzeh soboh
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid