Steganography - Hiding data in plain sight
Phoenix Roberts - 07/Apr/2012
Phoenix Roberts - 07/Apr/2012
[SHOWTOGROUPS=4,20]
Download:
.../KB/security/355482/Stegan.zip
Introduction
This article aims to demonstrate the basic concept of Steganography.
It is the practice of placing hidden information within a message. There are a myriad number of ways to hide data within a file.
Many file types support header fields that provide space for self describing data. Some well known, some obscure.
These can be leveraged to transfer additional/hidden content. Other techniques involve a subtle altering of the data in the file such that on the surface the new file appears identical to the original. In all cases, access to the original file will indicate it has been modified.
Decoding of the hidden content in such cases is a function of the encryption algorithm used (if any) to embed the concealed content in the cover file. In this article and its accompanying sample application, I'll be using a basic bitmap file and modifying the least significant bits of the pixel bytes to store the message.
The task here is to introduce the concept and detail one possible method as an example. Once an understanding of the basic concept is in place, the type of encryption applied, the subtlety with which the cover file is modified, and the type of cover file used can all be modified to taste.
Structure of a bitmap file
First, a little background material on the type of cover file we'll be using. As an overview, a bitmap is essentially a mapping of the values used to represent the pixels within a given area on a screen. One of the older image formats in use today it consists of three to four parts or sections.
Header, Information Header, Palette (optional), and Image Data.
The header contains a "magic" identifier (letting us know the file is a bitmap) as well as the file size and the offset in the file where the actual pixel mapping begins.
The information section contains such things as width and height of the image, bits per pixel, number of colors, and what (if any) compression type has been used.
If "indexed color" has been used, a section containing a table of colors will then be next. Bitmaps using indexed colors do not store the color specification within each pixel but rather an index into the optional section that contains the colors table.
We will not be considering indexed color bitmaps within the bounds of our simple Steganography application. The final section contains the actual image data i.e, the map of pixels that make up our bitmap image.
Thankfully, in our simple Steganography application, we only need to focus on the image data section and will ignore the other sections. Our entry point into the bitmap file will be the .Net "Bitmap" class and we'll be using it's GetPixel and SetPixel methods to store and retrieve our hidden data. GetPixel and SetPixel return and are passed a Color object, respectively. By "borrowing bits" from the Color object we will interleave the data we wish to transfer.
Application overview
The sample application this article is based on is a commandline utility that allows for the encoding and decoding of a file into a bitmap in such a manner as to conceal the existence of the embedded file from the casual observer. At a high level the objects that will be doing the work for the application are as follows.
SteganographyEngine - this class acts as a wrapper for the Steganography functionality provided. SteganographyEngine manages the file IO for the the various Steganography objects in the application as well as instantiation and invocation of those objects based on parameters passed to it from the application object.
SteganographyEngine has a single member variable of type ISteganographyFactory. ISteganographyFactory in turn works with two other Steganography specific objects.
IMetaData and IBitmapCodec. Together these three form the basis for all Steganography operations performed by the application. All three are interfaces in order to provide a measure of extensibility to the application. The concrete classes SteganographyFactory, MetaData, and BitmapCodec, respectively provide implementations for each of these interfaces within the application. BitmapCodec is where the rubber hits the road so to speak.
This is where the individual bytes are encoded and decoded into the cover files bytes. MetaData is a section of data we encode/decode along with the content we wish to embed. As the name implies it is data about the data we've embedded. This is done to facilitate extraction.
SteganographyFactory ties the codec and meta-data classes together performing the needed function calls in order to provide a cohesive Steganography interface to the engine class.
Encoding the data
Each pixel returned by the GetPixel method of the Bitmap class is represented by a Color object. Color objects contain Red, Green, Blue properties and can be represented by one byte each. It is within the last two to three bits of each of these bytes that we will store our data. Thus, in this example each pixel color will provide for us three bytes with which we can encode our data.
Each byte ranges from 0 to 255. The higher the number the greater the amount of that color is applied to it's respective pixel. In general, these last three bytes can be modified without having an overly large impact on the bitmap picture. Certainly not enough to notice if one is not familiar with the original.
So, each byte to embed will be split between each of the three color bytes. As stated in the application overview the code to encode/decode a byte resides in the class ByteCodec. Below I have detailed the code to encode i.e., splitting and allocating a byte between the red, green, and blue bytes of a Color object.
First the function definition. An array of byte values is passed in. These are the red, green, and blue values of our target pixel's color. Additionally, a single byte value is passed in. This is the value we wish to encode.
So "bytValue" is the value we wish to encode within the red, green, and blue color bytes. We have a total of eight bits to encode over a combination of three bytes. Bits are encoded into their target bytes starting at the least significant bit of each byte.
As a concrete example we will encode a value of 105 into a pixel with the colors red=34, green=73, and blue=80. The program by default allocates the bits of the byte to encode as the 3 most significant bits to red, next 3 bits to green, and the final 2 bits to blue. We will refer to this as a mask of 3:3:2. Our bit patterns as EncodeByte begins to execute looks as follows.
From the bits of the value to encode (bytValue) we must transfer bits into our pixel bytes as follows
As stated, all bit transfers are done starting from the least significant bit of each targeted color byte
For the red byte it looks like this
The three least significant bits have been replaced
For the green byte we have:
The three least significant bits have been replaced
And finally for the blue byte:
The two least significant bits have been replaced.
So applying a bit dispersal mask of 3:3:2 to our byte to encode modifies the original color of the pixel just slightly. From a pixel color of red=34, green=73, and blue=80 to a color of red=35, green=74, and blue=81 We have encoded the byte and the picture to the causal observer giving no indication that it contains a second level of information.
As far as the source code for this function, we have what follows below. First, we create bit array objects that corresponds to our byte to encode (bytValue) and the bit dispersal mask we have chosen to use, 3:3:2. This is the default value for the mask.
Other values to use for the mask can be passed in from the commandline at run time. The member variables of the ByteCodec class m_bytRedMask, m_bytGreenMask, and m_bytBlueMask correspond to the three values in the bit dispersal mask.
Next we need to separate the bits of the value to encode into their respective red, green, and blue components based on the bit dispersal masks. We use a logical "And" operation on our bit arrays to do this.
At this point "red" bits to encode reside in the bit array SourceA. Similarly "green" bits are in SourceB and "blue" bits are in SourceC.
The bit patterns look like this.
If you look diagonally from the top left to the bottom right of the three patterns you can still see the original bit pattern of the byte to encode 0110 1001
The next step is to shift these bits so that the mask we are using will be applied to the least significant bits of each color byte.
Our mask specifies 3:3:2 so in SourceA we are using the first three bits. Those bits must now be shifted into the three least significant bit positions of the red byte. To do this, we employ the shift operator. The code to shift the "red bits" into the red byte is below.
The green bits however are in the middle of the byte to encode.
As such they don't have to travel as far to reach the position of their corresponding least significant bits. Again our mask is 3:3:2 but since we are already 3 bits into the byte from our activities related to the "red" bits, we need to adjust our shift value accordingly. Instead of shifting five bits we shift three bits less. Our green bits are thus shifted only two bits.
The value of the member variable "m_bytGreenShift" is therefore two. The bit pattern of SourceB then becomes 0000 0010
[/SHOWTOGROUPS]
Download:
.../KB/security/355482/Stegan.zip
Introduction
This article aims to demonstrate the basic concept of Steganography.
It is the practice of placing hidden information within a message. There are a myriad number of ways to hide data within a file.
Many file types support header fields that provide space for self describing data. Some well known, some obscure.
These can be leveraged to transfer additional/hidden content. Other techniques involve a subtle altering of the data in the file such that on the surface the new file appears identical to the original. In all cases, access to the original file will indicate it has been modified.
Decoding of the hidden content in such cases is a function of the encryption algorithm used (if any) to embed the concealed content in the cover file. In this article and its accompanying sample application, I'll be using a basic bitmap file and modifying the least significant bits of the pixel bytes to store the message.
The task here is to introduce the concept and detail one possible method as an example. Once an understanding of the basic concept is in place, the type of encryption applied, the subtlety with which the cover file is modified, and the type of cover file used can all be modified to taste.
Structure of a bitmap file
First, a little background material on the type of cover file we'll be using. As an overview, a bitmap is essentially a mapping of the values used to represent the pixels within a given area on a screen. One of the older image formats in use today it consists of three to four parts or sections.
Header, Information Header, Palette (optional), and Image Data.
The header contains a "magic" identifier (letting us know the file is a bitmap) as well as the file size and the offset in the file where the actual pixel mapping begins.
The information section contains such things as width and height of the image, bits per pixel, number of colors, and what (if any) compression type has been used.
If "indexed color" has been used, a section containing a table of colors will then be next. Bitmaps using indexed colors do not store the color specification within each pixel but rather an index into the optional section that contains the colors table.
We will not be considering indexed color bitmaps within the bounds of our simple Steganography application. The final section contains the actual image data i.e, the map of pixels that make up our bitmap image.
Thankfully, in our simple Steganography application, we only need to focus on the image data section and will ignore the other sections. Our entry point into the bitmap file will be the .Net "Bitmap" class and we'll be using it's GetPixel and SetPixel methods to store and retrieve our hidden data. GetPixel and SetPixel return and are passed a Color object, respectively. By "borrowing bits" from the Color object we will interleave the data we wish to transfer.
Application overview
The sample application this article is based on is a commandline utility that allows for the encoding and decoding of a file into a bitmap in such a manner as to conceal the existence of the embedded file from the casual observer. At a high level the objects that will be doing the work for the application are as follows.
SteganographyEngine - this class acts as a wrapper for the Steganography functionality provided. SteganographyEngine manages the file IO for the the various Steganography objects in the application as well as instantiation and invocation of those objects based on parameters passed to it from the application object.
SteganographyEngine has a single member variable of type ISteganographyFactory. ISteganographyFactory in turn works with two other Steganography specific objects.
IMetaData and IBitmapCodec. Together these three form the basis for all Steganography operations performed by the application. All three are interfaces in order to provide a measure of extensibility to the application. The concrete classes SteganographyFactory, MetaData, and BitmapCodec, respectively provide implementations for each of these interfaces within the application. BitmapCodec is where the rubber hits the road so to speak.
This is where the individual bytes are encoded and decoded into the cover files bytes. MetaData is a section of data we encode/decode along with the content we wish to embed. As the name implies it is data about the data we've embedded. This is done to facilitate extraction.
SteganographyFactory ties the codec and meta-data classes together performing the needed function calls in order to provide a cohesive Steganography interface to the engine class.
Encoding the data
Each pixel returned by the GetPixel method of the Bitmap class is represented by a Color object. Color objects contain Red, Green, Blue properties and can be represented by one byte each. It is within the last two to three bits of each of these bytes that we will store our data. Thus, in this example each pixel color will provide for us three bytes with which we can encode our data.
Each byte ranges from 0 to 255. The higher the number the greater the amount of that color is applied to it's respective pixel. In general, these last three bytes can be modified without having an overly large impact on the bitmap picture. Certainly not enough to notice if one is not familiar with the original.
So, each byte to embed will be split between each of the three color bytes. As stated in the application overview the code to encode/decode a byte resides in the class ByteCodec. Below I have detailed the code to encode i.e., splitting and allocating a byte between the red, green, and blue bytes of a Color object.
First the function definition. An array of byte values is passed in. These are the red, green, and blue values of our target pixel's color. Additionally, a single byte value is passed in. This is the value we wish to encode.
Код:
void EncodeByte(byte[] ByteArray, byte bytValue)
So "bytValue" is the value we wish to encode within the red, green, and blue color bytes. We have a total of eight bits to encode over a combination of three bytes. Bits are encoded into their target bytes starting at the least significant bit of each byte.
As a concrete example we will encode a value of 105 into a pixel with the colors red=34, green=73, and blue=80. The program by default allocates the bits of the byte to encode as the 3 most significant bits to red, next 3 bits to green, and the final 2 bits to blue. We will refer to this as a mask of 3:3:2. Our bit patterns as EncodeByte begins to execute looks as follows.
Код:
0110 1001 The value to encode (105)
0010 0010 The red byte of the pixel (34)
0100 1001 The green byte of the pixel (73)
0101 0000 The blue byte of the pixel (80)
From the bits of the value to encode (bytValue) we must transfer bits into our pixel bytes as follows
Код:
011 To the red byte
010 To the green byte
01 To the blue byte
As stated, all bit transfers are done starting from the least significant bit of each targeted color byte
For the red byte it looks like this
Код:
0010 0010 Red Byte
0000 0011 Bits to encode
========
0010 0011 New red (35).
The three least significant bits have been replaced
For the green byte we have:
Код:
0100 1001 Green Byte
0000 0010 Bits to encode
========
0100 1010 New green (74).
The three least significant bits have been replaced
And finally for the blue byte:
Код:
0101 0000 Blue Byte
0000 0001 Bits to encode
========
0101 0001 New blue (81).
The two least significant bits have been replaced.
So applying a bit dispersal mask of 3:3:2 to our byte to encode modifies the original color of the pixel just slightly. From a pixel color of red=34, green=73, and blue=80 to a color of red=35, green=74, and blue=81 We have encoded the byte and the picture to the causal observer giving no indication that it contains a second level of information.
As far as the source code for this function, we have what follows below. First, we create bit array objects that corresponds to our byte to encode (bytValue) and the bit dispersal mask we have chosen to use, 3:3:2. This is the default value for the mask.
Other values to use for the mask can be passed in from the commandline at run time. The member variables of the ByteCodec class m_bytRedMask, m_bytGreenMask, and m_bytBlueMask correspond to the three values in the bit dispersal mask.
Код:
BitArray Source = new BitArray(new byte[] { bytValue });
BitArray SourceA = new BitArray(new byte[] { m_bytRedMask });
BitArray SourceB = new BitArray(new byte[] { m_bytGreenMask });
BitArray SourceC = new BitArray(new byte[] { m_bytBlueMask });
Next we need to separate the bits of the value to encode into their respective red, green, and blue components based on the bit dispersal masks. We use a logical "And" operation on our bit arrays to do this.
Код:
SourceA.And(Source);
SourceB.And(Source);
SourceC.And(Source);
At this point "red" bits to encode reside in the bit array SourceA. Similarly "green" bits are in SourceB and "blue" bits are in SourceC.
The bit patterns look like this.
Код:
0110 0000 SourceA
0000 1000 SourceB
0000 0001 SourceC
If you look diagonally from the top left to the bottom right of the three patterns you can still see the original bit pattern of the byte to encode 0110 1001
The next step is to shift these bits so that the mask we are using will be applied to the least significant bits of each color byte.
Our mask specifies 3:3:2 so in SourceA we are using the first three bits. Those bits must now be shifted into the three least significant bit positions of the red byte. To do this, we employ the shift operator. The code to shift the "red bits" into the red byte is below.
Код:
byte[] TempByteArray = new byte[1];
SourceA.CopyTo(TempByteArray, 0);
TempByteArray[0] = (byte)(TempByteArray[0] >> m_bytRedShift);
SourceA = new BitArray(new byte[] { TempByteArray[0] });
/[code]
The member variable "m_bytRedShift" of the ByteCodec class is the number of bits to shift for red bits. This value is calculated when the class is instantiated based on the bit dispersal mask supplied. In our case 3:3:2.
So we are taking the first three bits of our value to encode and that means that we will need to shift them five places to get them into the position of the corresponding least significant bits.
The value of "m_bytRedShift" is therefore five. After shifting our bits in the bit array "SourceA" five places we will have the bit pattern 0000 0011. A similar operation must also be done for the "green bits" (below)
[code]
TempByteArray = new byte[1];
SourceB.CopyTo(TempByteArray, 0);
TempByteArray[0] = (byte)(TempByteArray[0] >> m_bytGreenShift);
SourceB = new BitArray(new byte[] { TempByteArray[0] });
The green bits however are in the middle of the byte to encode.
As such they don't have to travel as far to reach the position of their corresponding least significant bits. Again our mask is 3:3:2 but since we are already 3 bits into the byte from our activities related to the "red" bits, we need to adjust our shift value accordingly. Instead of shifting five bits we shift three bits less. Our green bits are thus shifted only two bits.
The value of the member variable "m_bytGreenShift" is therefore two. The bit pattern of SourceB then becomes 0000 0010
[/SHOWTOGROUPS]
Последнее редактирование: