PNG Image Steganography with libpng
Grant Curell - 22/Apr/2013
Grant Curell - 22/Apr/2013
[SHOWTOGROUPS=4,20]
Downloads:
.../KB/security/581298/libpng_windows_source.zip
.../KB/security/581298/PNG_stego.zip
Introduction
Digital steganography is defined as hiding messages within digital media. Here we'll take a look at hiding information in images. In this article I introduce the basic concepts of digital steganography and a proof of concept using PNG images. If you're reading this, I assume a strong knowledge of C++ and a good grasp on binary arithmetic.
Fun Fact: This is a common technique used by clandestine organizations and terrorist groups such as Al Qaeda alike to covertly share information. Two parties agree on an image and a location beforehand. The party that wants to communicate information encodes hidden data into the image, uploads it, and the second party downloads it. To any onlookers, the image is completely normal, but underneath the hood the image contains any arbitrary hidden data. An example from Al Qaeda is Для просмотра ссылки Войдиили Зарегистрируйся.
Background
In the example I will show you, we will hide data in the least significant bits of a PNG image. There are many ways to hide data in an image and this is just one technique of the many. Here I will discuss at a rudimentary level some of the details of the PNG file specification to give the necessary background to understand the code.
There are three varieties of PNG image; we will examine truecolor images. The other two are greyscale and palette. We will not be using an alpha channel. For the curious, the alpha channel provides color transparency information.
PNG images are comprised of chunks. There are many different types of chunks each serving various roles. The three chunk types we will concern ourselves with are the IHDR, IDAT, and IEND chunks.
The full specification is Для просмотра ссылки Войдиили Зарегистрируйся. Below is a synopsis of the relevant parts for this article.
Chunk layout
Each chunk consists of three or four fields.
Length - A four-byte unsigned integer giving the number of bytes in the chunk's data field. The length counts only the data field, not itself, the chunk type, or the CRC. Zero is a valid length. Although encoders and decoders should treat the length as unsigned, its value shall not exceed 231-1 bytes.
Chunk Type - A sequence of four bytes defining the chunk type. Each byte of a chunk type is restricted to the decimal values 65 to 90 and 97 to 122. These correspond to the uppercase and lowercase ISO 646 letters (A-Z and a-z) respectively for convenience in description and examination of PNG datastreams. Encoders and decoders shall treat the chunk types as fixed binary values, not character strings. For example, it would not be correct to represent the chunk type Для просмотра ссылки Войдиили Зарегистрируйся by the equivalents of those letters in the UCS 2 character set.
Chunk Data - The data bytes appropriate to the chunk type, if any. This field can be of zero length.
CRC - A four-byte CRC (Cyclic Redundancy Code) calculated on the preceding bytes in the chunk, including the chunk type field and chunk data fields, but not including the length field. The CRC can be used to check for corruption of the data. The CRC is always present, even for chunks containing no data.
The IHDR Chunk
The four-byte chunk type field contains the decimal values 73 72 68 82.
The IHDR chunk shall be the first chunk in the PNG datastream. It contains:
Width and height give the image dimensions in pixels. They are PNG four-byte unsigned integers. Zero is an invalid value. Bit depth is a single-byte integer giving the number of bits per sample. Sample here means one color. Valid values are 1, 2, 4, 8, and 16, although not all values are allowed for all color types. Each pixel is made up of three bytes. Each of the three bytes represents a different color, in this case red, green, blue. These colors combined make up the one pixel you actually see.
The IDAT Chunk
The four-byte chunk type field contains the decimal values 73 68 65 84. The IDAT chunk contains the actual image data which is the output stream of the compression algorithm. If you're curious about the filtering and compression on PNG images check out Для просмотра ссылки Войдиили Зарегистрируйся and Для просмотра ссылки Войди или Зарегистрируйся. There may be multiple IDAT chunks; if so, they shall appear consecutively with no other intervening chunks. The compressed datastream is then the concatenation of the contents of the data fields of all the IDAT chunks.
The IEND Chunk
The four-byte chunk type field contains the decimal values 73 69 78 68. The IEND chunk marks the end of the PNG datastream. The chunk's data field is empty.
Steganography on PNG
As mentioned earlier each pixel is arranged in 3 bytes the first red, the second green, and the third blue. Various studies have yielded different results, but the all-reliable source wikipedia says that the human eye can distinguish approximately 10 million different colors. With three bytes total we can represent 2^(8+3) or 16,777,216 different colors. That means that there approximately 6,777,216 colors that we can represent, but the human eye won't notice the change as compared to one of the other 10 million colors.
Said another way:
Given the following truecolor pixel (this data would be located in an IDAT chunk)
The human eye would not be able to distinguish the difference if we changed the three parts to
You can probably see where this is going. This means that I can change the least significant bit of each byte and no one will be able to visually tell the difference between the original pixel and the changed pixel unless of course they have super human highdef eyes (which to my extensive medical knowledge don't exist
What we're going to do is leverage this to hide messages in the least significant bits in the following manner:
Encode our hidden message from least significant bit (LSB) to most significant bit (MSB) into the least significant bits of the image data. The image data then becomes:
Now we've got one byte of message data hidden in our image. Do it several thousand more times and we can hide quite a lot of data. Clearly this isn't the most efficient scheme since the PNG image we use we'll need to be 8 times the size of the hidden data. We're going for simple here rather than super slick. We could get a lot more efficiency by leveraging other hiding places, compression techniques, using more bits that wouldn't be detectable, alpha channels, text channels, and a million other things, but here we'll stick to the least significant bit.
Using the code
Programmer Note: Everything below was done in Visual Studio 2012
To work with the PNG images I made a PNG image class to implement the steganography portion and used the libpng library and the zlib library to actually do all the PNG manipulation and such. It's worth mentioning again that PNG image files are filtered and then compressed so that they take up less space. For this reason we can't edit the IDAT chunks of a raw PNG or you'll get some really funky results (I tried just for giggles and it's more than a little noticeable when you try to encode something). That being said, we need libpng to decompress and then unfilter the image for us. The compression algorithm used by libpng is deflate, which is implemented by zlib in case you were wondering why we need zlib.
You can view the libpng documentation Для просмотра ссылки Войдиили Зарегистрируйся.
The PNG_file class definition is as follows:
Don't worry too much about understanding what each bit means just yet. We'll go through it.
[/SHOWTOGROUPS]
Downloads:
.../KB/security/581298/libpng_windows_source.zip
.../KB/security/581298/PNG_stego.zip
Introduction
Digital steganography is defined as hiding messages within digital media. Here we'll take a look at hiding information in images. In this article I introduce the basic concepts of digital steganography and a proof of concept using PNG images. If you're reading this, I assume a strong knowledge of C++ and a good grasp on binary arithmetic.
Fun Fact: This is a common technique used by clandestine organizations and terrorist groups such as Al Qaeda alike to covertly share information. Two parties agree on an image and a location beforehand. The party that wants to communicate information encodes hidden data into the image, uploads it, and the second party downloads it. To any onlookers, the image is completely normal, but underneath the hood the image contains any arbitrary hidden data. An example from Al Qaeda is Для просмотра ссылки Войди
Background
In the example I will show you, we will hide data in the least significant bits of a PNG image. There are many ways to hide data in an image and this is just one technique of the many. Here I will discuss at a rudimentary level some of the details of the PNG file specification to give the necessary background to understand the code.
There are three varieties of PNG image; we will examine truecolor images. The other two are greyscale and palette. We will not be using an alpha channel. For the curious, the alpha channel provides color transparency information.
PNG images are comprised of chunks. There are many different types of chunks each serving various roles. The three chunk types we will concern ourselves with are the IHDR, IDAT, and IEND chunks.
The full specification is Для просмотра ссылки Войди
Chunk layout
Each chunk consists of three or four fields.
Length - A four-byte unsigned integer giving the number of bytes in the chunk's data field. The length counts only the data field, not itself, the chunk type, or the CRC. Zero is a valid length. Although encoders and decoders should treat the length as unsigned, its value shall not exceed 231-1 bytes.
Chunk Type - A sequence of four bytes defining the chunk type. Each byte of a chunk type is restricted to the decimal values 65 to 90 and 97 to 122. These correspond to the uppercase and lowercase ISO 646 letters (A-Z and a-z) respectively for convenience in description and examination of PNG datastreams. Encoders and decoders shall treat the chunk types as fixed binary values, not character strings. For example, it would not be correct to represent the chunk type Для просмотра ссылки Войди
Chunk Data - The data bytes appropriate to the chunk type, if any. This field can be of zero length.
CRC - A four-byte CRC (Cyclic Redundancy Code) calculated on the preceding bytes in the chunk, including the chunk type field and chunk data fields, but not including the length field. The CRC can be used to check for corruption of the data. The CRC is always present, even for chunks containing no data.
The IHDR Chunk
The four-byte chunk type field contains the decimal values 73 72 68 82.
The IHDR chunk shall be the first chunk in the PNG datastream. It contains:
Width | 4 bytes |
Height | 4 bytes |
Bit depth | 1 byte |
Colour type | 1 byte |
Compression method | 1 byte |
Filter method | 1 byte |
Interlace method | 1 byte |
Width and height give the image dimensions in pixels. They are PNG four-byte unsigned integers. Zero is an invalid value. Bit depth is a single-byte integer giving the number of bits per sample. Sample here means one color. Valid values are 1, 2, 4, 8, and 16, although not all values are allowed for all color types. Each pixel is made up of three bytes. Each of the three bytes represents a different color, in this case red, green, blue. These colors combined make up the one pixel you actually see.
The IDAT Chunk
The four-byte chunk type field contains the decimal values 73 68 65 84. The IDAT chunk contains the actual image data which is the output stream of the compression algorithm. If you're curious about the filtering and compression on PNG images check out Для просмотра ссылки Войди
The IEND Chunk
The four-byte chunk type field contains the decimal values 73 69 78 68. The IEND chunk marks the end of the PNG datastream. The chunk's data field is empty.
Steganography on PNG
As mentioned earlier each pixel is arranged in 3 bytes the first red, the second green, and the third blue. Various studies have yielded different results, but the all-reliable source wikipedia says that the human eye can distinguish approximately 10 million different colors. With three bytes total we can represent 2^(8+3) or 16,777,216 different colors. That means that there approximately 6,777,216 colors that we can represent, but the human eye won't notice the change as compared to one of the other 10 million colors.
Said another way:
Given the following truecolor pixel (this data would be located in an IDAT chunk)
Код:
Red Part -> 10100100 Green Part -> 11101100 Blue Part -> 1010100
The human eye would not be able to distinguish the difference if we changed the three parts to
Код:
Red Part -> 10100101 Green Part -> 11101101 Blue Part -> 1010101
You can probably see where this is going. This means that I can change the least significant bit of each byte and no one will be able to visually tell the difference between the original pixel and the changed pixel unless of course they have super human highdef eyes (which to my extensive medical knowledge don't exist
What we're going to do is leverage this to hide messages in the least significant bits in the following manner:
Код:
One byte of a message to hide: 10101010
8 bytes of image data (that's two pixels and the red and green bytes of a third pixel)
11110000, 10101010, 11001100, 11100011, 11111111, 00000000, 00001111, 10011011
Encode our hidden message from least significant bit (LSB) to most significant bit (MSB) into the least significant bits of the image data. The image data then becomes:
Код:
11110000, 10101011, 11001100, 11100011, 11111110, 00000001, 00001110, 10011011
Now we've got one byte of message data hidden in our image. Do it several thousand more times and we can hide quite a lot of data. Clearly this isn't the most efficient scheme since the PNG image we use we'll need to be 8 times the size of the hidden data. We're going for simple here rather than super slick. We could get a lot more efficiency by leveraging other hiding places, compression techniques, using more bits that wouldn't be detectable, alpha channels, text channels, and a million other things, but here we'll stick to the least significant bit.
Using the code
Programmer Note: Everything below was done in Visual Studio 2012
To work with the PNG images I made a PNG image class to implement the steganography portion and used the libpng library and the zlib library to actually do all the PNG manipulation and such. It's worth mentioning again that PNG image files are filtered and then compressed so that they take up less space. For this reason we can't edit the IDAT chunks of a raw PNG or you'll get some really funky results (I tried just for giggles and it's more than a little noticeable when you try to encode something). That being said, we need libpng to decompress and then unfilter the image for us. The compression algorithm used by libpng is deflate, which is implemented by zlib in case you were wondering why we need zlib.
You can view the libpng documentation Для просмотра ссылки Войди
The PNG_file class definition is as follows:
Код:
#include <png.h>
/* Class PNG_file
* Contains the data for a PNG file object
*/
class PNG_file {
public:
//Constructor
PNG_file(const char *inputFileName);
//Function for encoding data into the PNG from a file
void encode(const char *fileToEncodeName);
//Function for outputing the newly created PNG to a file
void outputPNG(const char *outputFileName);
//Function for outputing the decoded PNG to a file
void decode(const char *outputFileName);
private:
png_bytep* row_pointers;
png_infop info_ptr;
png_structp read_ptr;
png_structp write_ptr;
};
Don't worry too much about understanding what each bit means just yet. We'll go through it.
[/SHOWTOGROUPS]
Последнее редактирование: