NÀr vi talar om steganografi, förestÀller sig folk terrorister, pedofiler, spioner, eller i bÀsta fall kryptoanarkister och andra forskare. Och det Àr sant, vem skulle annars behöva gömma nÄgonting frÄn yttre ögon? Vad kan egentligen en vanlig mÀnniska ha för nytta av detta?
Det visar sig att det finns en viss nytta. Just dÀrför kommer vi idag att komprimera data med hjÀlp av steganografiska metoder. I slutet kommer lÀsaren till och med att kunna anvÀnda sina vÀrdefulla JPEG-fotografier för att öka antalet lediga gigabyte pÄ filsystemet.

Vad?
Om lÀsaren minns, sÄ Àr steganografi en slags mÀrkliga algoritmer som möjliggör att gömma en informations nÀrvaro inom en annan. Med enklare ord: bild + fil == ungefÀr samma bild, men inte riktigt (det kan vara vad som helst istÀllet för bilder, men de Àr oftast lÀttare att förstÄ). Samtidigt ska det inte finnas nÄgot enkelt sÀtt att avgöra om det finns nÄgot inuti eller inte.
Men om det inte gÄr att sÀrskilja det ena frÄn det andra, finns det dÄ nÄgon skillnad? Ur ett konsumentperspektiv bryr sig inte anvÀndaren om matematisk noggrannhet (reflekterad av en specifik uppsÀttning bitar), utan enbart om vad de uppfattar.
Till exempel, lÄt oss titta pÄ tre bilder av en söt hund:
Varning, JPEG!

Trots den kolossala skillnaden i storlek Àr det fÄ som skulle vÀlja den tredje versionen. à andra sidan Àr skillnaden mellan de tvÄ första fotografierna inte sÄ mÀrkbar, och mÀngden information i dem (enligt min Äsikt) kan likstÀllas.
SjĂ€lva denna princip Ă€r lĂ€nge gammal och har i mĂ„nga Ă„r aktivt utnyttjats i metoder för informationstryck med förlust. Men att bryta ner Ă€r inte att bygga, vi Ă€r intresserade av en mer avancerad sida av frĂ„gan. Ăr det möjligt att integrera extra information av storlek N i en fil sĂ„ att dess storlek ökar med M < N, och att Ă€ndringar inte mĂ€rks av anvĂ€ndaren?
SjÀlvklart Àr det möjligt. Men det Àr vÀrt att göra nÄgra förbehÄll direkt:
- För det första mÄste metoden vara universell och ge ett positivt resultat pÄ de flesta ingÄngar. Det vill sÀga, i genomsnitt bör det ske en faktisk minskning av den lagrade mÀngden information för godtycklig ingÄng. "I genomsnitt" innebÀr att motsatta fall kan förekomma, men de bör inte dominera.
- För det andra ska storleken pÄ den komprimerade containern innan informationen integreras vara större Àn den komprimerade motsvarigheten av den modifieringen. Att bara integrera en massa bitar i BMP-bilder med LSB-metoden Àr inte steganografisk komprimering, eftersom den ursprungliga bilden troligen kommer att vara betydligt mindre efter att ha körts genom nÄgon DEFLATE.
- För det tredje mÄste resultaten jÀmföras med redan komprimerade data som anvÀnts med klassiska metoder. Detta gör det möjligt att eliminera den probabilistiska effekten av deras överflöd och genomföra en mer effektiv komprimering i allmÀnhet.
Var?
AnvÀndningen av steganografi innebÀr att vi, utöver den information som ska komprimeras, behöver containrar dÀr den kommer att integreras. Den maximala mÀngden information som kan integreras beror till stor del pÄ specifika egenskaper, men ju fler det finns, desto enklare gÄr det att skala. DÀrför bör formatet pÄ containrarna vara allmÀnt, sÄ att anvÀndaren har tillrÀckligt mÄnga för att fÄ nÄgon nytta av "komprimeringsprocessen".
I det hÀr sammanhanget Àr grafiska, ljud- och videofiler goda kandidater. Men pÄ grund av mÄngfalden av olika format och codecs kvarstÄr praktiskt taget ett ganska begrÀnsat antal alternativ att vÀlja mellan.
Med detta i Ätanke har jag valt JPEG. Det anvÀnds av nÀstan alla, och Àr mycket vanligt bÄde för privata och affÀrsmÀssiga ÀndamÄl, och har blivit nÀstan de facto-formatet för de flesta bilder.

