A long time ago, I wrote my . That post was dedicated to a rather interesting problem, namely steganography. Of course, the solution proposed in that old topic can't be called true steganography. It's just a manipulation with file formats, but nevertheless, it is quite an interesting game.
Today, we'll dig a little deeper and examine the LSB algorithm. If you're interested, you're welcome to read on. (Below this point, the traffic is about a megabyte.)
First of all, a brief introduction is necessary. It is well known that the purpose of cryptography is to make it impossible to read secret information. Naturally, cryptography has its areas of application, but there's also another approach to data protection. Instead of encrypting information, one can make it seem as if there is no information at all. That's precisely what steganography is for. Wikipedia assures us that, "steganography (from Greek στεγανοσ — hidden and γραφω — I write, literally 'secret writing') is the science of hidden information transmission by keeping the fact of transmission secret.
Of course, no one prohibits combining cryptographic and steganographic methods. Moreover, in practice, that's often done, but our goal is to understand the basics. If you carefully study the Wikipedia article, you'll find that steganography algorithms involve a so-called container and a message. The container is any information that helps conceal our secret message.
In our case, the container will be an image in BMP format. First, let's examine the structure of this file. The file can be conditionally divided into four parts: the file header, the image header, the palette, and the actual image. For our purposes, we only need to know what's recorded in the header.
The first two bytes of the header are the signature BM, followed by a double word that indicates the file size in bytes, the next 4 bytes are reserved and should contain zeros, and finally, in another double word, the offset from the beginning of the file to the actual image bytes is recorded. In a 24-bit BMP file, each pixel is encoded with three bytes BGR.
Now we know how to access the image, we need to understand how to write the necessary information into it. For this, we will use the LSB method. The essence of the method is as follows: we replace the least significant bits in the bytes responsible for color encoding. Suppose the next byte of our secret message is 11001011, and the bytes in the image are…11101100 01001110 01111100 0101100111…, then the encoding will look like this. We will break the byte of the secret message into 4 two-bit parts: 11, 00, 10, 11, and replace the least significant bits of the image with these fragments: …11101111 01001100 01111110 0101100111…. Such a replacement is generally not noticeable to the human eye. Moreover, many old output devices may not even be able to display such insignificant changes.
It is clear that we can change not only the 2 least significant bits, but any number of them. Here is the following pattern: the more bits we change, the more information we can hide, and the greater disturbances it will cause in the original image. For example, here are two images:


Despite my best efforts, I couldn't see the difference between them, yet in the second image, a poem by Lewis Carroll, 'The Hunting of the Snark,' is hidden using the method described. If you've made it this far, you're probably curious about the implementation. It's quite simple, but I warn you right away that everything is done in Delphi. There are two reasons for this: 1. I believe Delphi is a good, viable language; 2. This program was created during the preparation of a course on the fundamentals of machine vision, and the students to whom I teach this course currently know nothing but Delphi. For those unfamiliar with the syntax, one thing should be clarified: shl x means a bitwise shift left by x, shr x means a bitwise shift right by x.
Let's assume we are writing text stored in a string and replacing the least significant two bytes:
Code for writing:
for i:=1 to length(str) do
begin
l1:=byte(str[i]) shr 6;
l2:=byte(str[i]) shl 2; l2:=l2 shr 6;
l3:=byte(str[i]) shl 4; l3:=l3 shr 6;
l4:=byte(str[i]) shl 6; l4:=l4 shr 6;
f.ReadBuffer(tmp,1);
f.Position:=f.Position-1;
tmp:=((tmp shr 2) shl 2)+l1;
f.WriteBuffer(tmp,1);
f.ReadBuffer(tmp,1);
f.Position:=f.Position-1;
tmp:=((tmp shr 2) shl 2)+l2;
f.WriteBuffer(tmp,1);
f.ReadBuffer(tmp,1);
f.Position:=f.Position-1;
tmp:=((tmp shr 2) shl 2)+l3;
f.WriteBuffer(tmp,1);
f.ReadBuffer(tmp,1);
f.Position:=f.Position-1;
tmp:=((tmp shr 2) shl 2)+l4;
f.WriteBuffer(tmp,1);
end;
Code for reading:
for i:=1 to MsgSize do
begin
f.ReadBuffer(tmp,1);
l1:=tmp shl 6;
f.ReadBuffer(tmp,1);
l2:=tmp shl 6; l2:=l2 shr 2;
f.ReadBuffer(tmp,1);
l3:=tmp shl 6; l3:=l3 shr 4;
f.ReadBuffer(tmp,1);
l4:=tmp shl 6; l4:=l4 shr 6;
str:=str+char(l1+l2+l3+l4);
end;
And for the truly lazy – .
Thank you.
Source: habr.com
