Use steganography to hide Unicode messages in an image in C#


[steganography]

The post Use steganography to hide messages in an image in C# shows one technique for hiding information inside an image. That method stores bits of information in the least significant bits of selected pixels scattered around the image.

That example scatters the pixels around largely because I was worried that putting them all in one place would make a discernible pattern in the resulting image. Fortunately that’s not the case. This example works in a similar way but it uses the pixels in order starting in the image’s upper left corner. That makes it easier to figure out where the pixels are and generally makes the structure of the program easier to understand.

At a high level, the program breaks the data into successively smaller pieces and places them in the image. Different methods encode and decode a string, array of bytes, individual byte, and bits.

The following EncodeBits method works at the lowest level to encode bits.

// Encode four bits. Values pos1 through pos4 give the
// positions from the left of the bits in b to encode.
private void EncodeBits(ref int x, ref int y, Bitmap bm,
    byte b, int pos1, int pos2, int pos3, int pos4)
{
    // Get the pixel's color.
    Color color = bm.GetPixel(x, y);

    // A mask to clear the rightmost bit.
    const byte only_1 = 0x01;

    // A mask to clear all bits except the rightmost bit.
    const byte clear_1 = only_1 ^ 0xFF;

    // Add the bits to the color components.
    byte alpha, red, green, blue;
    byte bit1 = (byte)((b >> pos1) & only_1);
    alpha = (byte)((color.A & clear_1) | bit1);

    byte bit2 = (byte)((b >> pos2) & only_1);
    red = (byte)((color.R & clear_1) | bit2);

    byte bit3 = (byte)((b >> pos3) & only_1);
    green = (byte)((color.G & clear_1) | bit3);

    byte bit4 = (byte)((b >> pos4) & only_1);
    blue = (byte)((color.B & clear_1) | bit4);

    // Update the pixel.
    bm.SetPixel(x, y, Color.FromArgb(alpha, red, green, blue));
    UsedBitmap.SetPixel(x, y, Color.Red);

    // Move to the next pixel.
    NextRowCol(ref x, ref y, bm);
}

This method takes as parameters the X and Y positions of the pixel that should contain the values, the bitmap that holds the pixel, a byte value to encode, and the positions of the four bits in the byte that should be encoded.

The method first gets the color of the pixel that will hold the data. It then makes two byte bit masks, one that has only the least significant bit set and one that has the other bits set.

To encode the first bit value, the code shifts the byte b to the right until its bit lies in the least significant position. It then uses & to combine the result with the only_1 mask to clear all but the first bit. As a result, the value bit1 is either 00000000 or 00000001.

Next the code gets the pixel’s alpha (opacity) component and combines it with the clear_1 mask to clear out the least significant bit. It uses | to combine the result with the bit1 value. The result is the same as the original alpha value with the least significant bit set to 0 or 1 so it matches the data bit value bit1.

The code repeats the same steps to encode the other bits in the pixel’s red, green, and blue components. It then uses the bitmap’s SetPixel method to save the result in the bitmap’s pixel. (The code also sets the corresponding pixel in the UsedBitmap image to red. The program displays that image on the right so you can see which pixels were used to store data.)

The method finishes by calling the following NextRowCol method to move the X and Y values to the image’s next pixel.

// Increment row and col to the next pixel in the image.
private void NextRowCol(ref int x, ref int y, Bitmap bm)
{
    x++;
    if (x >= bm.Width)
    {
        x = 0;
        y++;
    }
}

This method simply increments x. If that value is then too wide to fit on the bitmap, it resets x to 0 and increments y.

Now that you know how to encode four bits, you can learn how the program uses that method to encode high-level pieces of information. The following EncodeByte method uses EncodeBits to encode a byte.

// Encode a single byte starting at pixel (row, col).
private void EncodeByte(ref int x, ref int y, Bitmap bm, byte b)
{
    // Encode the byte's bits 3 at a time.
    EncodeBits(ref x, ref y, bm, b, 0, 1, 2, 3);
    EncodeBits(ref x, ref y, bm, b, 4, 5, 6, 7);
}

This method calls EncodeBits twice to encode the first four and the last four bits of a byte in the bitmap.

Working higher up the complexity chain, the following EncodeBytes method calls EncodeByte to encode an array of bytes.

// Encode the bytes starting at pixel (row, col).
private void EncodeBytes(ref int x, ref int y,
    Bitmap bm, byte[] bytes)
{
    // Encode the bytes.
    for (int i = 0; i < bytes.Length; i++)
        EncodeByte(ref x, ref y, bm, bytes[i]);
}

This method simply loops through an array of bytes and uses EncodeByte to encode each byte.

The following method uses the EncodeBytes method to encode a message’s bytes for this example.