H̶u̶r̶?
Sedan följer omkringliggande och tekniska diagram och beskrivningar utan nÄgra större förklaringar, sÄ de som vill kan hoppa över dem och gÄ vidare till avsnittet "Högteknologi".
Gemensamma drag
För att integrera data nÄgonstans mÄste man först bestÀmma var. Det kan finnas hur mÄnga olika fotografier som helst pÄ filsystemet, bland vilka anvÀndaren kanske bara vill anvÀnda nÄgra. Den uppsÀttning av containrar som önskas kommer vi att kalla bibliotek.
Det bildas i tvÄ fall: före komprimering och före dekomprimering. I det första fallet kan man enkelt anvÀnda en uppsÀttning filnamn (eller bÀttre, ett reguljÀrt uttryck för dem), men i det andra fallet krÀvs nÄgot mer pÄlitligt: anvÀndaren kan kopiera och flytta dem inom filsystemet, vilket gör det svÄrt att korrekt identifiera dem. DÀrför behöver man lagra deras hash (md5 rÀcker) efter att alla modifikationer har genomförts.
Inledande sökningar med reguljÀra uttryck Àr meningslösa att utföra pÄ hela filsystemet, det rÀcker att ange en viss rotmapp. Dit kommer en speciell arkivfil att sparas, som kommer att innehÄlla dessa hashvÀrden, tillsammans med annan metainformation som Àr nödvÀndig för framtida ÄterstÀllning av den komprimerade informationen.
Allt detta Àr lika tillÀmpligt pÄ vilken implementering som helst av nÄgot steganografiskt datakomprimeringsalgoritm. SjÀlva processerna för komprimering och ÄterstÀllande av data kan kallas paketering och unpacking.
F5
Nu nÀr det Àr klart vad vi gör och varför, Àr det dags att beskriva sjÀlva algoritmen för att uppnÄ mÄlet. LÄt oss pÄminna oss om processen för kodning av en JPEG-fil (tack Wikipedia för Bauman National Library):

NÀr man ser pÄ den, Àr det bra att omedelbart göra nÄgra anmÀrkningar:
- Storleken pÄ en JPEG-fil kan anses vara optimal, Àven utan att försöka komprimera den med nÄgot WinRAR;
- Det som kan Àndras Àr endast den lagrade informationen (den som Àr output frÄn den diskreta cosinustransformationen, DCT), för att sÀkerstÀlla en acceptabel prestanda.
- För att inte förlora data i mÀrkbara anvÀndarindustrier krÀvs det att göra minimiÀndringar i varje enskild bild;
Under sÄdana förhÄllanden finns det en hel familj algoritmer som man kan bekanta sig med . Den mest avancerade av dem Àr algoritmen av Andreas Westfeld, som arbetar med DCT-koefficienterna för ljusstyrkekomponenten (den mÀnskliga ögat Àr minst kÀnsligt för just dessa förÀndringar). Dess allmÀnna schema nÀr det gÀller att arbeta med en existerande JPEG-fil visas med följande diagram:

Block F5 anvÀnder en avancerad inbÀddningsteknik baserad pÄ matrisprogrammering. Mer om det och sjÀlva algoritmen kan lÀsaren ta del av via lÀnken ovan, men vi Àr frÀmst intresserade av det faktum att fÀrre Àndringar kan göras med hjÀlp av den ju större storlek pÄ den anvÀnda containern, och för att genomföra sjÀlva algoritmen krÀvs endast enkla (de)kodningsoperationer av Huffman och RLE.
SjÀlva Àndringarna görs pÄ heltalskoefficienterna och handlar om att minska deras absolutvÀrde med ett, vilket gör det möjligt att anvÀnda F5 för datakomprimering. Anledningen Àr att en minskad koefficient i absolutvÀrde troligtvis kommer att ta upp fÀrre bitar efter Huffman-kodning pÄ grund av den statistiska fördelningen av vÀrden i JPEG.

Vid bildandet av nollor (sÄ kallade förkortningar) kommer den lagrade informationen att minska med dess storlek, eftersom den tidigare oberoende koefficienten blir en del av den kodade RLE-sekvensen av nollor:

