Bevezetés
Üdvözlöm.
Nem rég, amikor az egyetemen tanultam, írtam egy dolgozatot az "Információvédelmi szoftveres módszerek" tantárgyból. A feladat egy olyan program készítése volt, amely üzenetet ágyaz be GIF formátumú fájlokba. Java nyelven döntöttem a megvalósítás mellett.
A cikkben néhány elméleti szempontot ismertetek, valamint azt, hogyan készült el ez a kis program.
Elméleti rész
GIF formátum
A GIF (angolul Graphics Interchange Format — képek cseréjére szolgáló formátum) egy grafikus képek tárolására szolgáló formátum, amely képes tömörített adatokat tárolni, minőségromlás nélkül, legfeljebb 256 színig. Ezt a formátumot 1987-ben (GIF87a) a CompuServe fejlesztette ki, hogy raszteres képeket továbbíthasson a hálózatokon. 1989-ben a formátumot módosították (GIF89a), hozzáadták az átlátszóság és animáció támogatását.
A GIF formátumú fájlok blokkszerkezetet alkalmaznak. Ezek a blokkok mindig rögzített hosszúságúak (vagy a bizonyos flagoktól függnek), így szinte lehetetlen hibázni a blokkok elhelyezkedésével kapcsolatban. A legegyszerűbb nem animált GIF-kép struktúrája GIF89a formátumban:

A szerkezet összes blokkjából minket most a globális paletta és a palettát irányító paraméterek érdekelnek:
CT— globális paletta megléte. Ha ez a flag be van állítva, akkor a logikai képernyő leíróját követően azonnal a globális palettának kell kezdődnie.Méret— a paletta mérete és a kép színeinek száma. E paraméter értékei:
Méret
Színek száma
A paletta mérete, byte
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Titkosítási módszerek
A képfájlokban lévő üzenetek titkosításához a következő módszereket fogjuk használni:
- LSB módszer (Legkisebb Jelentős Bit)
- Paletta kiegészítési módszer
LSB módszer — a legelterjedtebb steganográfiai módszer. Ez abban rejlik, hogy a konténer utolsó jelentős biteit (esetünkben a globális paletta byte-jait) a rejtett üzenet biteivel helyettesítjük.
A program will use the last two bits in the bytes of the global palette within this method. This means that for a 24-bit image, where the color palette consists of three bytes for red, blue, and green colors, after the message is embedded, each color component will change by a maximum of 3/255 gradations. Such a change will, firstly, be unnoticed or hard to notice by the human eye, and secondly, will not be distinguishable on low-quality output devices.
The amount of information will directly depend on the size of the image palette. Since the maximum size of the palette is 256 colors, and if two message bits are recorded in each color component, then the maximum length of the message (with the maximum palette in the image) is 192 bytes. After embedding the message in the image, the file size does not change.
Palette Expansion Method, which works only for the GIF structure. It will be most effective in images with small palette sizes. Its essence is that it increases the palette size, thus providing additional space for writing the necessary bytes in place of the color bytes. Considering that the minimum palette size is 2 colors (6 bytes), the maximum size of the embedded message can be 256×3–6=762 bytes. A drawback is low cryptographic protection; the embedded message can be read using any text editor if it has not undergone additional encryption.
Practical Part
Program Design
All necessary tools for implementing the encryption and decryption algorithms will be located in the package com.tsarik.steganography. This package includes the interface Encryptor with the methods encrypt és decrypt, the class Binary, providing the ability to work with arrays of bits, as well as exception classes UnableToEncryptException és UnableToDecryptException, which should be used in the interface methods Encryptor in case of encoding and decoding errors, respectively.
The main package of the program com.tsarik.programs.gifed will include the executable class of the program with a static method main, allowing the program to be launched; a class that stores the program parameters; and packages with other classes.
A végrehajtás magukat az algoritmusokat a csomagban fogja bemutatni com.tsarik.programs.gifed.gif osztályokban GIFEncryptorByLSBMethod és GIFEncryptorByPaletteExtensionMethod. Mindkét osztály meg fogja valósítani a felületet Encryptor.
A GIF formátum szerkezete alapján általános algoritmust lehet kidolgozni az üzenet képként való elrejtésére:

Az üzenet meglétének meghatározásához az üzenet elejére egy bit sorozatot kell hozzáadni, amelyet a dekódoló elsőként olvas el, és ellenőrzi a helyességet. Ha ez nem egyezik, akkor azt feltételezzük, hogy a képben nincs elrejtett üzenet. Ezután meg kell adni az üzenet hosszát. Majd az üzenet szövegét.
Az alkalmazás teljes osztálydiagramja:

A program végrehajtása
A teljes program végrehajtását két összetevőre lehet bontani: a titkosítási és dekódolási módszerek megvalósítására Encryptor, az osztályokban GIFEncryptorByLSBMethod és GIFEncryptorByPaletteExtensionMethod, és a felhasználói felület megvalósítására.
Nézzük meg az osztályt GIFEncryptorByLSBMethod.

