Twee keer per maand bekijken we enkele van onze favoriete lezers uit de geschiedenis van Activetuts +. Deze tutorial werd voor het eerst gepubliceerd in februari 2010.
In deze tutorial zal ik een techniek demonstreren die ik gebruik om code en eigendommen te beschermen tegen diefstal.
Decompilers vormen een echte zorg voor mensen die Flash-inhoud maken. Je kunt veel moeite doen om de beste game te maken die er is, dan kan iemand het stelen, het logo vervangen en het op hun site plaatsen zonder het je te vragen. Hoe? Een Flash-decompiler gebruiken. Tenzij u enige bescherming over uw SWF plaatst, kan deze worden gedecompileerd met een druk op de knop en de decompiler zal de leesbare broncode uitvoeren.
Ik heb een klein project van mij gebruikt om aan te tonen hoe kwetsbaar SWF's zijn voor decompilatie. Je kunt het downloaden en jezelf testen via de bronlink hierboven. Ik heb Sothink SWF Decompiler 5 gebruikt om de SWF te decompileren en onder de motorkap te kijken. De code is vrij leesbaar en je kunt het redelijk gemakkelijk begrijpen en hergebruiken.
Ik bedacht een techniek om SWF's te beschermen tegen decompilers en ik ga het in deze tutorial demonstreren. We zouden dit moeten kunnen produceren:
De gedecompileerde code is eigenlijk de code voor het decoderen van de inhoud en heeft niets te maken met je hoofdcode. Bovendien zijn de namen illegaal, dus compileert deze niet. Probeer het zelf te decompileren.
Voordat we beginnen, wil ik erop wijzen dat deze tutorial niet geschikt is voor beginners en dat je goede kennis van AS3 moet hebben als je mee wilt doen. Deze tutorial gaat ook over programmeren op laag niveau met bytes, ByteArrays en het manipuleren van SWF-bestanden met een hex-editor.
Dit is wat we nodig hebben:
Open een nieuw ActionScript 3.0-project en stel het in om te compileren met Flex SDK (ik gebruik FlashDevelop om code te schrijven). Kies een SWF die u wilt beveiligen en insluiten als binaire gegevens met de inslaaptag:
[Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream")] // source = pad naar de swf die u wilt beschermen private var content: Class;
Nu is de SWF ingesloten als een ByteArray in de lader SWF en deze kan worden geladen Loader.loadBytes ().
var loader: Loader = new Loader (); addChild (loader); loader.loadBytes (nieuwe inhoud (), nieuwe LoaderContext (false, new ApplicationDomain ()));
Uiteindelijk zouden we deze code moeten hebben:
pakket import flash.display.Loader; import flash.display.Sprite; import flash.system.ApplicationDomain; import flash.system.LoaderContext; [SWF (width = 640, height = 423)] // de dimensies moeten hetzelfde zijn als de public class van het geladen swf Hoofd breidt Sprite uit [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream") ] // source = pad naar het swf dat u privé-var-inhoud wilt beschermen: Class; public function Main (): void var loader: Loader = new Loader (); addChild (loader); loader.loadBytes (nieuwe inhoud (), nieuwe LoaderContext (false, new ApplicationDomain ()));
Compileer en kijk of het werkt (het zou moeten). Vanaf nu zal ik de ingesloten SWF de "protected SWF" noemen, en de SWF hebben we zojuist de "loading SWF" gecompileerd.
Laten we proberen te decompileren en kijken of het werkt.
Yey! De activa en de originele code zijn verdwenen! Wat nu wordt weergegeven, is de code die de beveiligde SWF laadt en niet de inhoud ervan. Dit zou waarschijnlijk de meeste aanvallers die voor de eerste keer Flash gebruiken niet stoppen, maar het is nog steeds niet goed genoeg om uw werk te beschermen tegen bekwame aanvallers, omdat de beschermde SWF op hen wacht binnen de ladende SWF.
Laten we de laad-SWF openen met een hex-editor:
Het moet eruit zien als willekeurige binaire gegevens omdat het is gecomprimeerd en het moet beginnen met ASCII "CWS". We moeten het decomprimeren! (Als uw SWF begint met 'FWS' en u ziet betekenisvolle strings in de SWF, is de kans groot dat deze niet is gecomprimeerd. U moet compressie inschakelen om mee te gaan).
In eerste instantie klinkt het misschien moeilijk, maar dat is het niet. Het SWF-formaat is een open indeling en er is een document dat dit beschrijft. Download het van adobe.com en scrol omlaag naar pagina 25 in het document. Er is een beschrijving van de header en hoe de SWF is gecomprimeerd, zodat we deze gemakkelijk kunnen decomprimeren.
Wat daar is geschreven is dat de eerste 3 bytes een handtekening zijn (CWS of FWS), de volgende byte de Flash-versie, de volgende 4 bytes zijn de grootte van de SWF. Het resterende deel is gecomprimeerd als de handtekening CWS is of niet is gecomprimeerd als de handtekening FWS is. Laten we een eenvoudige functie schrijven om een SWF te decomprimeren:
private function decompress (data: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var gecomprimeerd: ByteArray = new ByteArray (); var decompressed: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // lees de niet-gecomprimeerde header, exclusief de handtekening gecomprimeerd.writeBytes (data, 8); // lees de rest, gecomprimeerde gecomprimeerde.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // markeer als ongecomprimeerd decompressed.writeBytes (header); // schrijf de header terug decompressed.writeBytes (gecomprimeerd); // schrijf het nu ongecomprimeerde inhoudsretour gedecomprimeerd;
De functie doet een paar dingen:
Vervolgens maken we een handig hulpprogramma in Flash voor het comprimeren en decomprimeren van SWF-bestanden. In een nieuw AS3-project compileert u de volgende klasse als een documentklasse:
pakket import flash.display.Sprite; import flash.events.Event; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; public class Compressor breidt Sprite uit private var ref: FileReference; public function Compressor () ref = new FileReference (); ref.addEventListener (Event.SELECT, laden); ref.browse ([nieuwe FileFilter ("SWF-bestanden", "* .swf")]); private function load (e: Event): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load (); private function processSWF (e: Event): void var swf: ByteArray; switch (ref.data.readMultiByte (3, "us-ascii")) case "CWS": swf = decomprimeren (ref.data); breken; case "FWS": swf = comprimeren (ref.data); breken; standaard: throw Error ("Not SWF?"); breken; nieuwe FileReference (). save (swf); private function comprimeren (data: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var decompressed: ByteArray = new ByteArray (); var gecomprimeerd: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // lees de header, exclusief de handtekening gedecomprimeerd.writeBytes (data, 8); // lees de rest decompressed.compress (); comprimed.writeMultiByte ("CWS", "us-ascii"); // markeer als gecomprimeerde gecomprimeerde.writeBytes (koptekst); compressed.writeBytes (gedecomprimeerd); terug gecomprimeerd; private function decompress (data: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var gecomprimeerd: ByteArray = new ByteArray (); var decompressed: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // lees de niet-gecomprimeerde header, exclusief de handtekening gecomprimeerd.writeBytes (data, 8); // lees de rest, gecomprimeerde gecomprimeerde.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // markeer als ongecomprimeerd decompressed.writeBytes (header); // schrijf de header terug decompressed.writeBytes (gecomprimeerd); // schrijf het nu ongecomprimeerde inhoudsretour gedecomprimeerd;
Zoals je waarschijnlijk hebt gemerkt, heb ik 2 dingen toegevoegd: laden van bestanden en comprimeren.
De comprimeerfunctie is identiek aan de decompressiefunctie, maar in omgekeerde volgorde. Het laden van bestanden gebeurt met behulp van FileReference (FP10 vereist) en het geladen bestand is gecomprimeerd of ongecomprimeerd. Merk op dat je de SWF lokaal moet uitvoeren vanuit een stand-alone speler, zoals FileReference.browse () moet worden opgeroepen door gebruikersinteractie (maar de lokale stand-alone speler staat het toe om het uit te voeren zonder).
Als u het hulpprogramma wilt testen, start u het op, selecteert u de laad-SWF en kiest u waar u het wilt opslaan. Open het dan met een hex-editor en scrub er doorheen. Je zou ascii strings als volgt binnen moeten zien:
Laten we teruggaan naar stap 2. Hoewel de decompiler geen bruikbare informatie over de beschermde SWF bevatte, is het vrij eenvoudig om de SWF uit de nu niet-gecomprimeerde lader te halen; zoek gewoon naar de handtekening "CWS" (als de beschermde SWF een ongecomprimeerde zoekopdracht is naar "FWS") en bekijk de resultaten:
Wat we hebben gevonden is een DefineBinaryData-tag die de beschermde SWF bevat en het extraheren ervan is een fluitje van een cent. We staan op het punt een extra beveiligingslaag toe te voegen boven de laad-SWF: versleuteling.
Om de beschermde SWF minder "toegankelijk" te maken, zullen we een soort codering toevoegen. Ik heb ervoor gekozen om as3crypto te gebruiken en u kunt het downloaden van code.google.com. U kunt in plaats daarvan elke gewenste bibliotheek (of uw eigen implementatie, nog beter) gebruiken. De enige vereiste is dat deze binaire gegevens moet kunnen coderen en decoderen met behulp van een sleutel..
Het eerste dat we willen doen is een hulpprogramma schrijven om de beschermde SWF te coderen voordat we deze insluiten. Het vereist een basiskennis van de as3crypto-bibliotheek en het is vrij eenvoudig. Voeg de bibliotheek toe aan uw bibliotheekpad en laten we beginnen met het schrijven van het volgende:
var aes: AESKey = nieuwe AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); / / zorg ervoor dat het kan worden verdeeld door 16, nul de laatste 4 bytes voor (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);
Wat is hier aan de hand? We gebruiken een klasse van as3crypto genaamd AESKey om de inhoud te versleutelen. De klasse versleutelt 16 bytes in een tijd (128-bit), en we moeten for-lus over de gegevens om alles te coderen. Let op de tweede regel: data.length & ~ 15. Het zorgt ervoor dat het aantal gecodeerde bytes kan worden gedeeld door 16 en we raken niet leeg wanneer we bellen aes.encrypt ().
Notitie: Het is belangrijk om het punt van codering in dit geval te begrijpen. Het is niet echt versleuteling, maar eerder een versluiering, omdat we de sleutel in de SWF opnemen. Het doel is om de gegevens in binair afval om te zetten, en de code hierboven doet zijn werk, hoewel hij maximaal 15 niet-versleutelde bytes kan achterlaten (wat in ons geval niet uitmaakt). Ik ben geen cryptograaf en ik ben er vrij zeker van dat de bovenstaande code er slap en zwak uit zou kunnen zien vanuit het perspectief van een cryptograaf, maar zoals ik al zei is het vrij irrelevant omdat we de sleutel in de SWF opnemen.
Tijd om een ander hulpprogramma te maken waarmee we SWF-bestanden kunnen coderen. Het is bijna hetzelfde als de compressor die we eerder hebben gemaakt, dus ik zal er niet veel over praten. Compileer het in een nieuw project als een documentklasse:
pakket import com.hurlant.crypto.symmetric.AESKey; import flash.display.Sprite; import flash.events.Event; import flash.net.FileReference; import flash.utils.ByteArray; public class Encryptor breidt Sprite uit private var key: String = "activetuts"; // Ik heb de sleutel private var ref gecodeerd: FileReference; public function Encryptor () ref = new FileReference (); ref.addEventListener (Event.SELECT, laden); ref.browse (); private function load (e: Event): void ref.addEventListener (Event.COMPLETE, coderen); ref.load (); private function encrypten (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = new ByteArray (); binKey.writeUTF (key); // AESKey vereist binaire sleutelvarianten: AESKey = nieuwe AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); / / zorg ervoor dat het kan worden gedeeld door 16, nul de laatste 4 bytes voor (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);
Voer het nu uit en maak een gecodeerde kopie van de beschermde SWF door deze eerst te selecteren en vervolgens onder een andere naam op te slaan.
Keer terug naar het ladende SWF-project. Omdat de inhoud nu is gecodeerd, moeten we de laad-SWF wijzigen en er decoderingscode aan toevoegen. Vergeet niet om de broncode in de tag Insluiten te wijzigen in een verwijzing naar de gecodeerde SWF.
pakket import com.hurlant.crypto.symmetric.AESKey; import flash.display.Loader; import flash.display.Sprite; import flash.system.ApplicationDomain; import flash.system.LoaderContext; import flash.utils.ByteArray; [SWF (width = 640, height = 423)] // de dimensies moeten hetzelfde zijn als de openbare klasse van het geladen swf Hoofd breidt Sprite uit [Embed (source = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // source = pad naar het swf dat u privé-var-inhoud wilt beschermen: Class; private var key: String = "activetuts"; public function Main (): void var data: ByteArray = nieuwe inhoud (); var binKey: ByteArray = new ByteArray (); binKey.writeUTF (key); // AESKey vereist binaire sleutelvarianten: AESKey = nieuwe AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); / / zorg ervoor dat het kan worden gedeeld door 16, nul de laatste 4 bytes voor (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()));
Dit is hetzelfde als voorheen, behalve met de code die in het midden zit. Compileer nu de laad-SWF en test of deze werkt. Als u tot nu toe zorgvuldig hebt gevolgd, moet de beveiligde SWF worden geladen en zonder fouten worden weergegeven.
Open de nieuwe ladende SWF met een decompiler en kijk.
Het bevat meer dan duizend regels sterk uitziende encryptie-code, en het is waarschijnlijk moeilijker om de beveiligde SWF eruit te halen. We hebben nog een paar stappen toegevoegd die de aanvaller moet uitvoeren:
Het probleem is dat het maken van een hulpprogramma net zo eenvoudig is als kopiëren van de decompiler naar de codebewerker en de code een beetje aanpassen. Ik probeerde mijn bescherming zelf te breken en het was vrij eenvoudig - ik slaagde erin om het in ongeveer 5 minuten te doen. Dus we zullen er een paar metingen tegen moeten doen.
Eerst zouden we de beveiligde SWF in de laad-SWF plaatsen en vervolgens versleutelen, en nu leggen we de laatste hand aan de laad-SWF. We zullen klassen, functies en variabelen hernoemen naar illegale namen.
Door te zeggen illegale namen Ik bedoel namen zoals;! @@, ^ # ^ en (^ _ ^). Het leuke is dat dit van belang is voor de compiler, maar niet voor de Flash Player. Wanneer de compiler illegale tekens in ID's tegenkomt, slaagt deze er niet in om ze te ontleden en kan het project dus niet worden gecompileerd. Aan de andere kant heeft de speler geen problemen met deze illegale namen. We kunnen de SWF compileren met legale identificaties, deze decomprimeren en hernoemen naar een heleboel betekenisloze illegale symbolen. De decompiler voert de illegale code uit en de aanvaller moet handmatig de honderden regels code doorlopen en de illegale identificatie verwijderen voordat hij deze kan compileren. Hij verdient het!
Dit is hoe het eruit ziet vóór een tekenreeks obfuscation:
Laten we beginnen! Decomprimeer de laad-SWF met behulp van het hulpprogramma dat we eerder hebben gemaakt en vouw een hex-editor op.
Laten we de documentklasse hernoemen. Ervan uitgaande dat je de originele naam (hoofd) hebt achtergelaten, laten we ernaar zoeken in de niet-gecomprimeerde loader-SWF met een hex-editor:
Hernoemen "Hoofd" naar ;;;;. Zoek nu naar andere "Main" en hernoem ze ;;;; te.
Zorg er bij het hernoemen voor dat u niet onnodige tekenreeksen hernoemt of dat de SWF niet wordt uitgevoerd.
Sla op en voer de SWF uit. Het werkt! En kijk wat de decompiler zegt:
Zege!! :)
Blijf de rest van je klassen hernoemen. Kies een klassenaam en zoek ernaar, vervang deze door illegale symbolen totdat u het einde van het bestand bereikt. Zoals ik al zei, het belangrijkste hier is om je gezond verstand te gebruiken, zorg ervoor dat je je SWF niet in de war brengt. Na het hernoemen van de klassen kunt u beginnen met het hernoemen van de pakketten. Houd er rekening mee dat u bij het hernoemen van een pakket ook de perioden kunt wissen en er een lange illegale pakketnaam van kunt maken. Kijk wat ik heb gemaakt:
Nadat u het hernoemen van de klassen en de pakketten hebt voltooid, kunt u beginnen met het hernoemen van functies en variabelen. Ze zijn nog eenvoudiger te hernoemen omdat ze meestal maar één keer voorkomen, in één grote wolk. Nogmaals, zorg ervoor dat u alleen "uw" methoden hernoemt en niet de ingebouwde Flash-methoden. Zorg ervoor dat je de sleutel niet wegvaagt ("activetuts" in ons geval).
Nadat u de naam hebt gewijzigd, wilt u waarschijnlijk de SWF comprimeren zodat deze kleiner wordt. Geen probleem, we kunnen het compressieprogramma gebruiken dat we eerder hebben gemaakt en het zal het werk doen. Voer het hulpprogramma uit, selecteer de SWF en sla deze op onder een andere naam.
Open het nog een laatste keer en kijk. De klassen, de variabelen en de methodamen zijn versluierd en de beschermde SWF bevindt zich ergens binnen, versleuteld. Deze techniek kan in eerste instantie traag van toepassing zijn, maar na enkele keren duurt het slechts een paar minuten.
Een tijdje geleden heb ik een automatisch hulpprogramma gemaakt om de beschermde SWF voor mij in de ladende SWF te injecteren en het werkte prima. Het enige probleem is dat als het kan worden geïnjecteerd met behulp van een automatisch hulpprogramma, het kan worden gedecodeerd met een ander hulpprogramma, dus als de aanvaller daar een hulpprogramma voor maakt, zal hij al je SWF gemakkelijk krijgen. Daarom geef ik er de voorkeur aan de SWF's elke keer handmatig te beveiligen en een kleine aanpassing toe te voegen, zodat het moeilijker te automatiseren is.
Een andere leuke toepassing van de techniek is Domein vergrendeling. In plaats van de SWF te decoderen met een constante tekenreeks, kunt u deze decoderen met het domein waarop de SWF momenteel wordt uitgevoerd. Dus in plaats van een if-instructie te hebben om het domein te controleren, kunt u een krachtigere manier introduceren om de SWF te beschermen tegen plaatsing op andere sites.
Als laatste kunt u de versleutelingscode vervangen door uw eigen implementatie. Waarom? We hebben geïnvesteerd in het illegaal maken van de crypto-code, maar de code die we gebruiken komt uit een populaire open-sourcebibliotheek en de aanvaller kan deze als zodanig herkennen. Hij zal een schone kopie downloaden en alle versluiering wordt overbodig. Aan de andere kant vereist het gebruik van je eigen implementatie dat hij alle illegale namen repareert voordat hij verder kan gaan.
Omdat SWF-diefstal een groot probleem is in de Flash-wereld, zijn er andere opties voor het beschermen van SWF's. Er zijn veel programma's om AS op het bytecode-niveau (zoals Kindisoft's secureSWF) te versluieren. Ze verknoeien de gecompileerde bytecode en wanneer de decompiler probeert om code uit te voeren, zal het mislukken en soms zelfs crashen. Natuurlijk is deze beveiliging beter in termen van beveiliging, maar het kost $ $ $. Overweeg daarom voordat u kiest hoe u uw SWF beschermt, hoeveel beveiliging u nodig hebt. Als het gaat om het beschermen van een gepatenteerd algoritme dat uw 50-medewerkers Flash-studio de afgelopen twee jaar heeft ontwikkeld, kunt u iets beters overwegen dan de variabelen een andere naam te geven. Aan de andere kant, als u wilt voorkomen dat de kinderen valse hoge scores geven, kunt u overwegen deze techniek te gebruiken.
Wat ik leuk vind aan deze techniek is het feit dat je beschermde SWF niet wordt aangeraakt tijdens het uitvoeren. AS obfuscation saboteert met de bytecode en het kan mogelijk de SWF beschadigen en bugs veroorzaken (hoewel ik mezelf nog niet ben tegengekomen).
Dat is alles voor vandaag, ik hoop dat je de tutorial leuk vond en iets nieuws hebt geleerd! Als je vragen hebt, voel je vrij om een reactie achter te laten.