Modifieringar
Dataskydd och komprimering Àr ortogonala uppgifter, sÄ den hemliga lösenordspermutationen frÄn den ursprungliga algoritmen kan ignoreras. Dessutom behöver vi exakt veta hur man extraherar data, sÄ all nödvÀndig information (vilka containrar som anvÀndes, i vilken ordning osv.) bör skrivas ner i en separat fil och vara tillgÀnglig för fri lÀsning av arkivprogrammet.
Den ursprungliga algoritmen Àr avsedd för överföring av hemliga meddelanden, sÄ den arbetar endast med en container Ät gÄngen, och förutsÀtter att anvÀndaren sjÀlv kommer att dela upp den i delar om behovet uppstÄr. Dessutom, vid oberoende inbÀddning i varje container kommer det krÀvas att man i förvÀg vet hur mÄnga bitar data som ska placeras i varje. DÀrför Àr det klokt att sammanfoga koefficienterna för varje element i biblioteket till en stor abstrakt och arbeta med den enligt den ursprungliga algoritmen.
Eftersom den ursprungliga F5 tillÄter anvÀndning av upp till 12% av containerns storlek kommer en sÄdan modifiering ocksÄ att öka den maximala kapaciteten: "upp till 12%" av hela bibliotekets storlek Àr större Àn eller lika med summan av "upp till 12%" av vart och ett av dess element.
Den kodade övergripande schemat ser ut som följer:

Algoritmen
Nu Àr det dags att beskriva sjÀlva algoritmen frÄn början till slut, sÄ att lÀsaren inte hÄlls i okunnighet:
- AnvÀndaren definierar de binÀra komprimerbara data M och biblioteket L med hjÀlp av reguljÀra uttryck och den sökande rotkatalogen;
- I ordning pÄ FS bildar bibliotekelementen MC:
- FrÄn filens data dekodas en serie koefficienter C;
- MC <- MC | C;
- Parametern k definieras utifrÄn det fruktansvÀrda olikhetsvillkoret:
|M| * 8 / (count_full(MC) + count_ones(MC) * k_rate(k)) < k / ((1 << k) - 1); - Det tas successivt
n = (1 << k) - 1av de lÀgsta bitarna av de icke-noll element frÄn MC och skrivs ner ia:- Den magiska hashfunktionen
f, som avbildar ett n-bitar ordatill ett k-bitars; - Om
s == 0, betyder att ingen Àndring behövs och algoritmen gÄr vidare till nÀsta koefficienter; - Minska det absoluta vÀrdet av koefficienten som ansvarar för
s-e biten i ordeta; - Om minskningen resulterade i en förkortning (koefficienten blev 0), upprepa steget frÄn början;
- Den magiska hashfunktionen
- Alla koefficienter kodas med RLE och Huffman, skrivs till de ursprungliga filerna;
- Parametern k skrivs till arkivfilen;
- För varje fil L, i ordning av deras ursprungliga placering, berÀknas en MD5-hash och skrivs till arkivfilen.
Högteknologier
En naiv form av algoritmen och implementeringen pÄ andra högre nivÄer (sÀrskilt de med skrÀpsamling) skulle ge förfÀrlig prestanda, sÄ jag implementerade alla dessa komplexiteter i rent C och genomförde en rad optimeringar bÄde för exekveringshastighet och minnesanvÀndning (ni kan inte förestÀlla er hur mycket dessa bilder vÀger utan komprimering, Àven till DCT). Men Àven dÄ lÀmnade exekveringshastigheten mycket att önska i början, sÄ jag kommer inte att beskriva hela processen och de anvÀnda metoderna.
Korsplattformsenhet har uppnÄtts genom att anvÀnda en kombination av biblioteken libjpeg, pcre och tinydir, varav jag tackar dem. Som standard kompileras allt genom en vanlig make, sÄ Windows-anvÀndare vill installera Cygwin eller hantera Visual Studio och biblioteken sjÀlva.
Implementeringen Àr tillgÀnglig som ett kommandoradsverktyg och ett bibliotek. De som vill veta mer om hur man anvÀnder det senaste kan lÀsa README-filen i repositoryt pÄ GitHub, lÀnken till vilken jag kommer att bifoga i slutet av inlÀgget. LÄt oss hÀr gÄ vidare till beskrivningen och demonstration av funktionaliteten.
Hur man anvÀnder?
Var försiktig. AnvÀnda bilder kan flyttas, döpas om och kopieras efter önskemÄl. Dock bör man vara oerhört försiktig och inte pÄ nÄgot sÀtt förÀndra innehÄllet. Att Àndra en enda bit kommer att leda till att hashen bryts och informationen inte kan ÄterstÀllas.
LÄt oss sÀga att vi efter kompileringen har fÄtt den körbara filen f5ar. Vi kan analysera storleken pÄ biblioteket för att berÀkna dess anvÀndningsmöjligheter med hjÀlp av flaggan -a: . /f5ar -a [sökmapp] [Perl-kompatibelt reguljÀrt uttryck]. Packningen utförs med kommandot . /f5ar -p [sökmapp] [Perl-kompatibelt reguljÀrt uttryck] [packad fil] [arkivnamn], och uppackning sker med hjÀlp av . /f5ar -u [arkivfil] [ÄterstÀllt filnamn].
Demonstration av funktionen.
För att visa metodens effektivitet har jag laddat upp en samling av 225 helt gratis hundbilder frÄn tjÀnsten . Varje bild har nÄgot högre kvalitet Àn vanliga anvÀndarfotografier, men ÀndÄ. Varje bild har omkodats med hjÀlp av libjpeg för att minska pÄverkan av bibliotekets kodningsspecifikationer pÄ den totala storleken. För att exemplifiera det sÀmsta exemplet pÄ komprimerade data har en slumpmÀssig fil pÄ 36 meter (lite mer Àn 5 % av den totala storleken) skapats med jÀmn fördelning med hjÀlp av dd.
Testprocessen Àr ganska enkel:
$ ls
binary_data dogs f5ar
$ du -sh dogs/
633M dogs/
$ du -h binary_data
36M binary_data
$ ./f5ar -p dogs/ .*jpg binary_data dogs.f5ar
LĂ€ser komprimeringsfil... okej
Initierar arkivet... okej
Analyserar bibliotekets kapacitet... klart pÄ 16.8s
UpptÀckte nÄgot guaranteed kapacitet pÄ 48439359 byte
UpptÀckte möjlig kapacitet pÄ upp till 102618787 byte
Komprimerar... klart pÄ 32.6s
Sparar arkivet... okej
$ ./f5ar -u dogs/dogs.f5ar unpacked
Initierar arkivet... okej
LĂ€ser arkivfilen... okej
Fyller arkivet med filer... klart pÄ 1.2s
Avkomprimerar... klart pÄ 17.5s
Skriver ut data... okej
$ sha1sum binary_data unpacked
ba7ade4bc77881ab463121e77bbd4d41ee181ae9 binary_data
ba7ade4bc77881ab463121e77bbd4d41ee181ae9 unpacked
$ du -sh dogs/
563M dogs/Eller med en skÀrmdump för entusiasterna.

