GIFステガノグラフィ

はじめに


ご挨拶。
少し前まで、私が大学で勉強していたとき、「情報保護のソフトウェア手法」という分野で授業がありました。 割り当て時には、メッセージをGIFファイルに埋め込むプログラムを作成する必要がありました。 Javaで行うことにしました。

この記事では、この小さなプログラムがどのように作成されたかだけでなく、いくつかの理論的なポイントについて説明します。



理論部


GIF形式

GIF(Eng。Graphics Interchange Format-画像を交換するためのフォーマット)-圧縮されたデータを最大256色のフォーマットで品質を損なうことなく保存できるグラフィック画像を保存するためのフォーマット。 この形式は、ネットワーク経由でラスターイメージを送信するために、CompuServeによって1987年(GIF87a)に開発されました。 1989年、形式が変更され(GIF89a)、透明度とアニメーションのサポートが追加されました。

GIFファイルにはブロック構造があります。 これらのブロックは常に固定長である(またはいくつかのフラグに依存する)ため、どのブロックが配置されているかを間違えることはほとんど不可能です。 最も単純な非アニメーションGIF画像形式GIF89aの構造:



この場合のすべての構造ブロックのうち、グローバルパレットブロックと、パレットを担当するパラメーターに注目します。

大きさ色数パレットサイズ、バイト
7256768
6128384
564192
43296
31648
2824
1412
026


暗号化方法

画像ファイル内のメッセージを暗号化する方法として使用されます:

LSB法は、一般的なステガノグラフィ法です。 これは、コンテナの最後の重要なビット(この場合、グローバルパレットのバイト)を非表示にするメッセージのビットで置き換えることで構成されています。

プログラムは、このメソッドの一部として、グローバルパレットのバイト単位の最後の2ビットを使用します。 つまり、パレットの色が赤、青、緑の3バイトである24ビット画像の場合、メッセージが埋め込まれた後、各色成分は最大で3/255階調変化します。 そのような変化は、第一に、目に見えないか、人間の目に気付くのが困難であり、第二に、低品質の情報出力デバイスでは区別できません。

情報の量は、画像パレットのサイズに直接依存します。 パレットの最大サイズは256色であり、各色のコンポーネントにメッセージの2ビットを書き込む場合、メッセージの最大長(画像内の最大パレットを含む)は192バイトです。 画像にメッセージを埋め込んだ後、ファイルサイズは変わりません。

GIF構造に対してのみ機能するパレット拡張メソッド 。 小さなパレットの画像で最も効果的です。 その本質は、パレットのサイズを大きくし、それによって色のバイトの代わりに必要なバイトを記録するための追加のスペースを与えることです。 パレットの最小サイズが2色(6バイト)であると考える場合、埋め込みメッセージの最大サイズは256×3–6 = 762バイトになります。 欠点はセキュリティが低いことです。メッセージが追加の暗号化を受けていない場合は、テキストエディタを使用して埋め込みメッセージを読み取ることができます。

実用部


プログラム設計

暗号化および復号化アルゴリズムの実装に必要なすべてのツールは、 com.tsarik.steganographyパッケージにcom.tsarik.steganographyます。 このパッケージには、 encrypt decryptメソッドとdecryptメソッドを備えたEncryptorインターフェイス、ビット配列を操作する機能を提供するBinaryクラス、およびエンコードおよびデコードエラーの場合にそれぞれEncryptorインターフェイスメソッドで使用されるUnableToEncryptExceptionおよびUnableToDecryptException例外クラスが含まれます。

メインプログラムパッケージcom.tsarik.programs.gifedには、プログラムを実行できる静的mainメソッドで起動されるプログラムクラスが含まれます。 プログラムパラメータを格納するクラス。 他のクラスのパッケージ。

アルゴリズム自体の実装は、 GIFEncryptorByLSBMethodおよびGIFEncryptorByPaletteExtensionMethod com.tsarik.programs.gifed.gifパッケージcom.tsarik.programs.gifed.gif提示されGIFEncryptorByPaletteExtensionMethod 。 これらのクラスは両方ともEncryptorインターフェイスを実装します。

