Digital Disappearing Ink: Steganography in C# - ' The Sample Program' (
Page 3 of 4 )
The SteganographyDemo Program
I wrote a program to demonstrate the technique I've presented here. It's called SteganographyDemo and the project can be downloaded here.
There are three parts to the program. The main part loads and image and strips data bits from it according to what is selected in a combo box. You can strip anywhere from 0 to 7 bits. The more bits you strip, the more degradation the images experience. The following Figure shows the program running.
ADVERTISEMENT
The code to strip the bits is fairly straight forward. It gets a mask value from a static byte array with which it will mask the data. For instance, to mask one bit the mask value is 0xfe, two bits the mask value is 0xfc, three bits the mask value is 0xf8, and so forth. A byte array with the bitmap image data is obtained by using the BitmapHelper.BitmapDataFromBitmap method. The data (past the 54 byte header information) is masked so that the desired bits are stripped, or discarded. The byte array is then used to recreate the display Bitmap object by using the BitmapHelper.BitmapFromBitmapData method. The entire StripBits method can be seen below.
// This array of masks determines which bits are
// stripped.
static byte[] m_Masks = { 0xff, 0xfe, 0xfc, 0xf8,
0xf0, 0xe0, 0xc0, 0x80 };
// This method strips bits from the image data.
// It starts at the least significant bit
// and works its way up.
void StripBits()
{
// If we're set to 0, then just bail out.
if (m_nBits == 0)
{
return;
}
// Use this single byte local variable
// since it'll be easier to use than
// the byte array.
byte Mask = m_Masks[m_nBits];
// Get the byte data from the Bitmap object.
byte[] BitmapData =
BitmapHelper.BitmapDataFromBitmap(m_objBitmap);
// Calculate the number of pixels.
int nPixels = m_objBitmap.Width * m_objBitmap.Height;
// Loop through the pixel data. It'll be
// four bytes of data per pixel.
for (int i = 0; i < nPixels * 4; i++)
{
// Mask the bottom bits off.
BitmapData[54+i] &= Mask;
}
// Create a new Bitmap object from the
// masked data.
m_objBitmap =
BitmapHelper.BitmapFromBitmapData(BitmapData);
}
Please note that this article is only showing the highlights of the source code that has to do with the Steganography process. This is because of its length. The code, however, is thoroughly commented. If you download and take a look at the demo program, you will easily understand how the program fits together.
The second part of the code is the code behind a Windows Form that adds a message to the currently displayed image, and saves it to disk. You can see this in operation in the following Figure.
There are three methods of interest contained in the AddMessage class (which is behind the dialog box shown previously) with regards to the Steganography process. They are the StoreBit, StoreBits, and btnSave_Click methods. Let's take a look at them one at a time, staring with the StoreBit method.
There are two variables are used to store bits into the bitmap data. The m_nCurrentBit variable will normally contain a value from 0 to the number of stripped bits. For instance, if there are 3 stripped bits, the m_nCurrentBit variable will have a value from 0-2. The m_nCurrentIndex variable contains the index into the bitmap data array. It won't increment every time that a bit is stored unless the number of bits stripped is 1. There is a method named InitBitStoring that initializes the two member variables that are used to store the bits.
The StoreBit method stores a single bit into the bitmap data array. It takes two arguments: the bit value (true or false) and the number of bits we're going to store into. Note that the number of bits is always 1 for the initial value that's stored in the data, regardless of the number of bits that have been stripped.
// These two variables are used
// to store bits into the bitmap data.
// The m_nCurrentBit variable will normally
// contain a value from 0 to the nubmer of
// stripped bits. For instance, if there are
// 3 stripped bits, the m_nCurrentBit variable
// will have a value from 0-2. The m_nCurrentIndex
// variable contains the index into the bitmap
// data array. It won't increment every time
// that a bit is stored unless the number of
// bits stripped is 1.
int m_nCurrentBit;
int m_nCurrentIndex;
// This method initializes the two member
// variables that are used to store the bits.
void InitBitStoring()
{
m_nCurrentBit = m_nCurrentIndex = 0;
}
// This is a precalculated table of bit values
// for each bit position. The m_nCurrentBit
// variable indexes this array.
static byte[] DataBitValue = { 1, 2, 4, 8, 16, 32, 64, 128 };
// This method stores a single bit into the
// bitmap data array. It takes two
// arguments: the bit value (true or false)
// and the number of bits we're going to
// store into. Note that the number of bits
// is always 1 for the initial value that's
// stored in the data, regardless of the
// number of bits that have been stripped.
void StoreBit(bool Bit, int nBits)
{
// Assume a bit value of 0.
byte Data = 0;
// If Bit==True, then the data value
// must be 1.
if (Bit)
{
// Get the data value based on the
// precalculated table indexed
// by the m_nCurrentBit variable.
Data = DataBitValue[m_nCurrentBit];
}
// Or this data bit with the currently
// indexed data byte.
m_BitmapData[54+m_nCurrentIndex] |= Data;
// Increment the bit counter.
m_nCurrentBit++;
// Check to see if the bit counter
// needs to be reset.
if (m_nCurrentBit >= nBits)
{
// Reset the bit counter.
m_nCurrentBit = 0;
// Increment the index into
// the bitmap data array.
m_nCurrentIndex++;
}
}