Mezők firstLSBit és secondLSBit tartalmazza a bit számokat minden képkocka byte-jában, ahová az üzenetet be kell írni és ahonnan olvasni kell. A mező checkSequence tárolja a beágyazott üzenet észleléséhez szükséges bit ellenőrző sorozatát. A statikus módszer getEncryptingFileParameters visszaadja a megadott fájl paramétereit és a potenciális üzenet jellemzőit.
A módszer algoritmusa encrypt kapcsolat és csatornák GIFEncryptorByLSBMethod:

És annak kódja:
@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("A bemeneti fájl null");
}
if (out == null) {
throw new NullPointerException("A kimeneti fájl null");
}
if (text == null) {
throw new NullPointerException("A szöveg null");
}
// olvassuk be a bájtokat a bemeneti fájlból
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// ellenőrizzük a formátumot
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToEncodeException("A bemeneti fájl hibás GIF formátumú");
}
// olvassuk be a paletta méret tulajdonságát a 10. bájt első három bitjéből
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// számítsuk ki a színszámot és a lehetséges üzenet hosszát
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // egy bájt a ellenőrzésre és egy bájt az üzenet hosszára
if (possibleTextLength < text.length()) {
throw new UnableToEncodeException("A szöveg túl hosszú");
}
int n = 13;
// írjuk be az ellenőrző sorozatot
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++;
}
// írjuk be a szöveg hosszát
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++;
}
// írjuk be az üzenetet
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++;
}
}
// írjuk ki a kimeneti fájlt
OutputStream os = new FileOutputStream(out);
os.write(bytes);
os.close();
}
Algoritmus és a módszer forráskódja decrypt kapcsolat és csatornák GIFEncryptorByLSBMethod:

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("A bemeneti fájl null");
}
// bájtok olvasása a bemeneti fájlból
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// formátum ellenőrzése
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToDecodeException("A bemeneti fájl formátuma hibás");
}
// a paletta méretének tulajdonságának olvasása a 10. bájt első három bitjéből
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// a színek számának és a lehetséges üzenethossz kiszámítása
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // egy bájt a ellenőrzésnek és egy bájt az üzenet hosszának
int n = 13;
// ellenőrző sorozat olvasása
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("Nincs titkosított üzenet a képen (Az ellenőrző sorozat hibás)");
}
// szöveghossz olvasása
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("A dekódolt szöveghossz kisebb, mint 0");
}
if (possibleTextLength < textLength) {
throw new UnableToDecodeException("Nincsenek üzenetek (A dekódolt üzenet hossza (" + textLength + ") kisebb, mint a lehetséges üzenethossz (" + possibleTextLength + "))");
}
// szövegbitek olvasása és szövegbájtok létrehozása
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);
}
A osztály megvalósítása GIFEncryptorByPaletteExtensionMethod hasonló lesz, csak a mentési/kiolvasási módszer eltér.
Az osztályban MainFrame a "wrapper" módszereket írja le: encryptImage(Encryptor encryptor) és decryptImage(Encryptor encryptor), amelyek feldolgozzák az interfész módszereinek eredményeit, Encryptor és lehetővé teszik a felhasználói interakciót, pl. fájlok kiválasztására nyit diákot, hibaüzeneteket mutat be stb.; valamint más módszereket is tartalmaz: openImage(), amely lehetőséget ad a felhasználónak egy kép kiválasztására, exit(), amely lehetővé teszi a programból való kilépést. Ezeket a módszereket a Akciómenüpontok megfelelő elemeiből hívják meg. Ebben az osztályban további segédmódszerek is vannak: createComponents() — a form komponenseinek létrehozása, loadImageFile(File f) — kép betöltése egy speciális komponensbe fájlból. Az osztály megvalósítása GIFEncryptorByPaletteExtensionMethod hasonló az osztály megvalósításához GIFEncryptorByLSBMethod, a fő különbség a üzenet bájtjainak a palettából való írása és olvasása módjában rejlik.
A program működése
LBS metódus
Tegyük fel, hogy van egy ilyen kép:

A képen 256 szín látható (így tárolja a Paint). Az első négy szín: fehér, fekete, piros, zöld. A többi szín fekete. A globális paletta bitjeinek sorrendje a következő lesz:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000…

Az üzenet beillesztése után a kiemelt bitek az üzenet bitjeivel lesznek helyettesítve. A kapott kép szinte alig különbözik az eredetitől.
Eredeti
Üzenetet tartalmazó kép

![]()
Palette Expansion Method
Ha megnyitunk egy olyan képet, amelybe üzenetet helyeztek el e módszer szerint, egy ilyen látvány fogad:

Egyértelmű, hogy ilyen módszerrel a teljes értékű kémkedés nem lehetséges, és esetleg további üzenet titkosítást igényel.
A titkosítás/dekódolás animált képekben ugyanúgy működik, mint a normális statikus képekben, és az animáció nem zavartatik meg.
Használt források:
Letöltés:
Forrás: habr.com
