📜 ⬆️ ⬇️

Steganography. Hide text information in the bmp file. Practical implementation in C #

Good day!

On Habrahabr, there are already articles on this topic and I want to share my solution to this problem. So, let's begin.


General information


Steganographic system (stegosystem) - the combination of methods and tools used to create a hidden channel for transmitting information. In our time, steganography is often used, as a rule, for embedding digital watermarks, which is the basis for copyright protection systems and DRM (Digital rights management) systems.
')
We will implement the LSB method.
LSB (Least Significant Bit, the least significant bit) - the essence of this method is to replace the last significant bits in the container (image, audio or video) with the bits of the message to be hidden. The difference between empty and filled containers should not be perceptible to human organs.

We will also use the bmp file that does not contain the palette for encryption / decryption. In such a bmp file, 3 pixel colors are defined every 3 bytes.

Preparation for implementation


Since we will work with bits of information, and the color of one pixel occupies one byte, we will need methods for converting byte into bits and vice versa, which are presented below:
private BitArray ByteToBit(byte src) { BitArray bitArray = new BitArray(8); bool st = false; for (int i = 0; i < 8; i++) { if ((src >> i & 1) == 1) { st = true; } else st = false; bitArray[i] = st; } return bitArray; } private byte BitToByte(BitArray scr) { byte num = 0; for (int i = 0; i < scr.Count; i++) if (scr[i] == true) num += (byte)Math.Pow(2, i); return num; } 

I think they are clear and not worth explaining.

And so, the location in the bmp information will be as follows:

To begin with, consider a code that writes in a pixel 0.0 a sign of an encrypted file.
  byte [] Symbol = Encoding.GetEncoding(1251).GetBytes("/"); BitArray ArrBeginSymbol = ByteToBit(Symbol[0]); Color curColor = bPic.GetPixel(0, 0); BitArray tempArray = ByteToBit(curColor.R); tempArray[0] = ArrBeginSymbol[0]; tempArray[1] = ArrBeginSymbol[1]; byte nR = BitToByte(tempArray); tempArray = ByteToBit(curColor.G); tempArray[0] = ArrBeginSymbol[2]; tempArray[1] = ArrBeginSymbol[3]; tempArray[2] = ArrBeginSymbol[4]; byte nG = BitToByte(tempArray); tempArray = ByteToBit(curColor.B); tempArray[0] = ArrBeginSymbol[5]; tempArray[1] = ArrBeginSymbol[6]; tempArray[2] = ArrBeginSymbol[7]; byte nB = BitToByte(tempArray); Color nColor = Color.FromArgb(nR, nG, nB); bPic.SetPixel(0, 0, nColor); 

The code in the Symbol variable stores the symbol code "/". Further, this code is converted to an array of bits (ArrBeginSymbol variable). The color of the 0.0 pixel is stored in the curColor variable. Then each of the three constituent colors of the pixel is converted into an array of bits, then the low 2 bits are replaced with the "/" bits in red, the low 3 bits are replaced with the "/" bits in green, and the low 3 bits are also replaced with blue. From 3 new received colors, a new pixel color (nColor) is created and set instead of the previous color. All, a sign that the file has information recorded in the bmp file.
The way of recording information, that is, 2 bits, 3 bits and 3 bits, is chosen for convenience of work, because a single byte of information is written into one pixel at once.