GIF形式の構造に基づいて、画像パレットにメッセージを埋め込むための一般的なアルゴリズムを作成できます。



画像内のメッセージの存在を判別するには、メッセージの先頭に特定のビットシーケンスを追加する必要があります。これは、デコーダーが最初に読み取り、正確性をチェックします。 一致しない場合は、画像に隠されたメッセージはないと見なされます。 次に、メッセージの長さを指定する必要があります。 次に、メッセージ自体のテキスト。

アプリケーション全体のクラス図:



プログラムの実施

プログラム全体の実装は、 GIFEncryptorByLSBMethodおよびGIFEncryptorByPaletteExtensionMethod Encryptorインターフェースの暗号化および復号化メソッドの実装、およびユーザーインターフェースの実装の2つのコンポーネントに分けることができます。

GIFEncryptorByLSBMethodクラスを検討してGIFEncryptorByLSBMethod



firstLSBitおよびsecondLSBitは、メッセージの入力先および送信元の画像の各バイトのビット番号が含まれます。 checkSequenceフィールドは、埋め込まれたメッセージの認識を保証するためにビットのチェックシーケンスを保持します。 静的メソッドgetEncryptingFileParametersは、指定されたファイルのパラメーターと潜在的なメッセージの特性を返します。

encryptクラスのencryptメソッドのアルゴリズム:



