Beskytt Flash-filer fra dekompilere ved hjelp av kryptering

To ganger i måneden besøker vi noen av våre leseres favorittinnlegg fra hele historien til Activetuts +. Denne opplæringen ble først publisert i februar 2010.

I denne opplæringen vil jeg demonstrere en teknikk jeg bruker for å beskytte kode og eiendeler fra tyveri.

Dekompilere er en reell bekymring for folk som lager Flash-innhold. Du kan legge mye arbeid på å skape det beste spillet der ute, så noen kan stjele den, erstatte logoen og sette den på sitt nettsted uten å spørre deg. Hvordan? Bruke en Flash Decompiler. Med mindre du legger noe beskyttelse over SWF-en, kan den dekompileres med et trykk på en knapp og dekompilatoren vil skrive ut lesbar kildekode.


Før vi begynner

Jeg brukte et lite prosjekt for å demonstrere hvordan sårbare SWF er å dekompilere. Du kan laste den ned og teste deg selv via kilde lenken ovenfor. Jeg brukte Sothink SWF Decompiler 5 til å dekompilere SWF og se under hetten. Koden er ganske lesbar, og du kan forstå og gjenbruke den ganske enkelt.


Hva kan vi gjøre om det?

Jeg kom opp med en teknikk for å beskytte SWFer fra dekompilere, og jeg skal demonstrere det i denne opplæringen. Vi burde kunne produsere dette:

Koden som dekompileres er faktisk koden for dekryptering av innholdet og har ingenting å gjøre med hovedkoden din. I tillegg er navnene ulovlige, så det vil ikke kompilere tilbake. Prøv å dekompilere det selv.

Før vi går, vil jeg påpeke at denne opplæringen ikke passer for nybegynnere, og du bør ha solid kunnskap om AS3 hvis du vil følge med. Denne opplæringen handler også om lavt nivå programmering som involverer byte, ByteArrays og manipulere SWF-filer med en hex editor.

Her er det vi trenger:

  • En SWF for å beskytte. Du er velkommen til å laste ned SWFen jeg skal jobbe med.
  • Flex SDK. Vi bruker det til å legge inn innhold ved hjelp av Embed-taggen. Du kan laste den ned fra opensource.adobe.com.
  • En hex-redigerer. Jeg bruker en gratis editor kalt Hex-Ed. Du kan laste den ned fra nielshorn.net eller du kan bruke en redaktør etter eget valg.
  • En dekompiler. Selv om det ikke er nødvendig, ville det være fint å sjekke om vår beskyttelse faktisk fungerer. Du kan ta en prøve av Sothink SWF Decompiler fra sothink.com

Trinn 1: Legg inn SWF på Runtime

