The current articles on the website have talked about how to hide data inside images via either appending bytes after an image and by manipulating pixels. In this article, we’re going to go a bit deeper in the second technique by explaining a few algorithms used to hide data.

Knowledge that will help you to read this article:
    - Architecture of a basic image.
    - Binary representation of bytes.
    - Any programming language will be useful if you want apply those algorithm.

We’ll consider the following:
    - Examples won’t include any data transformation (we’ll be only hiding plain text data).
    - Binary representations are written in big-endian for better understanding.
    - We won’t take transparency into account and we’ll use BMP file with 24bpp.

Brief explanation of image architecture

Here is what you should keep in mind: an image file is not more than a huge list of pixels that should be displayed on your screen.
In a 24bpp BMP file, we can distinguish two main parts; a header that’s describing the file and a body that’s including all the pixel information.
A pixel is a composition of 3 colors, red, green and blue that are encoded in 1 byte per color (so 3 for a pixel). A byte can take value from 0 to 255 and it’s those byte we will alter to hide our data.

Methods of hiding data

Method 1: Encode raw bytes

This method consists of encoding each value as they are inside our image.
Let’s say we want to hide the word “Hidden” inside an image. That’s 6 characters to hide (so 6 bytes). Since we can hide 3 bytes per pixels, that’s means we can hide our data in 2 pixels.

Let’s create an image with a width of 2 and height of 1.

We want to put our data so we can hide “Hid” in the first pixel and “den” in the second one:

Let’s transform our data into its hexadecimal representation.

We’re now able to affect value to our pixels:
Pixel 1: Red = 48 | Green = 69 | Blue = 64
Pixel 2: Red = 64 | Green = 65 | Blue = 6E

Congrats we’ve hidden our first data in an image!

    - Very easy to apply.
    - We can hide a large amount of data through this process
    - If we use this process on an existing image (like a photo of your cat), it will just destroy the main image as we’re replacing all the bytes and it will be obvious that some data are hidden there.
Because of the cons we need to find a more reliable way to hide data without telling to everyone “Hey! Look at my pixelated image, there is something here”. If we cannot hide our data directly, why not just hide the minimum of information per pixels? As we said before, a pixel is 3 bytes that does take the value from 0 to 255. As a human, we’re not able to make the difference between a red with the value of 255 and the value of 254. That’s what we’re going to exploit to hide our data.

Method 2: Least significant bit encoding

We’re going to hide our data by using only the least significant bits of a pixel. That means we can use up to 3 bits per pixels. Let’s take the word “Hidden” again and hide it with this method. We still have 6 bytes to hides but since one byte contains 8 bits and we cannot store more than 3 bits per pixels we’ll need a bigger image if we want be able to hide this data.

Let’s calculate the space we’ll need this time:
6bytes * 8bits = 48 bits to hide.
48 bits / 3bits per pixel = 16 pixels.
That’s 800% more pixels than the first method!

Let’s then make an image with a width of 4 and an height of 4 and add a white background. Now, let’s transit our data into 8 bits padded binary:

We need to encode this data into each pixel we have. Since we have a white image, all the pixels have the hex value of FF FF FF that’s 11111111 11111111 11111111 in binary. To hide the letter ‘H’, we’ll need approximately 3 pixels. So let’s change the pixels composite by the following:

Repeat this for each byte in our data and we’re now having a data hidden that is invisible to human eyes!

    - Completely undetectable by visual sight in an existing image.
    - Require a lot of space for a small amount of data. (~2.7 pixels per bytes compared to 0.3 pixels per byte for the first method.)
Now we’ve find a way to make our change invisible, the truth is the human eye is even less perfect than that and we’re going to optimized our method with this.
It has been proved that the human eyes can only percept about 10 millions of colors.
Thus said, we can calculate how many bits we are able to change to keep our changes invisible.
We’ll be quick and admit we can modify at most 5 bits. Since our eyes are more sensitive to contrast change on the green channel, we’ll only modify 1bit from here and 2 from the others.
Here come the third method

Method 3: least significant bit encoding (optimized)

So far, this method is the same as the second one, but instead of storing only 3 bit per pixel, we’ll store 5. That’s ensuring ~ 1.7 pixels per character.
In our case and the hiding of the word “Hidden” it will be still +480% spaces occupied than the first method but much compressed than the second one.

Let’s see for example what will looks like the letter ‘H’ inside our white image.
H = 01001000 =>

Process the same for each character to hide and you’re done.

    - Changes are still invisible to human eyes!
    - Optimized space!
    - Still more space than method 1.


We got out way to hide data but now a few problems persist.
Here is some and how we can possibly fix them:

How to know the length of the data?

Let’s say we have a recalcitrant mailbox that’s filter any exe we’re sending even inside archive and it pissed us off so much that we’ve decided to hide it into a photo of our cat (because there is not enough over the net).
So far, for a text based data that’s fine, you’ll be able to see by yourself where it starts and where it’s end.
But for the case of an executable, that’s another story !
We must know how many data we need to extract or we’ll never get our exe to work.

Example: Write an Header
We can write an header with the seize of the data to recover. Let’s take a 32 bits integer so we can reserve little less than 7 pixels to write the length of our data.
If we want apply this to our word “Hidden” with the method 3, this will be:
Length(“Hidden”) = 6 = 110 = 00000000 00000000 00000000 00000110
Let’s just prefix this value to our image and we’ll have the length of the data to extract.

How to prevent an attacker from extracting our data?

Hard time here, we spend so much time encoding our data but they’re still so easy to read. No doubt someone suspicious fill find out what’s going on if he intercept our images. The first one will probably be to add offset to our data so we can hide them in a specific area of our image. This is not an acceptable solution as a global analysis will just break it. Instead, we’re going to generate a key that will contain all the pixel indexes where we can find our data. Let’s say we want to hide the word “Hide” into a 5w * 5h image. We need to generate a key of the position of 7 pixels.

For example:
=> 14, 8, 15, 1, 3, 9, 5

We’ll be hiding the 5 first bit of ‘h’ in the pixel n^14 and the 3 other in the pixel n^8 then the first two bits of ‘I’ in the pixel 8, 5 other in pixel n^15 and the last one in n^1 etc..

For better results, we’ll use a cryptographic random to generate the key. We even can improve this by setting the number of bits we’re hiding from 1 to 5 and make the output even less predictable but we need make sure our image can support the amount of data we’re going to store with the specified key.

Please note this is not a perfect solution to hide data and it shouldn’t be used as it is for real critical information.