そしてそのコード:
 @Override public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException { if (in == null) { throw new NullPointerException("Input file is null"); } if (out == null) { throw new NullPointerException("Output file is null"); } if (text == null) { throw new NullPointerException("Text is null"); } // read bytes from input file byte[] bytes = new byte[(int)in.length()]; InputStream is = new FileInputStream(in); is.read(bytes); is.close(); // check format if (!(new String(bytes, 0, 6)).equals("GIF89a")) { throw new UnableToEncodeException("Input file has wrong GIF format"); } // read palette size property from first three bits in the 10-th byte from the file byte[] b10 = Binary.toBitArray(bytes[10]); byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]}); // calculate color count and possible message length int bOrigColorCount = (int)Math.pow(2, bsize+1); int possibleMessageLength = bOrigColorCount*3/4; int possibleTextLength = possibleMessageLength-2;// one byte for check and one byte for message length if (possibleTextLength < text.length()) { throw new UnableToEncodeException("Text is too big"); } int n = 13; // write check sequence for (int i = 0; i < checkSequence.length/2; i++) { byte[] ba = Binary.toBitArray(bytes[n]); ba[firstLSBit] = checkSequence[2*i]; ba[secondLSBit] = checkSequence[2*i+1]; bytes[n] = Binary.toByte(ba); n++; } // write text length byte[] cl = Binary.toBitArray((byte)text.length()); for (int i = 0; i < cl.length/2; i++) { byte[] ba = Binary.toBitArray(bytes[n]); ba[firstLSBit] = cl[2*i]; ba[secondLSBit] = cl[2*i+1]; bytes[n] = Binary.toByte(ba); n++; } // write message byte[] textBytes = text.getBytes(); for (int i = 0; i < textBytes.length; i++) { byte[] c = Binary.toBitArray(textBytes[i]); for (int ci = 0; ci < c.length/2; ci++) { byte[] ba = Binary.toBitArray(bytes[n]); ba[firstLSBit] = c[2*ci]; ba[secondLSBit] = c[2*ci+1]; bytes[n] = Binary.toByte(ba); n++; } } // write output file OutputStream os = new FileOutputStream(out); os.write(bytes); os.close(); } 


GIFEncryptorByLSBMethodクラスのdecryptメソッドのアルゴリズムとソースコード:



 @Override public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException { if (in == null) { throw new NullPointerException("Input file is null"); } // read bytes from input file byte[] bytes = new byte[(int)in.length()]; InputStream is = new FileInputStream(in); is.read(bytes); is.close(); // check format if (!(new String(bytes, 0, 6)).equals("GIF89a")) { throw new UnableToDecodeException("Input file has wrong GIF format"); } // read palette size property from first three bits in the 10-th byte from the file byte[] b10 = Binary.toBitArray(bytes[10]); byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]}); // calculate color count and possible message length int bOrigColorCount = (int)Math.pow(2, bsize+1); int possibleMessageLength = bOrigColorCount*3/4; int possibleTextLength = possibleMessageLength-2; // one byte for check and one byte for message length int n = 13; // read check sequence byte[] csBits = new byte[checkSequence.length]; for (int i = 0; i < 4; i++) { byte[] ba = Binary.toBitArray(bytes[n]); csBits[2*i] = ba[firstLSBit]; csBits[2*i+1] = ba[secondLSBit]; n++; } byte cs = Binary.toByte(csBits); if (cs != Binary.toByte(checkSequence)) { throw new UnableToDecodeException("There is no encrypted message in the image (Check sequence is incorrect)"); } // read text length byte[] cl = new byte[8]; for (int i = 0; i < 4; i++) { byte[] ba = Binary.toBitArray(bytes[n]); cl[2*i] = ba[firstLSBit]; cl[2*i+1] = ba[secondLSBit]; n++; } byte textLength = Binary.toByte(cl); if (textLength < 0) { throw new UnableToDecodeException("Decoded text length is less than 0"); } if (possibleTextLength < textLength) { throw new UnableToDecodeException("There is no messages (Decoded message length (" + textLength + ") is less than Possible message length (" + possibleTextLength + "))"); } // read text bits and make text bytes byte[] bt = new byte[textLength]; for (int i = 0; i < bt.length; i++) { byte[] bc = new byte[8]; for (int bci = 0; bci < bc.length/2; bci++) { byte[] ba = Binary.toBitArray(bytes[n]); bc[2*bci] = ba[firstLSBit]; bc[2*bci+1] = ba[secondLSBit]; n++; } bt[i] = Binary.toByte(bc); } return new String(bt); } 


GIFEncryptorByPaletteExtensionMethodクラスの実装も同様であり、情報の保存/読み取り方法のみが異なります。

MainFrameクラスは「ラッパー」 encryptImage(Encryptor encryptor)記述しますencryptImage(Encryptor encryptor)インターフェースメソッドの結果を処理し、ユーザーと対話する、つまりファイル選択ダイアログを開く、エラーメッセージを表示するなど、 decryptImage(Encryptor encryptor) encryptImage(Encryptor encryptor)およびdecryptImage(Encryptor encryptor)です。 ; 他のメソッド: openImage() 、ユーザーが画像を選択できるようにする、 exit() 、アプリケーションを終了する これらのメソッドは、対応するメニュー項目のアクションから呼び出されます。 このクラスでは、補助メソッドが追加で実装されますcreateComponents() -フォームコンポーネントの作成、 loadImageFile(File f) -ファイルから特別なコンポーネントに画像をロードします。 GIFEncryptorByPaletteExtensionMethodクラスの実装はGIFEncryptorByPaletteExtensionMethodクラスの実装に似ていますが、主な違いは、メッセージバイトがパレットで読み書きされる方法にあります。

プログラム作業


LBS法

そのような画像があるとしましょう:



この画像では、パレットは256色で構成されています(これがペイントの保存方法です)。 最初の4色:白、黒、赤、緑。 残りの色は黒です。 グローバルパレットのビットシーケンスは次のようになります。

111111 11 111111 11 111111 11 000 000 00 000 000 00 000 000 00 111111 11 000 000 00 000 000 00 000 000 00 111111 11 000 000 00 ...



メッセージの埋め込み後、下線付きのビットはメッセージのビットに置き換えられます。 結果の画像は、元の画像とほとんど変わりません。

オリジナルメッセージが埋め込まれた画像
オリジナルメッセージが埋め込まれた画像


パレット拡張方法

このメソッドを使用してメッセージが配置されている画像を開くと、次の画像を見つけることができます。



このような方法は本格的なスパイ活動では機能せず、追加のメッセージ暗号化が必要になる場合があることは明らかです。

アニメーション画像の暗号化/復号化は、通常の静的画像と同様に機能しますが、アニメーションは破損しません。

使用されたソース:


ダウンロード:

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


All Articles