Åpne et nytt ActionScript 3.0-prosjekt, og sett det til å kompilere med Flex SDK (jeg bruker FlashDevelop til å skrive kode). Velg en SWF du vil beskytte og legge inn som binær data ved hjelp av Embed-taggen:

 [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream")] // kilde = sti til swf du vil beskytte private var innhold: Klasse;

Nå er SWF innebygd som en ByteArray inn i lasteren SWF og den kan lastes gjennom Loader.loadBytes ().

 var loader: Loader = ny Loader (); addChild (loader); Loader.loadBytes (nytt innhold (), ny LoaderContext (falsk, ny ApplicationDomain ()));

Til slutt bør vi ha denne koden:

 pakke import flash.display.Loader; importer flash.display.Sprite; importere flash.system.ApplicationDomain; importer flash.system.LoaderContext; [SWF (bredde = 640, høyde = 423)] // dimensjonene skal være det samme som den lastede swfens offentlige klasse Hoved utvider Sprite [Embed (kilde = "VerletCloth.swf", mimeType = "application / octet-stream") ] // source = sti til swf du vil beskytte private var innhold: klasse; offentlig funksjon Main (): void var loader: Loader = new Loader (); addChild (loader); Loader.loadBytes (nytt innhold (), ny LoaderContext (falsk, ny ApplicationDomain ())); 

Sett sammen og se om det fungerer (det burde). Fra nå av vil jeg ringe den innebygde SWF den "beskyttede SWF", og SWF vi nettopp samlet "lasting SWF".


Trinn 2: Analyser resultatet

La oss prøve å dekompilere og se om det fungerer.

Yey! Aktivene og den opprinnelige koden er borte! Det som vises nå er koden som laster den beskyttede SWF og ikke innholdet. Dette vil trolig stoppe de fleste av de første angriperne som ikke er kjent med Flash, men det er fortsatt ikke godt nok til å beskytte arbeidet ditt fra dyktige angripere fordi den beskyttede SWF venter på dem uberørt inne i lasten SWF.


Trinn 3: Dekomprimer SWF

La oss åpne SWF-lasten med en hex-editor:

Det skal se ut som tilfeldige binære data fordi den er komprimert, og den skal begynne med ASCII "CWS". Vi må dekomprimere det! (Hvis SWF begynner med "FWS" og du ser meningsfulle strenger i SWF er det sannsynlig at det ikke ble komprimert. Du må aktivere komprimering for å følge med).

Først kan det høres vanskelig, men det er det ikke. SWF-formatet er et åpent format, og det er et dokument som beskriver det. Last ned den fra adobe.com og bla ned til side 25 i dokumentet. Det er en beskrivelse av overskriften og hvordan SWF er komprimert, slik at vi kan pakke ut det enkelt.

Det som står skrevet er at de første 3 byte er en signatur (CWS eller FWS), neste byte er Flash-versjonen, de neste 4 bytes er størrelsen på SWF. De resterende er komprimert hvis signaturen er CWS eller ukomprimert hvis signaturen er FWS. La oss skrive en enkel funksjon for å dekomprimere en SWF:

 privat funksjon dekomprimering (data: ByteArray): ByteArray var overskrift: ByteArray = ny ByteArray (); var komprimert: ByteArray = ny ByteArray (); var dekomprimert: ByteArray = ny ByteArray (); header.writeBytes (data, 3, 5); // les den ukomprimerte overskriften, unntatt signatur compressed.writeBytes (data, 8); // les resten, komprimert compressed.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // markere som ukomprimerte decompressed.writeBytes (header); // skriv overskriften tilbake decompressed.writeBytes (komprimert); // skriv det nå ukomprimerte innholdsretet dekomprimert; 

Funksjonen gjør noen ting:

  1. Den leser den ukomprimerte overskriften (de første 8 bytes) uten signaturen og husker den.
  2. Den leser resten av dataene og komprimerer den.
  3. Den skriver tilbake overskriften (med "FWS" signaturen) og de ukomprimerte dataene, skaper en ny, ukomprimert SWF.

Trinn 4: Opprette et verktøy

Deretter skal vi opprette et praktisk verktøy i Flash for å komprimere og dekomprimere SWF-filer. I et nytt AS3-prosjekt, kompilere følgende klasse som en dokumentklasse:

 pakke import flash.display.Sprite; importere flash.events.Event; importer flash.net.FileFilter; importer flash.net.FileReference; importere flash.utils.ByteArray; offentlig klasse Kompressor utvider Sprite private var ref: FileReference; offentlig funksjon Kompressor () ref = ny FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse ([new FileFilter ("SWF Files", "* .swf")]);  privat funksjonsbelastning (e: Event): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load ();  privatfunksjon prosessSWF (e: Event): void var swf: ByteArray; bytt (ref.data.readMultiByte (3, "us-ascii")) case "CWS": swf = dekomprimere (ref.data); gå i stykker; tilfelle "FWS": swf = komprimere (ref.data); gå i stykker; standard: kaste feil ("ikke SWF?"); gå i stykker;  Ny FileReference (). lagre (swf);  privatfunksjon komprimere (data: ByteArray): ByteArray var overskrift: ByteArray = ny ByteArray (); var dekomprimert: ByteArray = ny ByteArray (); var komprimert: ByteArray = ny ByteArray (); header.writeBytes (data, 3, 5); // les overskriften, unntatt signatur decompressed.writeBytes (data, 8); // les resten dekompressed.compress (); compressed.writeMultiByte ("CWS", "us-ascii"); // markere som komprimerte compressed.writeBytes (header); compressed.writeBytes (dekomprimert); retur komprimert;  privat funksjon dekomprimering (data: ByteArray): ByteArray var overskrift: ByteArray = ny ByteArray (); var komprimert: ByteArray = ny ByteArray (); var dekomprimert: ByteArray = ny ByteArray (); header.writeBytes (data, 3, 5); // les den ukomprimerte overskriften, unntatt signatur compressed.writeBytes (data, 8); // les resten, komprimert compressed.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // markere som ukomprimerte decompressed.writeBytes (header); // skriv overskriften tilbake decompressed.writeBytes (komprimert); // skriv det nå ukomprimerte innholdsretet dekomprimert; 

Som du sikkert har lagt merke til, har jeg lagt til 2 ting: Filinnlasting og komprimeringsfunksjonen.

Komprimeringsfunksjonen er identisk med dekomprimeringsfunksjonen, men i revers. Filopplasting er ferdig med FileReference (FP10 kreves) og den lastede filen er enten komprimert eller ukomprimert. Legg merke til at du må kjøre SWF lokalt fra en frittstående spiller, som FileReference.browse () må påberopes av brukerinteraksjon (men den lokale frittstående spilleren tillater å kjøre den uten).


Trinn 5: Uncompressing Loading SWF

For å teste verktøyet, brann det opp, velg lasting SWF og velg hvor du skal lagre det. Deretter åpner du den med en seksk redaktør og skrubbe gjennom. Du bør se ascii strenger inne slik:


Trinn 6: Analyser igjen

La oss gå tilbake til trinn 2. Mens dekompileren ikke viste noen nyttig informasjon om den beskyttede SWF, er det ganske enkelt å få SWF fra den nå ukomprimerte lasteren; bare søk etter signaturen "CWS" (hvis den beskyttede SWF er ukomprimert søk etter "FWS") og se resultatene:

Det vi fant er en DefineBinaryData-tag som inneholder den beskyttede SWF-en, og ekstraherer den derfra er et stykke kake. Vi skal legge til et annet lag med beskyttelse over lasting SWF: Kryptering.


Trinn 7: Kryptering

For å gjøre beskyttet SWF mindre "tilgjengelig" vil vi legge til en slags kryptering. Jeg valgte å bruke as3crypto, og du kan laste den ned fra code.google.com. Du kan bruke hvilket som helst bibliotek du vil i stedet (eller din egen implementering, enda bedre), det eneste kravet er at det skal kunne kryptere og dekryptere binære data ved hjelp av en nøkkel.


Trinn 8: Krypteringsdata

Det første vi vil gjøre er å skrive et verktøy for å kryptere den beskyttede SWF før vi legger den inn. Det krever veldig grunnleggende kunnskap om as3crypto biblioteket, og det er ganske greit. Legg til biblioteket i bibliotekets bane og la oss begynne med å skrive følgende:

 var aes: AESKey = nytt AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // sørg for at den kan deles med 16, null de siste 4 byte for (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);

Hva foregår her? Vi bruker en klasse fra as3crypto kalt AESKey for å kryptere innholdet. Klassen krypterer 16 byte om gangen (128 bit), og vi må for-loop over dataene for å kryptere alt. Merk den andre linjen: data.length & ~ 15. Det sørger for at antall byte kryptert kan deles med 16 og vi går ikke tom for data når du ringer aes.encrypt ().

Merk: Det er viktig å forstå krypteringspunktet i dette tilfellet. Det er egentlig ikke kryptering, men heller obfuscation siden vi inkluderer nøkkelen inne i SWF. Hensikten er å slå dataene i binært søppel, og koden ovenfor gjør det jobb, selv om det kan gi opptil 15 ukrypterte byte (noe som ikke betyr noe i vårt tilfelle). Jeg er ikke en kryptograf, og jeg er ganske sikker på at koden ovenfor kan se seg svak og svak fra et kryptografperspektiv, men som sagt, det er ganske irrelevant da vi inkluderer nøkkelen inne i SWF.


Trinn 9: Krypteringsverktøy

Tid til å opprette et nytt verktøy som hjelper oss med å kryptere SWF-filer. Det er nesten det samme som kompressoren vi opprettet tidligere, så jeg vil ikke snakke mye om det. Kompilere det i et nytt prosjekt som en dokumentklasse:

 pakke import com.hurlant.crypto.symmetric.AESKey; importer flash.display.Sprite; importere flash.events.Event; importer flash.net.FileReference; importere flash.utils.ByteArray; offentlig klasse Encryptor utvider Sprite private var-nøkkel: String = "activetuts"; // Jeg hardcoded nøkkel privat var ref: FileReference; offentlig funksjon Encryptor () ref = ny FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse ();  privat funksjon last (e: Event): void ref.addEventListener (Event.COMPLETE, krypter); ref.load ();  privat funksjon kryptere (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = ny ByteArray (); binKey.writeUTF (nøkkel); // AESKey krever binær nøkkel var aes: AESKey = ny AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // sørg for at den kan deles med 16, null de siste 4 bytesene for (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);   

Kjør det nå, og lag en kryptert kopi av den beskyttede SWF ved å velge den først og deretter lagre den under et annet navn.


Trinn 10: Endre lasteren

Gå tilbake til SWF-prosjektet som lastes inn. Fordi innholdet er kryptert, må vi endre laste SWF og legge til dekrypteringskode inn i den. Ikke glem å endre src i Embed-taggen for å peke på den krypterte SWF-en.

 pakke import com.hurlant.crypto.symmetric.AESKey; importer flash.display.Loader; importer flash.display.Sprite; importere flash.system.ApplicationDomain; importer flash.system.LoaderContext; importere flash.utils.ByteArray; [SWF (bredde = 640, høyde = 423)] // dimensjonene skal være det samme som den lastede swfens offentlige klasse Hoved utvider Sprite [Embed (source = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // source = sti til swf du vil beskytte private var innhold: klasse; privat var nøkkel: String = "activetuts"; offentlig funksjon Main (): void var data: ByteArray = nytt innhold (); var binKey: ByteArray = ny ByteArray (); binKey.writeUTF (nøkkel); // AESKey krever binær nøkkel var aes: AESKey = ny AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); // sørg for at den kan deles med 16, null de siste 4 bytesene for (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()));   

Dette er det samme som før, unntatt med dekrypteringskoden fast i midten. Sett sammen lasten SWF og test om det virker. Hvis du har fulgt nøye opp til nå, bør den beskyttede SWF lastes og vises uten feil.


Trinn 11: Se innsiden med en Decompiler

Åpne den nye laste SWF med en dekompiler og ta en titt.

Den inneholder over tusen linjer med tøft utseende krypteringskode, og det er sannsynligvis vanskeligere å få beskyttet SWF ut av det. Vi har lagt til noen få trinn som angriperen må påta seg:

  1. Han (eller hun) må finne DefineBinaryData som inneholder kryptert innhold og trekke det ut.
  2. Han må opprette et verktøy for å dekryptere det.

Problemet er at det å lage et verktøy er så enkelt som å kopiere fra dekompilatoren til kodeditoren og tilpasse koden litt. Jeg prøvde å bryte min beskyttelse selv, og det var ganske enkelt - jeg klarte å gjøre det på omtrent 5 minutter. Så vi må ta noen målinger mot det.


Trinn 12: Strengeforvirring

Først vil vi sette den beskyttede SWF i lastet SWF, krypterte den da, og nå legger vi de siste detaljene til lasten SWF. Vi omdøper klasser, funksjoner og variabler til ulovlige navn.

Ved å si ulovlige navn Jeg mener navn som,;! @@, ^ # ^ og (^ _ ^). Den kule tingen er at dette er viktig for kompilatoren, men ikke til Flash Player. Når kompilatoren møter ulovlige tegn innenfor identifikatorer, svikter det ikke å analysere dem, og dermed mislykkes prosjektet. På den annen side har spilleren ingen problemer med de ulovlige navnene. Vi kan kompilere SWF med juridiske identifikatorer, dekomprimere den og omdøpe dem til en rekke meningsløse ulovlige symboler. Dekompilatoren vil sende ut ulovlig kode og angriperen må gå over hundrevis av kodelinjer manuelt, fjerne ulovlige identifikatorer før han kan kompilere den. Han fortjener det!

Slik ser det ut før noen obfuscation av snor:

La oss begynne! Dekomprimér lasten SWF ved hjelp av verktøyet vi opprettet før og brann opp en hex-editor.


Trinn 13: Din første forvirring

La oss prøve å gi nytt navn til dokumentklassen. Forutsatt at du har forlatt det opprinnelige navnet (Main), la vi søke etter det i den ukomprimerte lasteren SWF med en hex-editor:

Gi nytt navn til "Hoved" til ;;;;. Søk nå etter andre "Main" s og endre navn på dem til ;;;; også.

Ved omdøping må du ikke endre navn på unødvendige strenger eller SWF vil ikke kjøre.

Lagre og kjøre SWF. Det fungerer! Og se hva decompiler sier:

Seier!! :)


