Ooer.com by Chris Neale
Steganography With GD

Steganography With GD

Images stored on computers are generally stored in one of two ways. There are indexed colour images, such as GIF and 8bit PNG files, and there are 24bit and 32bit true colour images such as JPG and 32bit PNG. A 24bit image is made up of 3 8bit colour channels, one each for red, green and blue. An 8bit channel is an 8bit integer, it has a range from 0 to 255, 256 different possible values. By combining 3 numbers between 0 and 255 it is possible to represent 16.7 million different colours. This is what makes modern image file formats able to represent photographs and graphics in such a realistic fashion.

Understanding the way images are make up allows us to do something rather clever with them. If you take 256 values you can change a value from 0 to 1 and the user viewing the image will probably be able to perceive a small difference. However, with 16.7million colours such a change from 0 to 1 is imperceivable. This allows us to hide information inside an image without anyone knowing its there. The art of hiding information in plain site is called steganography.

Steganography has many uses in the real world. You can embed a watermark in your photographs that you can use later to prove that you make the images, you can send seemingly innocent email images to friends with secret messages that not even the strictest filter would find, and you can embed messages in your webpages for users to try and find.

The way steganography works in our PHP script is to take a source image and combine into it a message image using the least significant bits of the pixel values. For example, if a pixel is completely black then its pixel values for each channel would all be 00000000. As each channel is 8 bits long there are 8 0s.

If the pixel in our message is black we set the least significant bit to 1. In other words our solid black pixel is now 00000001. The user will not be able to see the change, especially in a photograph, but our decoder will be able to. To extract the message all we need to do is scan through the image and where the least significant bit is equal to 1 we draw a black pixel, and where it is 0 we draw a white one. This will give us a copy of our message picture.

In this script we will use a technique called 'binary shifting'. This is a way of altering numbers in their binary state. If you do not understand binary then follow the link at the end of this article for a tutorial.
In order to hide a message we need to create a message. The easy method of doing this is to create an image file in plain black and white that contains your message.


Now that we have our message we need a source image that we're going to hide a message in. As I am a keen photographer I will be using one of my original images.
I decided to use a nice picture of a butterfly.


As with most image scripts the first thing to do is setup and open the two image files as GD resources. This is basic stuff.

<?php
$m
= imagecreatefrompng("message.png");
$s = imagecreatefromjpeg("source.jpg");
$image = imagecreatetruecolor(imagesx($s),imagesy($s));
?>

The next stage is to loop through the pixels of the source image.

<?php
for ($x=0;$x<imagesx($s);$x++) {
    for (
$y=0;$y<imagesy($s);$y++) {
        
//Steganography process
    
}
}
?>

Finally we have to encode the message into the destination image. This is where the binary maths process occurs. Using "&~" (and not) makes the least significant bit of the value 0, while using "|" (or) sets it to 1. We use the "&~" operator if the pixel in the message file is black, and the "|" operator is anything else. Of course, this process could be applied to an image filtering script to extract particular colours, but thats going off on a tangent.

<?php
$rgb
= imagecolorat($s,$x,$y);
$r=($rgb>>16)&0xFF;
$g=($rgb>>8)&0xFF;
$b=$rgb&0xFF;
$rgb = imagecolorat($m,$x,$y);
if (
$rgb == 0) {
    
$r=$r&~1;
    
$g=$g&~1;
    
$b=$b&~1;
} else {
    
$r=$r|1;
    
$g=$g|1;
    
$b=$b|1;
}
$col = imagecolorallocate($output,$r,$g,$b);
imagesetpixel($output,$x,$y,$col);
?>

Once we have set the least significant bit to be the value of the message image we then set the colour and draw the pixel in the destination image, and we're ready to save it and send the image somewhere.
To save the image simply right click to save it in a browser, or save the image using the optional parameters in imagepng(). Note: You cannot use imagejpeg() to create your image as the jpeg process loses the information hiding our message.

<?php
header
("Content-Type: image/png");
imagepng($output);
?>

Now that we have our hidden image we can save it and send it to our friends.


But what do they do with it once they receive it? Well, they would have to run it through a script that reverses the hiding process and outputs the decoded message image.

<?php
$s
= imagecreatefrompng("hidden.png");
$output = imagecreatetruecolor(imagesx($s),imagesy($s));
for (
$x=0;$x<imagesx($s);$x++) {
    for (
$y=0;$y<imagesy($s);$y++) {
        
$rgb=imagecolorat($s,$x,$y);
        
$r=(((($rgb>>16)&0xFF)&1)==1)?0:255;
        
$g=(((($rgb>>8)&0xFF)&1)==1)?0:255;
        
$b=((($rgb&0xFF)&1)==1)?0:255;
        
$col=imagecolorallocate($output,$r,$g,$b);
        
imagesetpixel($output,$x,$y,$col);
    }
}
header("Content-Type: image/png");
imagepng($output);
?>

Thats all there is to it. Pretty easy stuff. Of course, it would be possible to go much futher. Heres some ideas for extending the script:

* Hide 3 images, one in each colour channel.
* Hide 24 images, eight in each colour channel, one per 'bit'. This would make an image that looked like static.
* Hide a 6bit colour picture using the 2 smallest bits in each channel.
* Hide a repeating watermark in the image, and automatically check it against a database of watermarks to see whose image it is.