Lenny Domnitser’s

⇙ Jump to content


This is a static archive of the domnit.org blog,
which Lenny Domnitser wrote between 2006 and 2009.

How Stepic Hides Data in Images

Yesterday, I introduced Stepic, a tool and Python module to hide any data within an image. Stepic will get its own documentation eventually, but for now I want to describe the method of steganography Stepic uses.

Stepic simply reads pixels from left to right, starting at the top, just like the English text you’re reading now. It reads three pixels at a time, each of which contains three values: red, green, and blue. (Sometimes pixels contain a fourth value which describe their opacity. Stepic ignores these.)

Each group of pixels has nine values. A byte of data has eight bits, so if each color can be modified just slightly, by setting the least significant bit to zero or one, these three pixels can store a byte, with one color value left over. Stepic uses the the least significant bit of this left over value to signify the end of data: 0 means keep reading; 1 means the message is over.

An example: suppose you want to hide the message “Hello”. Since the message is 5 bytes long, the image has to be at least 5 × 3 pixels = 15 pixels. The following listing is an example of a 4 × 4 image , which has 16 pixels—more than enough. Each (red, green, blue) triple represents a pixel.

[(22, 180, 163), (88, 65, 21), (123, 240, 125), (50, 214, 175),
(200, 56, 164), (158, 220, 102), (224, 63, 33), (155, 217, 110),
(167, 135, 29), (84, 62, 139), (7, 11, 123), (165, 219, 151),
(45, 165, 117), (145, 185, 5), (33, 205, 193), (165, 18, 125)]

Start at the top-left, and read in three pixels: (22, 180, 163), (88, 65, 21), (123, 240, 125). We want to encode ‘H’, which is ASCII 72, or 0100 1000 in binary. Taking the color values from left to right, the value should be made odd for 1 and even for 0. 22 stays 22, 180 becomes 181, 163 becomes 162, and so on, through 240 staying 240. Finally, 125 is changed to 124 to indicate that the data is not finished.The next three pixels are read—(50, 214, 175), (200, 56, 164), (158, 220, 102)—and modified the same way, using ASCII ‘e’, or the bitstring 0110 0101, with the last color value, 102, left alone to indicate that the data is not finished. It goes on this way until ‘o’, which encodes 0110 1111 1, the first eight bits for the data, and that last 1 to mark the end of the data. The one pixel that is left over doesn’t matter. Our new image is:

[(22, 181, 162), (88, 65, 20), (122, 240, 124), (50, 215, 175),
(200, 56, 165), (158, 221, 102), (224, 63, 33), (154, 217, 111),
(166, 134, 28), (84, 63, 139), (6, 11, 123), (164, 218, 150),
(44, 165, 117), (144, 185, 5), (33, 205, 193), (165, 18, 125)]

To decode, read three pixels at a time from left to right, top to bottom, until the last bit of the last color of the last pixel that you read is a 1. The least significant bit of each color value besides every third blue makes up the data.

Since the data is stored in the colors, not in any format specific manner, Stepic can be write to PNG, BMP, and other formats, and can read a whole slew of others. (JPEG doesn’t work, since it throws out data.) Files that Stepic writes can be modified by other programs as long as the first 3n pixels are not modified, where n is the length of the hidden data.

The encoding scheme gives no hint about whether an image contains data, so Stepic will always extract at least one byte from any image, whether or not anybody intentionally hid data there. Data that just happens to be in an image is likely to be very short, and garbage.

This was my first experience with steganography, and I got to do some fun bit bashing. I hope this was an interesting introduction to a simple steganographic technique. If you’re interested in the actual bitwise operations and implementation details, please see the source.

1 comment on How Stepic Hides Data in Images

1.   Steganography using Python by Design says:

[...] if you are unfamiliar with the technology used (in this case least-significant bit) then have a look here. It would be nice to see support for second least significant bit in order to hide more data, but of [...]

—  Steganography using Python by Design, 30 December 2007, 20:27

Comments on this entry are now closed. Thanks to those who participated. You can still email me.