Som det framgÄr, har vi frÄn de ursprungliga 633 + 36 == 669 megabyte data pÄ hÄrddisken kommit ner till mer tilltalande 563, vilket ger oss en komprimeringskoefficient pÄ ~1,188. Denna radikala skillnad förklaras av extremt smÄ förluster, liknande dem som uppnÄs vid optimering av JPEG-filer med klassiska metoder (som tinyjpg). Naturligtvis, med hjÀlp av steganografisk komprimering förloras informationen inte bara utan anvÀnds för att koda andra data. Dessutom Àr antalet "optimerade" koefficienter genom anvÀndning av F5 mycket mindre Àn vid traditionell optimering.
Oavsett vilka modifieringar som görs, Àr de helt osynliga för ögat. Under spoileravsnittet nedan kan lÀsaren bedöma skillnaden bÄde visuellt och genom att subtrahera vÀrdena för den Àndrade komponenten frÄn originalet (ju mer dÀmpad fÀrgen Àr, desto mindre skillnad):
LÀnkar till bilder som inte fick plats pÄ habrastorage
Original â
Modifierad â
Skillnad â
Avslutande
Jag hoppas att jag har kunnat övertyga lÀsaren om att sÄdana metoder Àr möjliga och har rÀtt att existera. Samtidigt kan det verka mycket enklare att köpa en hÄrddisk eller en extra kanal (för nÀtverksöverföring) Àn att försöka spara pÄ det hÀr sÀttet. à ena sidan Àr det verkligen sÄ, att extensiv utveckling ofta Àr enklare och mer pÄlitlig. Men Ä andra sidan bör man inte glömma den intensiva aspekten. Det finns ju inga garantier för att man i morgon kan gÄ till affÀren och köpa en ny hÄrddisk pÄ tusen terabyte, men de som redan finns hemma kan alltid anvÀndas.
->
KĂ€lla: habr.com