Trinn 14: Omdøpe resten av klassene

Fortsett å gi nytt navn til resten av klassene dine. Velg et klassenavn og søk etter det, erstatt det med ulovlige symboler til du kommer til slutten av filen. Som sagt, det viktigste her er å bruke sunn fornuft, sørg for at du ikke roter SWF-opp. Etter å ha omdøpt klassene kan du begynne å gi nytt navn til pakkene. Legg merke til at når du omdøper en pakke, kan du også slette periodene og gjøre det til et langt ulovlig pakkenavn. Se hva jeg laget:

Når du er ferdig med å endre navn på klassene og pakkene, kan du begynne å gi nytt navn til funksjoner og variabler. De er enda enklere å gi nytt navn, da de vanligvis bare vises en gang, i en stor sky. Igjen, vær sikker på at du bare omdøper bare "dine" metoder og ikke de innebygde Flash-metodene. Pass på at du ikke tørker ut nøkkelen ("activetuts" i vårt tilfelle).


Trinn 15: Komprimer SWF

Når du er ferdig med å endre navn, vil du sannsynligvis ønske å komprimere SWF slik at den blir mindre i størrelse. Ikke noe problem, vi kan bruke komprimeringsverktøyet vi opprettet før, og det vil gjøre jobben. Kjør verktøyet, velg SWF og lagre det under et annet navn.