Next, we consider the method for checking the characteristic described above.
 private bool isEncryption(Bitmap scr) { byte[] rez = new byte[1]; Color color = scr.GetPixel(0, 0); BitArray colorArray = ByteToBit(color.R); //        BitArray messageArray = ByteToBit(color.R); ;//    messageArray[0] = colorArray[0]; messageArray[1] = colorArray[1]; colorArray = ByteToBit(color.G);//        messageArray[2] = colorArray[0]; messageArray[3] = colorArray[1]; messageArray[4] = colorArray[2]; colorArray = ByteToBit(color.B);//        messageArray[5] = colorArray[0]; messageArray[6] = colorArray[1]; messageArray[7] = colorArray[2]; rez[0] = BitToByte(messageArray); //  ,   1  string m = Encoding.GetEncoding(1251).GetString(rez); if (m == "/") { return true; } else return false; } 

The method is similar to the code above with the exact opposite. If the "/" character is written in pixel 0.0, the function returns true, otherwise it is false.

Next, the size of the text information is written to the file. Consider the method in more detail:
  private void WriteCountText(int count, Bitmap src) { byte[] CountSymbols = Encoding.GetEncoding(1251).GetBytes(count.ToString()); for (int i = 0; i < 3; i++) { BitArray bitCount = ByteToBit(CountSymbols[i]); //   Color pColor = src.GetPixel(0, i + 1); //1, 2, 3  BitArray bitsCurColor = ByteToBit(pColor.R); //    bitsCurColor[0] = bitCount[0]; bitsCurColor[1] = bitCount[1]; byte nR = BitToByte(bitsCurColor); //    bitsCurColor = ByteToBit(pColor.G);//     bitsCurColor[0] = bitCount[2]; bitsCurColor[1] = bitCount[3]; bitsCurColor[2] = bitCount[4]; byte nG = BitToByte(bitsCurColor);//   bitsCurColor = ByteToBit(pColor.B);//     bitsCurColor[0] = bitCount[5]; bitsCurColor[1] = bitCount[6]; bitsCurColor[2] = bitCount[7]; byte nB = BitToByte(bitsCurColor);//   Color nColor = Color.FromArgb(nR, nG, nB); //     src.SetPixel(0, i + 1, nColor); //     } } 

CountSymbols records the number of characters in the source text. Each digit occupies one byte, therefore the maximum length of the source text is 999 - 4 = 995 characters (4 is one pixel for the presence of information in the file and three pixels for the size of the text information). If necessary, you can increase by taking the pixels not from 0.1 to 0.3, but from 0.1 to 0.4 for example, and so on. In the for loop, each digit of the amount of source text is converted into an array of bits and written to the lower pixels of the color according to the principle described above.

Method for reading text size information:
  private int ReadCountText(Bitmap src) { byte[] rez = new byte[3]; //  3 , ..  999   for (int i = 0; i < 3; i++) { Color color = src.GetPixel(0, i + 1); // 1, 2, 3  BitArray colorArray = ByteToBit(color.R); //  BitArray bitCount = ByteToBit(color.R); ; //    bitCount[0] = colorArray[0]; bitCount[1] = colorArray[1]; colorArray = ByteToBit(color.G); bitCount[2] = colorArray[0]; bitCount[3] = colorArray[1]; bitCount[4] = colorArray[2]; colorArray = ByteToBit(color.B); bitCount[5] = colorArray[0]; bitCount[6] = colorArray[1]; bitCount[7] = colorArray[2]; rez[i] = BitToByte(bitCount); } string m = Encoding.GetEncoding(1251).GetString(rez); return Convert.ToInt32(m, 10); } 

The method is the inverse of WriteCountText. Explain, I think, not worth it.

Implementation


Omit the code that opens / closes the file, checks for errors, it can be viewed by downloading the project. We give the code that actually writes information to a file. Some code has already been given above.
Bitmap bPic - open image file.
  BinaryReader bText = new BinaryReader(rText, Encoding.ASCII); List<byte> bList = new List<byte>(); while (bText.PeekChar() != -1) { //         bList.Add(bText.ReadByte()); } int CountText = bList.Count; //  CountText -    ,    bText.Close(); rFile.Close(); //,       if (CountText > ((bPic.Width * bPic.Height)) - 4 ) { MessageBox.Show("      ", "", MessageBoxButtons.OK); return; } //,      if (isEncryption(bPic)) { MessageBox.Show("  ", "", MessageBoxButtons.OK); return; } byte [] Symbol = Encoding.GetEncoding(1251).GetBytes("/"); BitArray ArrBeginSymbol = ByteToBit(Symbol[0]); Color curColor = bPic.GetPixel(0, 0); BitArray tempArray = ByteToBit(curColor.R); tempArray[0] = ArrBeginSymbol[0]; tempArray[1] = ArrBeginSymbol[1]; byte nR = BitToByte(tempArray); tempArray = ByteToBit(curColor.G); tempArray[0] = ArrBeginSymbol[2]; tempArray[1] = ArrBeginSymbol[3]; tempArray[2] = ArrBeginSymbol[4]; byte nG = BitToByte(tempArray); tempArray = ByteToBit(curColor.B); tempArray[0] = ArrBeginSymbol[5]; tempArray[1] = ArrBeginSymbol[6]; tempArray[2] = ArrBeginSymbol[7]; byte nB = BitToByte(tempArray); Color nColor = Color.FromArgb(nR, nG, nB); bPic.SetPixel(0, 0, nColor); WriteCountText(CountText, bPic); //     int index = 0; bool st = false; for (int i = 4; i < bPic.Width; i++) { for (int j = 0; j < bPic.Height; j++) { Color pixelColor = bPic.GetPixel(i, j); if (index == bList.Count) { st = true; break; } BitArray colorArray = ByteToBit(pixelColor.R); BitArray messageArray = ByteToBit(bList[index]); colorArray[0] = messageArray[0]; // colorArray[1] = messageArray[1]; //     byte newR = BitToByte(colorArray); colorArray = ByteToBit(pixelColor.G); colorArray[0] = messageArray[2]; colorArray[1] = messageArray[3]; colorArray[2] = messageArray[4]; byte newG = BitToByte(colorArray); colorArray = ByteToBit(pixelColor.B); colorArray[0] = messageArray[5]; colorArray[1] = messageArray[6]; colorArray[2] = messageArray[7]; byte newB = BitToByte(colorArray); Color newColor = Color.FromArgb(newR, newG, newB); bPic.SetPixel(i, j, newColor); index ++; } if (st) { break; } } 


And, accordingly, the code that reads information from the bmp file
 Bitmap bPic = new Bitmap(rFile); if (!isEncryption(bPic)) { MessageBox.Show("    ", "", MessageBoxButtons.OK); return; } int countSymbol = ReadCountText(bPic); //   byte[] message = new byte[countSymbol]; int index = 0; bool st = false; for (int i = 4; i < bPic.Width; i++) { for (int j = 0; j < bPic.Height; j++) { Color pixelColor = bPic.GetPixel(i, j); if (index == message.Length) { st = true; break; } BitArray colorArray = ByteToBit(pixelColor.R); BitArray messageArray = ByteToBit(pixelColor.R); ; messageArray[0] = colorArray[0]; messageArray[1] = colorArray[1]; colorArray = ByteToBit(pixelColor.G); messageArray[2] = colorArray[0]; messageArray[3] = colorArray[1]; messageArray[4] = colorArray[2]; colorArray = ByteToBit(pixelColor.B); messageArray[5] = colorArray[0]; messageArray[6] = colorArray[1]; messageArray[7] = colorArray[2]; message[index] = BitToByte(messageArray); index++; } if (st) { break; } } string strMessage = Encoding.GetEncoding(1251).GetString(message); 


Conclusion


The LSB method is not complicated to implement, and it can be used to hide the necessary information in the bmp file. But a significant disadvantage of the method is that the size of bmp is large, which makes this method unviable for transmitting confidential information over the Internet.
The project itself: drive.google.com/file/d/0B-i4aT8Q0ZNxdDRiUTc2WUs2OTA/view?usp=sharing
The archive 2 pictures, one with the text, the second without.

Source: https://habr.com/ru/post/140373/


All Articles