// Encode an array of bytes in the bitmap's bytes.
private Bitmap EncodeMesssageBytes(Bitmap bm, byte[] message_bytes)
{
    // Make sure it will fit.
    int message_length = message_bytes.Length;
    int space_available = (bm.Width * bm.Height) / 2;
    if (message_length + 4 > space_available)
        throw new InvalidDataException(
            "Message length " + message_bytes.Length +
            " is too long. This image can hold only " +
            space_available + " bytes.");

    int total_length = message_length + 4;
    lblResult.Text = "Encoded " +
        total_length.ToString("N0") + " bytes";

    // Make the result Bitmap.
    Bitmap result = bm.Clone() as Bitmap;
    UsedBitmap = bm.Clone() as Bitmap;

    // Records the location of the next pixel.
    int x = 0, y = 0;

    // Encode the message's length.
    byte[] length_bytes = BitConverter.GetBytes(message_length);
    EncodeBytes(ref x, ref y, result, length_bytes);

    // Encode the message.
    EncodeBytes(ref x, ref y, result, message_bytes);

    // Return the result.
    return result;
}

This method first determines the number of bytes the message will need for encoding, adding 4 exatr bits to hold the message’s length. If the bitmap isn’t big enough, the method throws an exception. Otherwise the method displays the number of bytes it will encode in a label.

Next the method clones the bitmap twice, once to hold the encoded data and once to show which pixels have been used to store data.

When you want to decode the data, you’ll need to know how many bytes were encoded, so the method next stores the message’s length in the bitmap. It uses BitConverter.GetBytes to get an array of bytes representing the length of the message and then uses EncodeBytes to store the result in the bitmap.

The method then calls EncodeBytes to store the message bytes in the bitmap and returns the resulting image.

The last piece of the encoding process is the following EncodeMessage method.

// Encode the message in the bitmap's bytes.
private Bitmap EncodeMesssage(Bitmap bm, string message)
{
    // Get the message as an array of bytes.
    byte[] message_bytes =
        System.Text.Encoding.UTF8.GetBytes(message);

    // Encode the bytes.
    return EncodeMesssageBytes(bm, message_bytes);
}

This method uses System.Text.Encoding.UTF8.GetBytes to convert a message string into an array of bytes. It then calls EncodeMesssageBytes to store the bytes in the bitmap and returns the result.

The following code shows how the program uses this method when you click the Encode button.

// Encode the message.
private void btnEncode_Click(object sender, EventArgs e)
{
    picEncoded.Image = EncodeMesssage(
        picOriginal.Image as Bitmap, txtMessage.Text);
    txtMessage.Clear();
    btnDecode.Enabled = true;

    picUsed.Image = UsedBitmap;
}

This code simply calls EncodeMessage and displays the result. It also displays the UsedBitmap so you can see which pixels were used to store data.

To summarize, the program uses the following code sequence to encode a message.

    btnEncode_Click
        EncodeMesssage
            EncodeMesssageBytes
                EncodeBytes
                    EncodeByte
                        EncodeBits

The methods that decode a message simply undo the work done by the encoding methods.

In the picture at the top of the post I stored a huge message in the image but you can't notice any difference between the original image and the image that contains the encoded message. The image on the right shows that most of the image's pixels were used to store data.

There are a couple of things you need to keep in mind when using this method. First, this method uses only the pixels' least significant bits to store data. You could use more bits and store more data, although if you use too many bits the quality of the image will suffer.

Second, because this method uses the pixels' least significant bits, the message is lost if those bits change. In particular, if you save the image in a JPEG file or using some other lossy compression algorithm, those bits will change and you'll lose all of the data.

Third, I used the pixels' alpha, red, green, and blue components to store data. In most images every pixel has alpha value 255 so it will be easy to notice that the value was changed for some pixels.

Many programs also treat completely transparent pixels differently from non-transparent ones. For example, if one pixel has alpha = 0 and another has alpha = 1, they will both look transparent to the user. However, some programs won't send mouse events to the first pixel because it is completely transparent, but they will send mouse events to the second pixel because it is not. That means if an image (perhaps used to give the form or a control a shape) has lots of completely transparent pixels, this program's changing their alpha values to 1 will mess up the mouse behavior.

If either of the last two issues regarding alpha components is an issue, you can just store the data in the pixels' red, green, and blue components and leave the alpha values unchanged.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.

This entry was posted in algorithms, cryptography, graphics, image processing, mathematics and tagged , , , , , , , , , , , , , , , , . Bookmark the permalink.

7 Responses to Use steganography to hide Unicode messages in an image in C#

  1. Mike Griffiths says:

    Hi Rod,

    your code contains the following:
    bm.Clone() as Bitmap;

    Not sure I have come across the “as” keyword in C# before how does it work? 😉

    Regards,

    Mike

    • RodStephens says:

      The “as” keyword converts an object into another type if the types are compatible. The place I use it most is in event handlers where “sender” is the object that raised the event. For example, suppose several button’s share a Click event handler. Then you can do this:

      Button btn = sender as Button;

      Then you have the sender as a Button so you can look at its properties such as Text to see which button it is.

      The result is similar to a cast except the variable is set to null if the types are not compatible (for example, if a CheckBox somehow calls the button’s Click event handler) instead of crashing.

  2. Mike Griffiths says:

    OK – got it. Creates a null object on failure. Thought it was VB leaking in…

  3. Pingback: Use steganography to hide Unicode message bytes in an image in C# - C# HelperC# Helper

  4. myo min says:

    By using LSB algorithm, i would like to use rightmost 4 bits to hide message in an imge.
    Can you help me, please.

Leave a Reply

Your email address will not be published. Required fields are marked *