Två gånger i månaden besöker vi några av våra läsares favoritinlägg från hela Activetuts + historia. Denna handledning publicerades först i februari 2010.
I denna handledning kommer jag att visa en teknik som jag använder för att skydda kod och tillgångar mot stöld.
Dekompilatorer är en verklig oro för människor som skapar Flash-innehåll. Du kan göra mycket för att skapa det bästa spelet där ute, då kan någon stjäla den, byta ut logotypen och lägga den på sin webbplats utan att fråga dig. På vilket sätt? Använda en Flash Decompiler. Om du inte lägger något skydd över din SWF kan det dekompileras med ett tryck på en knapp och dekompilatorn kommer att mata ut läsbar källkod.
Jag använde ett litet projekt av mig för att visa hur sårbara SWF: er är att dekompilera. Du kan ladda ner det och testa dig själv via länken ovan. Jag använde Sothink SWF Decompiler 5 för att dekompilera SWF och titta under huven. Koden är ganska läsbar och du kan förstå och återanvända den ganska enkelt.
Jag kom fram med en teknik för att skydda SWFs från dekompilatorer och jag kommer att demonstrera det i denna handledning. Vi borde kunna producera detta:
Koden som dekompileras är faktiskt koden för dekryptering av innehållet och har inget att göra med din huvudkod. Dessutom är namnen olagliga så det kommer inte att kompilera tillbaka. Försök att dekompilera det själv.
Innan vi går, vill jag påpeka att denna handledning inte är lämplig för nybörjare och du borde ha gedigen kunskap om AS3 om du vill följa med. Denna handledning handlar också om programmering med låg nivå som innefattar byte, ByteArrays och manipulering av SWF-filer med en hex-redigerare.
Här är vad vi behöver:
Öppna ett nytt ActionScript 3.0-projekt och sätt det för att kompilera med Flex SDK (jag använder FlashDevelop för att skriva kod). Välj en SWF du vill skydda och bädda in som binär data med hjälp av Embed taggen:
[Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream")] // source = sökväg till swf du vill skydda privata varinnehåll: Klass;
Nu är SWF inbäddad som en Bytearray in i lastaren SWF och den kan laddas genom Loader.loadBytes ().
var loader: Loader = ny Loader (); addChild (loader); loader.loadBytes (nytt innehåll (), ny LoaderContext (falskt, nytt ApplicationDomain ()));
Till slut borde vi ha den här koden:
paket importera flash.display.Loader; importera flash.display.Sprite; importera flash.system.ApplicationDomain; importera flash.system.LoaderContext; [SWF (bredd = 640, höjd = 423)] // måtten ska vara samma som den laddade swf: s allmänna klass. Huvudet sträcker Sprite [Bädda in (source = "VerletCloth.swf", mimeType = "application / octet stream") ] // source = sökväg till swf du vill skydda privat var innehåll: klass; allmän funktion Main (): void var loader: Loader = new Loader (); addChild (loader); loader.loadBytes (nytt innehåll (), ny LoaderContext (falskt, nytt ApplicationDomain ()));
Kompilera och se om det fungerar (det borde). Från och med nu kommer jag att ringa den inbäddade SWF: den "skyddade SWF", och SWFen har vi bara sammanställt "Loading SWF".
Låt oss försöka dekompilera och se om det fungerar.
Yey! Tillgångarna och den ursprungliga koden är borta! Vad som nu visas är koden som laddar den skyddade SWF och inte dess innehåll. Detta skulle förmodligen stoppa de flesta av de första angriparna som inte är bekanta med Flash men det är fortfarande inte tillräckligt bra för att skydda ditt arbete från skickliga angripare eftersom den skyddade SWF väntar på dem orörda inuti lastningen SWF.
Låt oss öppna SWF-filen med en hex-redigerare:
Det ska se ut som slumpmässig binär data eftersom den är komprimerad och den bör börja med ASCII "CWS". Vi måste dekomprimera det! (Om din SWF börjar med "FWS" och du ser meningsfulla strängar i SWF är det troligt att det inte komprimerades. Du måste aktivera komprimering att följa med).
Först kan det låta svårt men det är det inte. SWF-formatet är ett öppet format och det finns ett dokument som beskriver det. Hämta det från adobe.com och bläddra ner till sidan 25 i dokumentet. Det finns en beskrivning av rubriken och hur SWF komprimeras, så vi kan enkelt komprimera det.
Det som skrivs där är att de första 3 byte är en signatur (CWS eller FWS), nästa byte är Flash-versionen, nästa 4 byte är storleken på SWF. Återstående komprimeras om signaturen är CWS eller okomprimerad om signaturen är FWS. Låt oss skriva en enkel funktion för att dekomprimera en SWF:
privat funktion dekomprimera (data: ByteArray): ByteArray varrubrik: ByteArray = ny ByteArray (); var komprimerad: ByteArray = ny ByteArray (); var dekomprimerad: ByteArray = ny ByteArray (); header.writeBytes (data, 3, 5); // läs den okomprimerade rubriken, med undantag för signatur compressed.writeBytes (data, 8); // läs resten, komprimerad compressed.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // markera som okomprimerade decompressed.writeBytes (header); // skriv rubriken tillbaka decompressed.writeBytes (compressed); // skriv den nu okomprimerade innehållsavkastningen dekomprimerad;
Funktionen gör några saker:
Nästa skapar vi ett användbart verktyg i Flash för att komprimera och dekomprimera SWF-filer. I ett nytt AS3-projekt sammanställer du följande klass som en dokumentklass:
paket import flash.display.Sprite; importera flash.events.Event; importera flash.net.FileFilter; importera flash.net.FileReference; importera flash.utils.ByteArray; offentlig klass Kompressor utökar Sprite privat var ref: FileReference; offentlig funktion Kompressor () ref = ny FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse ([nya FileFilter ("SWF-filer", "* .swf")]); privatfunktionsbelastning (e: Event): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load (); privat funktion processSWF (e: Event): void var swf: ByteArray; switch (ref.data.readMultiByte (3, "us-ascii")) case "CWS": swf = dekomprimera (ref.data); ha sönder; fallet "FWS": swf = komprimera (ref.data); ha sönder; standard: kasta fel ("inte SWF?"); ha sönder; Ny FileReference (). spara (swf); privatfunktion komprimera (data: ByteArray): ByteArray varrubrik: ByteArray = ny ByteArray (); var dekomprimerad: ByteArray = ny ByteArray (); var komprimerad: ByteArray = ny ByteArray (); header.writeBytes (data, 3, 5); // läs rubriken, exklusive signaturen decompressed.writeBytes (data, 8); // läs resten dekomprimerad.komprimera (); compressed.writeMultiByte ("CWS", "us-ascii"); // markera som komprimerade compressed.writeBytes (header); compressed.writeBytes (dekomprimeras); retur komprimerad; dekomprimera den privata funktionen (data: ByteArray): ByteArray varrubrik: ByteArray = ny ByteArray (); var komprimerad: ByteArray = ny ByteArray (); var dekomprimerad: ByteArray = ny ByteArray (); header.writeBytes (data, 3, 5); // läs den okomprimerade rubriken, med undantag för signatur compressed.writeBytes (data, 8); // läs resten, komprimerad compressed.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // markera som okomprimerade decompressed.writeBytes (header); // skriv rubriken tillbaka decompressed.writeBytes (compressed); // skriv den nu okomprimerade innehållsavkastningen dekomprimerad;
Som du antagligen märkte har jag lagt till 2 saker: Filhantering och komprimeringsfunktionen.
Komprimeringsfunktionen är identisk med dekomprimeringsfunktionen, men i omvänd. Filhanteringen är klar med FileReference (FP10 krävs) och den laddade filen är antingen komprimerad eller okomprimerad. Observera att du måste köra SWF lokalt från en fristående spelare, som FileReference.browse () måste åberopas genom användarinteraktion (men den lokala fristående spelaren tillåter att köra den utan).
För att testa verktyget, skjut det upp, välj SWF-fältet och välj var för att spara det. Öppna sedan upp den med en hex-redaktör och skrubba igenom. Du bör se ascii strängar inuti så här:
Låt oss återvända till steg 2. Medan dekompilatorn inte visade någon användbar information om den skyddade SWF-enheten är det ganska lätt att få SWF från den nu okomprimerade lastaren. sök bara efter signaturen "CWS" (om den skyddade SWF är okomprimerad leta efter "FWS") och se resultaten:
Vad vi hittade är en DefineBinaryData-tagg som innehåller den skyddade SWF-enheten och extraherar den därifrån finns en bit kaka. Vi håller på att lägga till ett annat skyddsklass över SWF: Kryptering.
För att göra den skyddade SWF mindre "tillgänglig" lägger vi till någon form av kryptering. Jag valde att använda as3crypto och du kan ladda ner den från code.google.com. Du kan använda vilket bibliotek du vill istället (eller ditt eget genomförande, ännu bättre), det enda kravet är att det ska kunna kryptera och dekryptera binär data med en nyckel.
Det första vi vill göra är att skriva ett verktyg för att kryptera den skyddade SWF innan vi lägger in den. Det kräver mycket grundläggande kunskaper om as3crypto-biblioteket och det är ganska enkelt. Lägg till biblioteket i din biblioteksväg och låt oss börja med att skriva följande:
var aes: AESKey = ny AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // se till att det kan delas med 16, noll de sista 4 byte för (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);
Vad händer här? Vi använder en klass från as3crypto som heter AESKey för att kryptera innehållet. Klassen krypterar 16 byte i en tid (128 bitar), och vi måste för-loop över data för att kryptera allt. Notera den andra raden: data.length & ~ 15. Det säkerställer att antalet byte krypterade kan delas med 16 och vi går inte slut på data när vi ringer aes.encrypt ().
Notera: Det är viktigt att förstå krypteringspunkten i det här fallet. Det är inte riktigt kryptering, utan snarare obfuscation eftersom vi inkluderar nyckeln inuti SWF. Syftet är att omvandla data till binärt skräp, och koden ovan gör det jobb, även om det kan lämna upp till 15 okrypterade byte (vilket inte spelar någon roll i vårt fall). Jag är inte en kryptograf, och jag är ganska säker på att ovanstående kod kan se löm och svag ur ett kryptografperspektiv, men som jag sa är det ganska irrelevant eftersom vi inkluderar nyckeln inuti SWF.
Det är dags att skapa ett annat verktyg som hjälper oss att kryptera SWF-filer. Det är nästan detsamma som kompressorn som vi skapade tidigare, så jag kommer inte prata mycket om det. Kompilera det i ett nytt projekt som en dokumentklass:
paket import com.hurlant.crypto.symmetric.AESKey; importera flash.display.Sprite; importera flash.events.Event; importera flash.net.FileReference; importera flash.utils.ByteArray; public class Encryptor utökar Sprite private var-nyckel: String = "activetuts"; // Jag hardcoded nyckeln privat var ref: FileReference; offentlig funktion Encryptor () ref = ny FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse (); privatfunktionsbelastning (e: Event): void ref.addEventListener (Event.COMPLETE, kryptera); ref.load (); privat funktion kryptera (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = ny ByteArray (); binKey.writeUTF (nyckel); // AESKey kräver binär nyckel var: AESKey = ny AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // se till att det kan delas med 16, noll de sista 4 byte för (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);
Kör nu det och gör en krypterad kopia av den skyddade SWF-enheten genom att välja den först och spara sedan den under ett annat namn.
Återvänd till SWF-projektet. Eftersom innehållet nu är krypterat måste vi ändra SWF-filen och lägga till dekrypteringskoden i den. Glöm inte att ändra src i Embed-taggen för att peka på den krypterade SWF-filen.
paket import com.hurlant.crypto.symmetric.AESKey; importera flash.display.Loader; importera flash.display.Sprite; importera flash.system.ApplicationDomain; importera flash.system.LoaderContext; importera flash.utils.ByteArray; [SWF (bredd = 640, height = 423)] // måtten ska vara samma som den laddade swf: s allmänna klass. Huvudet sträcker sig Sprite [Embed (source = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // source = sökväg till swf du vill skydda privat var innehåll: klass; privat var nyckel: String = "activetuts"; allmän funktion Main (): void var data: ByteArray = nytt innehåll (); var binKey: ByteArray = ny ByteArray (); binKey.writeUTF (nyckel); // AESKey kräver binär nyckel var: AESKey = ny AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); // se till att det kan delas med 16, noll de sista 4 byte för (var i: int = 0; i < bytesToDecrypt; i += 16) aes.decrypt(data, i); var loader:Loader = new Loader(); addChild(loader); loader.loadBytes(data, new LoaderContext(false, new ApplicationDomain()));
Det här är detsamma som tidigare, med avkodningskoden fast i mitten. Kompilera nu lastningen SWF och testa om den fungerar. Om du följt noggrant upp till nu bör den skyddade SWF-enheten ladda och visa utan fel.
Öppna den nya laddnings SWF med en dekompiler och ta en titt.
Den innehåller över tusen linjer av tuff kodningskod, och det är nog svårare att få den skyddade SWF ut ur den. Vi har lagt till några steg som angriparen måste åta sig:
Problemet är att skapa ett verktyg är lika enkelt som att kopiera från dekompilatorn till kodredigeraren och justera koden lite. Jag försökte bryta mitt skydd själv, och det var ganska lätt - jag lyckades göra det på ungefär 5 minuter. Så vi måste ta några mätningar mot det.
Först skulle vi sätta den skyddade SWF in i SWF-laddningen, krypterade den sedan, och nu lägger vi de sista detaljerna till lastningen SWF. Vi byter namn på klasser, funktioner och variabler till olagliga namn.
Genom att säga olagliga namn Jag menar namn som,;! @@, ^ # ^ och (^ _ ^). Det häftiga är att det här är viktigt för kompilatorn men inte för Flash Player. När kompilatorn stöter på olagliga tecken inom identifierare misslyckas det att analysera dem och således misslyckas projektet. Å andra sidan har spelaren inga problem med de olagliga namnen. Vi kan sammanställa SWF med juridiska identifierare, dekomprimera det och byta namn på dem till en massa meningslösa olagliga symboler. Dekompilatorn kommer att mata ut olaglig kod och angriparen måste gå över hundratals kodrader manuellt och ta bort olagliga identifierare innan han kan sammanställa det. Han förtjänar det!
Så här ser det ut innan någon strängblodning uppstår:
Låt oss börja! Dekomprimera laddnings SWF med hjälp av verktyget som vi skapade före och avbröt en hex-redigerare.
Låt oss försöka byta namn på dokumentklassen. Om vi antar att du har lämnat originalnamnet (Main), letar vi efter det i den okomprimerade lastaren SWF med en hex-redigerare:
Byt namn på "Huvudsaklig"till ;;;;. Sök nu efter andra "Main" s och byt namn på dem till ;;;; för.
När du byter namn måste du se till att du inte byter namn på onödiga strängar eller att SWF inte körs.
Spara och kör SWF. Det fungerar! Och se vad decompileren säger:
Seger!! :)
Fortsätt namnge resten av dina lektioner. Välj ett klassnamn och sök efter det, ersätt det med olagliga symboler tills du når slutet av filen. Som jag sa är det viktigaste att använda din sunt förnuft, se till att du inte förstör din SWF-upp. Efter att ha bytt namn på klasserna kan du börja omdöpa paketet. Observera att när du byter namn på ett paket kan du radera perioderna och göra det till ett långt olagligt paketnamn. Titta vad jag gjorde:
När du har slutfört namn på klasserna och förpackningarna kan du börja omdöpa funktioner och variabler. De är ännu enklare att byta namn eftersom de oftast bara visas en gång i ett stort moln. Återigen, se till att du byter namn på bara "dina" metoder och inte de inbyggda Flash-metoderna. Se till att du inte torkar ut nyckeln ("activetuts" i vårt fall).
När du har avslutat omdirigeringen skulle du förmodligen vilja komprimera SWF så att den blir mindre i storlek. Inget problem, vi kan använda komprimeringsverktyget som vi skapade tidigare och det kommer att göra jobbet. Kör verktyget, välj SWF och spara det under ett annat namn.
Öppna den förra gången och ta en titt. Klasserna, variablerna och metodnamnen är obfuscated och den skyddade SWF är någonstans inuti, krypterad. Denna teknik kan vara långsam att applicera först, men efter några gånger tar det bara några minuter.
För ett tag sedan skapade jag ett automatiskt verktyg för att injicera den skyddade SWF-enheten för mig i lastningen SWF, och det fungerade bra. Det enda problemet är att om det kan injiceras med hjälp av ett automatiskt verktyg, kan det dekrypteras med ett annat verktyg, så om angriparen gör ett verktyg för det kommer han att få all din SWF enkelt. På grund av detta föredrar jag att skydda SWF manuellt varje gång och lägger till en liten ändring så det skulle vara svårare att automatisera.
En annan bra tillämpning av tekniken är Domänlåsning. Istället för att dekryptera SWF med en konstant sträng kan du dekryptera den med den domän som SWF körs för närvarande. Så istället för att ha ett if-uttalande för att kontrollera domänen kan du införa ett mer kraftfullt sätt att skydda SWF från placering på andra webbplatser.
Sista, kanske du vill ersätta krypteringskoden med din egen implementering. Varför? Vi satsade på att göra kryptokoden olaglig, men koden vi använder är från ett populärt open source-bibliotek och angriparen kan känna igen det som sådant. Han kommer ladda ner en ren kopia, och allt förvirrande arbete görs onödigt. Å andra sidan kräver användandet av ditt eget genomförande att han fixar alla olagliga namn innan han kan fortsätta.
Eftersom SWF-stöld är ett stort problem i Flash-världen finns det andra alternativ för att skydda SWF: er. Det finns många program där ute för att obfuscate AS på bytecode-nivån (som Kindisoft's secureSWF). De förstör den sammanställda bytekoden och när dekompilatorn försöker skriva ut kod kommer den att misslyckas, och till och med krascha ibland. Naturligtvis är detta skydd bättre när det gäller säkerhet, men det kostar $$$, så innan du väljer hur man skyddar din SWF, överväga hur mycket säkerhet som behövs. Om det handlar om att skydda en proprietär algoritm har din 50-anställda Flash-studio utvecklats under de senaste två åren, kan du överväga något bättre än att byta namn på variablerna. Å andra sidan om du vill förhindra att barnen lämnar felaktiga poäng kan du överväga att använda den här tekniken.
Vad jag tycker om den här tekniken är det faktum att din skyddade SWF lämnas orörd när den körs. AS obfuscation tampers med byte-koden och det kan eventuellt skada SWF och orsaka buggar (även om jag inte har stött på någon själv).
Det är allt för idag, hoppas du njöt av handledningen och lärde dig något nytt! Om du har några frågor är du välkommen att släppa en kommentar.