Konklusjon: Har en endelig titt

Åpne den en siste gang og ta en titt. Klassene, variablene og metodenavnene er obfuscated og den beskyttede SWF er et sted inne, kryptert. Denne teknikken kan være sakte å bruke først, men etter noen få ganger tar det bare noen få minutter.

For en stund siden opprettet jeg et automatisk verktøy for å injisere den beskyttede SWF for meg i SWF-lasten, og det fungerte bra. Det eneste problemet er at hvis den kan injiseres ved hjelp av et automatisk verktøy, kan det dekrypteres med et annet verktøy, så hvis angriperen gjør et verktøy for det, vil han få all din SWF enkelt. På grunn av dette foretrekker jeg å beskytte SWFene manuelt hver gang, og legger til en liten endring, slik at det ville bli vanskeligere å automatisere.

En annen fin applikasjon av teknikken er Domenelåsing. I stedet for å dekryptere SWF med en konstant streng kan du dekryptere den med domenet som SWF kjører for øyeblikket. Så i stedet for å ha en if-setning for å sjekke domenet, kan du introdusere en mer kraftfull måte å beskytte SWF fra plassering på andre nettsteder.

Sist, kanskje du vil erstatte krypteringskoden med din egen implementering. Hvorfor? Vi investerte innsats i å lage kryptokoden ulovlig, men koden vi bruker er fra et populært open source-bibliotek, og angriperen kan gjenkjenne den som sådan. Han vil laste ned en ren kopi, og alt obfuscation-arbeidet blir gjort unødvendig. På den annen side vil bruk av din egen implementering kreve at han løser alle de ulovlige navnene før han kan fortsette.


