ステガノグラフィ。 bmpファイルのテキスト情報を非表示にします。 C#での実用的な実装

良い一日!



Habrahabrにはこのトピックに関する記事が既にあります。この問題に対するソリューションのバージョンを共有したいと思います。 それでは、始めましょう。





一般的な情報



ステガノグラフィックシステム(stegosystem)-情報を送信するための隠れチャネルを作成するために使用される方法と手段の組み合わせ。 最近では、著作権保護システムとDRM(デジタル著作権管理)システムの基礎となるデジタル透かしの埋め込みに、原則としてステガノグラフィがよく使用されます。



LSBメソッドを実装します。

LSB(最下位ビット、最下位ビット)-このメソッドの本質は、コンテナ内の最後の有意ビット(画像、オーディオ、またはビデオ)を非表示にするメッセージのビットで置き換えることです。 空のコンテナと満杯のコンテナの違いは、人間の感覚器官に気付かれてはなりません。



また、bmpの暗号化/復号化用のパレットを含まないファイルを使用します。 このようなbmpファイルでは、3バイトごとに3ピクセルの色が決まります。



実装の準備



情報のビットを処理し、1ピクセルの色が1バイトを占有するため、以下に示すように、バイトをビットに、またはその逆に変換するメソッドが必要になります。

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; }
      
      





それらは理解可能であり、説明する価値はないと思います。



したがって、bmp情報の場所は次のようになります。



まず、暗号化されたファイルの属性をピクセル0.0に書き込むコードを考えます。

  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);
      
      





Symbol変数のコードには、シンボルコード「/」が格納されます。 次に、このコードはビットの配列(ArrBeginSymbol変数)に変換されます。 ピクセル0.0の色は、curColor変数に格納されます。 次に、ピクセルの3つの構成色のそれぞれがビットの配列に変換され、次に下位2ビットが赤の「/」記号のビットに置き換えられ、下位3ビットが「/」記号のビットに置き換えられ、色の下位3ビットも青に置き換えられます。 3つの新しい受信色から、以前の色の代わりに新しいピクセル色(nColor)が作成および設定されます。 すべて、ファイルに情報があるという兆候がbmpファイルに書き込まれます。

情報の記録方法、つまり2ビット、3ビット、3ビットは、1バイトの情報が1つのピクセルにすぐに記録されるため、便宜上選択されています。



次に、上記の機能を確認する方法を検討します

 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; }
      
      





このメソッドは上記のコードと似ていますが、正反対です。 文字「/」がピクセル0.0に書き込まれている場合、関数はtrueを返します。



次に、テキスト情報のサイズがファイルに書き込まれます。 この方法をさらに詳しく検討してください。

  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は、ソーステキストの文字数を記録します。 各桁は1バイトを占有するため、ソーステキストの最大長は999-4 = 995文字です(4はファイル内の情報の符号用に1ピクセル、テキスト情報のサイズ用に3ピクセルです)。 必要に応じて、ピクセルを0.1から0.3に変更することで、たとえば0.1から0.4などに増やすことができます。 forループでは、ソーステキストの量の各桁がビットの配列に変換され、上記の原理に従ってより低い色のピクセルに書き込まれます。



テキスト情報サイズの読み取り方法:

  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); }
      
      





WriteCountText関数の逆のメソッド。 説明する、価値がないと思う。



実装



ファイルを開いたり閉じたり、エラーをチェックするコードは省略します。プロジェクトをダウンロードすることで表示できます。 以下は、実際にファイルに情報を書き込むコードです。 いくつかのコードはすでに上に与えられています。

ビットマップbPic-画像付きの開いているファイル。

  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; } }
      
      







そして、それに応じて、bmpファイルから情報を読み取るコード

 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);
      
      







おわりに



LSBメソッドの実装は難しくなく、bmpファイル内の必要な情報を隠すために使用できます。 しかし、この方法の重大な欠点は、bmpサイズが大きいことです。このため、この方法は、インターネットを介して機密情報を送信するには実行不可能です。

プロジェクト自体: drive.google.com/file/d/0B-i4aT8Q0ZNxdDRiUTc2WUs2OTA/view?usp=sharing

アーカイブには2つの画像が含まれ、1つはテキストあり、もう1つはテキストなしです。



All Articles