Introduktion
Hej.
För inte sÄ lÀnge sedan, nÀr jag studerade vid universitetet, hade jag en uppsats i Àmnet "Programmeringsmetoder för informationsskydd". I uppgiften krÀvdes det att jag skulle göra ett program som integrerade meddelanden i filer av GIF-format. Jag bestÀmde mig för att anvÀnda Java.
I denna artikel kommer jag att beskriva vissa teoretiska punkter samt hur detta lilla program skapades.
Teoretisk Del
GIF-formatet
GIF (eng. Graphics Interchange Format â bildutbytesformat) Ă€r ett format för lagring av grafiska bilder som kan lagra komprimerad data utan kvalitetsförlust i ett format med upp till 256 fĂ€rger. Detta format utvecklades 1987 (GIF87a) av CompuServe för överföring av rasterbilder över nĂ€tverk. Ă r 1989 modifierades formatet (GIF89a) med tillĂ€gg av stöd för transparens och animation.
GIF-filer har en blockstruktur. Dessa block har alltid en fast lÀngd (eller den beror pÄ vissa flaggor), sÄ det Àr praktiskt taget omöjligt att göra fel i var ett block ligger. Strukturen av den enklaste icke-animerade GIF-bilden i GIF89a-formatet:

Av alla block i strukturen kommer vi i detta fall att vara intresserade av blocket för den globala palett och parametrarna som ansvarar för paletten:
CTâ nĂ€rvaro av en global palett. Om denna flagga Ă€r instĂ€lld, mĂ„ste den globala paletten börja omedelbart efter beskrivaren för den logiska skĂ€rmen.Sizeâ palettens storlek och antal fĂ€rger i bilden. VĂ€rden för denna parameter:
Size
Antal fÀrger
Palettstorlek, 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
Krypteringsmetoder
Som metoder för att kryptera meddelanden i bildfiler kommer följande att anvÀndas:
- LSB-metoden (Least Significant Bit, minst signifikanta biten)
- Palettkompletteringsmetoden
LSB-metoden â en vanlig metod för steganografi. Den gĂ„r ut pĂ„ att ersĂ€tta de sista signifikanta bitarna i containern (i vĂ„rt fall byte av den globala paletten) med bitarna av det dolda meddelandet.
Metoden kommer att anvÀnda de tvÄ sista bitarna i varje byte av den globala paletten. Det innebÀr att för en 24-bitars bild, dÀr fÀrgpaletten bestÄr av tre byte för röd, blÄ och grön, kommer varje fÀrgkomponent efter införandet av meddelandet att Àndras med högst 3/255 nyanser. SÄdan förÀndring kommer för det första att vara osynlig eller svÄr att upptÀcka för det mÀnskliga ögat och för det andra kommer den inte att vara mÀrkbar pÄ lÄgkvalitativa informationsutmatningsenheter.
MÀngden information kommer att bero direkt pÄ storleken av bildpaletten. Eftersom den maximala storleken pÄ paletten Àr 256 fÀrger, och om man skriver tvÄ bitar av meddelandet i varje fÀrgkomponent, kommer den maximala lÀngden pÄ meddelandet (vid maximal palett i bilden) att vara 192 byte. Filstorleken förÀndras inte efter att meddelandet har införts i bilden.
Palettutvidgningsmetoden, som endast fungerar för GIF-strukturen. Den kommer vara mest effektiv i bilder med smĂ„ paletter. Dess kĂ€rna Ă€r att den ökar palettens storlek, vilket ger mer utrymme för att skriva nödvĂ€ndiga byte pĂ„ platsen för fĂ€rgbyte. Med tanke pĂ„ att den minimi palettstorleken Ă€r 2 fĂ€rger (6 byte), kan den maximala storleken pĂ„ det infogade meddelandet vara 256Ă3â6=762 byte. Nackdelen Ă€r lĂ„g kryptografisk sĂ€kerhet; det infogade meddelandet kan lĂ€sas med vilken textredigerare som helst, om meddelandet inte har genomgĂ„tt ytterligare kryptering.
Praktisk del
Programdesign
Alla nödvÀndiga verktyg för implementeringen av krypterings- och dekrypteringsalgoritmerna kommer att finnas i paketet com.tsarik.steganography. Detta paket inkluderar grÀnssnittet Encryptor med metoderna encrypt och decrypt, klassen Binary, som ger möjlighet att arbeta med bitarrayer, samt undantagsklasserna UnableToEncryptException och UnableToDecryptException, som ska anvÀndas i grÀnssnittets metoder Encryptor vid kodnings- och avkodningsfel respektive.
Huvudpaketet för programmet com.tsarik.programs.gifed kommer att inkludera den körbara klassens program med den statiska metoden main, som möjliggör programstart; en klass som lagrar programmets parametrar; samt paket med andra klasser.
Implementeringen av algoritmerna presenteras i paketet com.tsarik.programs.gifed.gif klasserna GIFEncryptorByLSBMethod och GIFEncryptorByPaletteExtensionMethod. BÄda dessa klasser kommer att implementera grÀnssnittet Encryptor.
UtifrÄn strukturen i GIF-formatet kan en övergripande algoritm för att införa meddelanden i bildpaletten formuleras:

För att bestÀmma nÀrvaron av ett meddelande i bilden mÄste en viss sekvens av bitar lÀggas till början av meddelandet, som avkodaren lÀser först och kontrollerar för korrekthet. Om den inte stÀmmer överens, anses det att det inte finns nÄgot dolt meddelande i bilden. DÀrefter mÄste meddelandets lÀngd anges. Sedan följer sjÀlva meddelandetexten.
Klassdiagrammet för hela applikationen:

Programimplementeringen
Implementeringen av hela programmet kan delas upp i tvÄ delar: implementering av krypterings- och avkodningsmetoderna för grÀnssnittet Encryptor, i klasserna GIFEncryptorByLSBMethod och GIFEncryptorByPaletteExtensionMethod, och implementeringen av anvÀndargrÀnssnittet.
LÄt oss titta pÄ klassen GIFEncryptorByLSBMethod.

FÀlt firstLSBit och secondLSBit innehÄller numren pÄ bitarna av varje byte i bilden dÀr meddelandet ska skrivas in och lÀsas frÄn. FÀltet checkSequence lagrar kontrollsekvensen av bitar för att sÀkerstÀlla igenkÀnning av det inbÀddade meddelandet. Den statiska metoden getEncryptingFileParameters returnerar parametrarna för den angivna filen och egenskaperna för det potentiella meddelandet.
Algoritmen för metoden encrypt klassen GIFEncryptorByLSBMethod:

Och dess kod:
@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");
}
// lÀs byte frÄn inmatningsfil
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// kontrollera format
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToEncodeException("Inmatningsfil har fel GIF-format");
}
// lÀs palettstorleksegenskap frÄn de första tre bitarna i den 10:e byten frÄn filen
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// berÀkna antalet fÀrger och möjlig meddelandelÀngd
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // en byte för kontroll och en byte för meddelandelÀngd
if (possibleTextLength < text.length()) {
throw new UnableToEncodeException("Texten Àr för stor");
}
int n = 13;
// skriv kontrollsekvens
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++;
}
// skriv textlÀngd
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++;
}
// skriv meddelande
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++;
}
}
// skriv utfil
OutputStream os = new FileOutputStream(out);
os.write(bytes);
os.close();
}
Algoritm och kÀllkod för metoden decrypt klassen GIFEncryptorByLSBMethod:

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Inmatfilen Àr null");
}
// lÀser byte frÄn inmatningsfil
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// kontrollerar format
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToDecodeException("Inmatfilen har fel GIF-format");
}
// lÀser palettens storhetsparameter frÄn de första tre bitarna i den 10:e byten frÄn filen
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// berÀknar fÀrgantal och möjlig meddelandelÀngd
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // en byte för kontroll och en byte för meddelandelÀngd
int n = 13;
// lÀser kontrollsekvens
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("Det finns inget krypterat meddelande i bilden (Kontrollsekvensen Àr felaktig)");
}
// lÀser textlÀngd
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("Dekodad textlÀngd Àr mindre Àn 0");
}
if (possibleTextLength < textLength) {
throw new UnableToDecodeException("Det finns inga meddelanden (Dekodad meddelandelÀngd (" + textLength + ") Àr mindre Àn möjlig meddelandelÀngd (" + possibleTextLength + "))");
}
// lÀser textbitar och gör textbytes
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);
}
Implementeringen av klassen GIFEncryptorByPaletteExtensionMethod kommer att vara liknande, skillnaden ligger i metoden för att spara/lÀsa information.
I klassen MainFrame beskrivs metoderna-"wrapper": encryptImage(Encryptor encryptor) och decryptImage(Encryptor encryptor), som bearbetar resultaten av grĂ€nssnittets metoder Encryptor och interagerar med anvĂ€ndaren, dvs öppnar dialoger för filval, visar felmeddelanden osv.; samt andra metoder: openImage(), som ger anvĂ€ndaren möjlighet att vĂ€lja en bild, exit(), som möjliggör utloggning frĂ„n applikationen. Dessa metoder anropas frĂ„n Action's motsvarande menyval. I denna klass har dessutom hjĂ€lpfunktioner implementerats: createComponents() â skapande av formulĂ€rkomponenter, loadImageFile(File f) â laddar upp en bild i en speciell komponent frĂ„n fil. Implementeringen av klassen GIFEncryptorByPaletteExtensionMethod Ă€r liknande implementeringen av klassen GIFEncryptorByLSBMethod, den största skillnaden ligger i hur bytes av meddelandet skrivs och lĂ€ses frĂ„n paletten.
Programmets arbete
Metod LBS
Anta att det finns en sÄdan bild:

I denna bild bestĂ„r paletten av 256 fĂ€rger (som Paint sparar). De första fyra fĂ€rgerna: vit, svart, röd, grön. Ăvriga fĂ€rger Ă€r svarta. Sekvensen av bitar i den globala paletten blir följande:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000âŠ

Efter att meddelandet har införts kommer de understrukna bitarna att ersÀttas av bitarna frÄn meddelandet. Den resulterande bilden skiljer sig nÀstan inte frÄn originalet.
Original
Bild med infört meddelande

![]()
Palettutvidgningsmetoden
NÀr man öppnar bilden, dÀr meddelandet har placerats med denna metod, kan man upptÀcka en sÄdan bild:

SjÀlvklart Àr denna metod inte lÀmplig för fullstÀndig spionageverksamhet och krÀver kanske ytterligare kryptering av meddelandet.
Kryptering/avkryptering i animerade bilder fungerar som i vanliga statiska bilder, och animationen störs inte.
AnvÀnda kÀllor:
Ladda ner:
KĂ€lla: habr.com