Andre beskyttelsesmetoder

Fordi SWF-tyveri er et stort problem i Flash-verdenen, er det andre alternativer for å beskytte SWFs. Det er mange programmer der ute for å forvirre AS på bytecode-nivået (som Kindisoft's secureSWF). De rydder opp kompilert bytecode og når decompiler forsøker å utføre kode, vil det mislykkes, og til og med krasje noen ganger. Selvfølgelig er denne beskyttelsen bedre når det gjelder sikkerhet, men det koster $$$, så før du velger hvordan du beskytter din SWF, må du vurdere hvor mye sikkerhet du trenger. Hvis det handler om å beskytte en proprietær algoritme, har Flash-studioet ditt med 50 ansatte utviklet seg de siste to årene, og du kan vurdere noe bedre enn å omdøpe variablene. På den annen side, hvis du vil forhindre at barnene sender falske høyt score, kan du vurdere å bruke denne teknikken.

Det jeg liker om denne teknikken er det faktum at din beskyttede SWF blir uberørt når du kjører. AS forvirring tamperer med byte-koden, og det kan muligens skade SWF og forårsake feil (selv om jeg ikke har møtt noen selv).

Det er alt for i dag, håper du likte opplæringen og lærte noe nytt! Hvis du har noen spørsmål, vær så snill å slippe en kommentar.