Due volte al mese, rivisitiamo alcuni dei post preferiti dei nostri lettori da tutta la storia di Activetuts +. Questo tutorial è stato pubblicato per la prima volta nel febbraio 2010.
In questo tutorial mostrerò una tecnica che uso per proteggere codice e risorse dal furto.
I decompilatori sono una vera preoccupazione per le persone che creano contenuti Flash. Puoi fare un sacco di sforzi per creare il miglior gioco là fuori, quindi qualcuno può rubarlo, sostituire il logo e metterlo sul loro sito senza chiederti. Come? Utilizzando un decompilatore Flash. A meno che tu non abbia messo un po 'di protezione sul tuo SWF, può essere decompilato premendo un pulsante e il decompilatore emetterà un codice sorgente leggibile.
Ho usato un mio piccolo progetto per dimostrare quanto sono vulnerabili i file SWF da decompilare. Puoi scaricarlo e metterti alla prova tramite il link sorgente qui sopra. Ho usato Sothink SWF Decompiler 5 per decompilare il SWF e guardare sotto il suo cofano. Il codice è abbastanza leggibile e puoi capirlo e riutilizzarlo abbastanza facilmente.
Ho inventato una tecnica per proteggere i file SWF dai decompilatori e ho intenzione di dimostrarlo in questo tutorial. Dovremmo essere in grado di produrre questo:
Il codice che viene decompilato è in realtà il codice per decodificare il contenuto e non ha nulla a che fare con il tuo codice principale. Inoltre, i nomi sono illegali e quindi non verranno compilati. Prova a decompilarlo da solo.
Prima di andare avanti, voglio sottolineare che questo tutorial non è adatto ai principianti e che dovresti avere una solida conoscenza dell'AS3 se vuoi seguirlo. Questo tutorial riguarda anche la programmazione di basso livello che coinvolge byte, ByteArray e manipola i file SWF con un editor esadecimale.
Ecco di cosa abbiamo bisogno:
Aprire un nuovo progetto ActionScript 3.0 e impostarlo per la compilazione con Flex SDK (utilizzo FlashDevelop per scrivere codice). Scegli un SWF che vuoi proteggere e incorporalo come dati binari usando il tag Embed:
[Incorpora (source = "VerletCloth.swf", mimeType = "application / octet-stream")] // source = percorso allo swf che si desidera proteggere contenuto var privato: Class;
Ora il file SWF è incorporato come ByteArray nel caricatore SWF e può essere caricato attraverso Loader.loadBytes ().
var loader: Loader = new Loader (); addChild (loader); loader.loadBytes (nuovo contenuto (), nuovo LoaderContext (false, new ApplicationDomain ()));
Alla fine dovremmo avere questo codice:
package import flash.display.Loader; import flash.display.Sprite; import flash.system.ApplicationDomain; import flash.system.LoaderContext; [SWF (larghezza = 640, altezza = 423)] // le dimensioni devono essere uguali alla classe pubblica dello swf caricato Main extends Sprite [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream") ] // source = percorso per swf che si desidera proteggere contenuto var privato: Classe; funzione pubblica Main (): void var loader: Loader = new Loader (); addChild (loader); loader.loadBytes (nuovo contenuto (), nuovo LoaderContext (false, new ApplicationDomain ()));
Compilare e vedere se funziona (dovrebbe). D'ora in poi chiamerò il SWF incorporato "SWF protetto" e il SWF abbiamo appena compilato il "caricamento SWF".
Proviamo a decompilare e vediamo se funziona.
Yey! Le risorse e il codice originale sono andati! Quello che viene mostrato ora è il codice che carica il file SWF protetto e non il suo contenuto. Questo probabilmente bloccherebbe la maggior parte degli attaccanti alle prime armi che non hanno familiarità con Flash, ma non è ancora abbastanza buono per proteggere il tuo lavoro dagli aggressori esperti perché il SWF protetto li sta aspettando intatti all'interno del SWF di caricamento.
Apriamo il file SWF di caricamento con un editor esadecimale:
Dovrebbe apparire come dati binari casuali perché è compresso e dovrebbe iniziare con ASCII "CWS". Dobbiamo decomprimerlo! (Se il file SWF inizia con "FWS" e si vedono stringhe significative nel file SWF, è probabile che non sia stato compresso. È necessario abilitare la compressione per proseguire).
All'inizio potrebbe sembrare difficile ma non lo è. Il formato SWF è un formato aperto e c'è un documento che lo descrive. Scaricalo da adobe.com e scorri verso il basso fino a pagina 25 nel documento. C'è una descrizione dell'intestazione e di come viene compresso il file SWF, quindi possiamo decomprimerlo facilmente.
Quello che è scritto lì è che i primi 3 byte sono una firma (CWS o FWS), il byte successivo è la versione Flash, i successivi 4 byte sono la dimensione del file SWF. Il resto viene compresso se la firma è CWS o non compressa se la firma è FWS. Scriviamo una semplice funzione per decomprimere un SWF:
private function decompress (data: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var compresso: ByteArray = new ByteArray (); var decompressed: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // legge l'intestazione non compressa, escludendo la firma compressed.writeBytes (data, 8); // legge il resto, compresso compresso.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // contrassegna come decompressed.writeBytes (intestazione) non compresso; // scrive l'intestazione back decompressed.writeBytes (compresso); // scrive il contenuto non compresso restituito decompresso;
La funzione fa alcune cose:
Successivamente creeremo una comoda utility in Flash per comprimere e decomprimere i file SWF. In un nuovo progetto AS3, compila la seguente classe come una classe di documenti:
pacchetto import flash.display.Sprite; import flash.events.Event; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; public class Compressor estende Sprite private var ref: FileReference; public function Compressor () ref = new FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse ([nuovo FileFilter ("SWF Files", "* .swf")]); load di funzione privata (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 = decompress (ref.data); rompere; caso "FWS": swf = compress (ref.data); rompere; default: throw Error ("Not SWF?"); rompere; nuovo FileReference (). save (swf); private function compress (dati: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var decompressed: ByteArray = new ByteArray (); var compresso: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // legge l'intestazione, escludendo la firma decompressed.writeBytes (data, 8); // leggi il resto decompressed.compress (); compressed.writeMultiByte ("CWS", "us-ascii"); // contrassegna come compresso compresso.writeByte (intestazione); compressed.writeBytes (decompresso); ritorno compresso; private function decompress (data: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var compresso: ByteArray = new ByteArray (); var decompressed: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // legge l'intestazione non compressa, escludendo la firma compressed.writeBytes (data, 8); // legge il resto, compresso compresso.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // contrassegna come decompressed.writeBytes (intestazione) non compresso; // scrive l'intestazione back decompressed.writeBytes (compresso); // scrive il contenuto non compresso restituito decompresso;
Come probabilmente avrai notato, ho aggiunto 2 cose: il caricamento dei file e la funzione di compressione.
La funzione di compressione è identica alla funzione di decompressione, ma al contrario. Il caricamento dei file avviene tramite FileReference (richiesto FP10) e il file caricato è compresso o non compresso. Nota che devi eseguire il SWF localmente da un player standalone, come FileReference.browse () deve essere invocato dall'interazione dell'utente (ma il player locale indipendente consente di eseguirlo senza).
Per testare lo strumento, attivarlo, selezionare il SWF di caricamento e scegliere dove salvarlo. Quindi aprilo con un editor esadecimale e sfoglia. Dovresti vedere le stringhe ascii all'interno in questo modo:
Torniamo al passaggio 2. Mentre il decompilatore non ha mostrato alcuna informazione utile sul SWF protetto, è abbastanza facile ottenere il file SWF dal loader non compresso; basta cercare la firma "CWS" (se il file SWF protetto è la ricerca non compressa di "FWS") e vedere i risultati:
Quello che abbiamo trovato è un tag DefineBinaryData che contiene il file SWF protetto ed estrarlo da lì è un gioco da ragazzi. Stiamo per aggiungere un altro livello di protezione sul file SWF di caricamento: Crittografia.
Per rendere il file SWF protetto meno "accessibile", aggiungeremo una sorta di crittografia. Ho scelto di utilizzare as3crypto e puoi scaricarlo da code.google.com. Puoi usare qualsiasi libreria che vuoi (o la tua implementazione, ancora meglio), l'unico requisito è che dovrebbe essere in grado di crittografare e decrittografare i dati binari usando una chiave.
La prima cosa che vogliamo fare è scrivere un'utilità per crittografare il file SWF protetto prima di incorporarlo. Richiede una conoscenza di base della libreria as3crypto ed è piuttosto semplice. Aggiungi la libreria nel percorso della tua libreria e iniziamo scrivendo quanto segue:
var aes: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // assicurati che possa essere diviso per 16, zero gli ultimi 4 byte per (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);
Cosa sta succedendo qui? Usiamo una classe di as3crypto chiamata AESKey per crittografare il contenuto. La classe crittografa 16 byte in un istante (128 bit) e dobbiamo eseguire il loop dei dati per crittografarli tutti. Nota la seconda riga: data.length e ~ 15. Si assicura che il numero di byte crittografati possa essere diviso per 16 e non si esauriscano i dati durante la chiamata aes.encrypt ().
Nota: In questo caso è importante comprendere il punto di crittografia. Non è realmente crittografia, ma piuttosto offuscamento poiché includiamo la chiave all'interno del file SWF. Lo scopo è quello di trasformare i dati in rifiuti binari, e il codice sopra fa il suo lavoro, anche se può lasciare fino a 15 byte non crittografati (che non ha importanza nel nostro caso). Non sono un crittografo, e sono abbastanza sicuro che il codice di cui sopra potrebbe sembrare zoppo e debole dal punto di vista di un crittografo, ma come ho detto è abbastanza irrilevante in quanto includiamo la chiave all'interno del file SWF.
È ora di creare un'altra utility che ci aiuterà a crittografare i file SWF. È quasi lo stesso del compressore che abbiamo creato in precedenza, quindi non ne parlerò molto. Compilalo in un nuovo progetto come una classe di documenti:
package 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 estende Sprite private var key: String = "activetuts"; // Ho hardcoded la chiave private var ref: FileReference; public function Encryptor () ref = new FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse (); load di funzione privata (e: Event): void ref.addEventListener (Event.COMPLETE, encrypt); ref.load (); private function encrypt (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = new ByteArray (); binKey.writeUTF (chiave); // AESKey richiede una chiave binaria var: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // assicurati che possa essere diviso per 16, zero gli ultimi 4 byte per (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);
Ora eseguilo e crea una copia crittografata del file SWF protetto selezionandolo prima e salvandolo con un nome diverso.
Tornare al progetto SWF in caricamento. Poiché il contenuto è ora crittografato, è necessario modificare il SWF di caricamento e aggiungere il codice di decrittografia. Non dimenticare di cambiare src nel tag Embed per puntare al file SWF crittografato.
package 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 (larghezza = 640, altezza = 423)] // le dimensioni devono essere uguali alla classe pubblica dello swf caricato Main extends Sprite [Embed (source = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // source = percorso per swf che si desidera proteggere contenuto var privato: Classe; private var key: String = "activetuts"; funzione pubblica Main (): void var data: ByteArray = new content (); var binKey: ByteArray = new ByteArray (); binKey.writeUTF (chiave); // AESKey richiede una chiave binaria var: AESKey = new AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); // assicurati che possa essere diviso per 16, zero gli ultimi 4 byte per (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()));
È lo stesso di prima tranne che con il codice di decodifica bloccato nel mezzo. Ora compila il SWF di caricamento e verifica se funziona. Se hai seguito attentamente fino ad ora, il file SWF protetto dovrebbe essere caricato e visualizzato senza errori.
Apri il nuovo SWF di caricamento con un decompilatore e dai un'occhiata.
Contiene oltre un migliaio di righe di codice di crittografia dall'aspetto resistente ed è probabilmente più difficile ottenere il file SWF protetto da esso. Abbiamo aggiunto alcuni altri passaggi che l'utente malintenzionato deve intraprendere:
Il problema è che creare un'utilità è semplice come copiare il decompilatore nell'editor di codice e modificare leggermente il codice. Ho provato a rompere la protezione da solo, ed è stato abbastanza facile - sono riuscito a farlo in circa 5 minuti. Quindi dovremo prendere delle misure contro di esso.
Per prima cosa inseriremmo il file SWF protetto nel file SWF di caricamento, quindi lo abbiamo crittografato e ora aggiungeremo il tocco finale al file SWF di caricamento. Rinomineremo classi, funzioni e variabili in nomi illegali.
Dicendo nomi illegali Intendo nomi come,;! @@, ^ # ^ e (^ _ ^). La cosa bella è che questo è importante per il compilatore ma non per il Flash Player. Quando il compilatore rileva caratteri non validi all'interno degli identificatori, non riesce a analizzarli e quindi il progetto non riesce a compilare. D'altra parte, il giocatore non ha alcun problema con quei nomi illegali. Possiamo compilare il SWF con identificatori legali, decomprimerlo e rinominarli in un gruppo di simboli illegali privi di significato. Il decompilatore emetterà un codice illegale e l'attaccante dovrà scavalcare manualmente le centinaia di righe di codice, rimuovendo gli identificatori illegali prima che possa compilarli. Se lo merita!
Ecco come appare prima di qualsiasi occultamento delle stringhe:
Iniziamo! Decomprimi il caricamento SWF usando l'utility che abbiamo creato prima e accendi un editor esadecimale.
Proviamo a rinominare la classe del documento. Supponendo che tu abbia lasciato il nome originale (Main), cerchiamo nel SWF del loader non compresso con un editor esadecimale:
Rinomina "Principale" a ;;;;. Ora cerca altri "Main" e rinominali ;;;; pure.
Quando si rinomina, assicurarsi di non rinominare le stringhe non necessarie o il SWF non verrà eseguito.
Salva ed esegui il file SWF. Funziona! E guarda cosa dice il decompilatore:
Vittoria!! :)
Continua a rinominare il resto dei tuoi corsi. Scegli un nome di classe e cercalo, sostituendolo con simboli illegali fino a raggiungere la fine del file. Come ho detto, la cosa più importante qui è usare il buon senso, assicurati di non rovinare il tuo SWF. Dopo aver rinominato le classi, puoi iniziare a rinominare i pacchetti. Nota che quando si rinomina un pacchetto, è possibile cancellare anche i periodi e renderlo un nome di pacchetto illegale lungo. Guarda cosa ho fatto:
Dopo aver terminato la ridenominazione delle classi e dei pacchetti, è possibile iniziare a rinominare funzioni e variabili. Sono ancora più facili da rinominare in quanto appaiono di solito solo una volta, in un grande cloud. Anche in questo caso, assicurati di rinominare solo i "tuoi" metodi e non i metodi Flash incorporati. Assicurati di non cancellare la chiave ("activetuts" nel nostro caso).
Dopo aver terminato la rinomina, probabilmente vorresti comprimere il file SWF in modo che sia di dimensioni più ridotte. Nessun problema, possiamo usare l'utility di compressione che abbiamo creato prima e farà il lavoro. Esegui l'utilità, seleziona il file SWF e salvalo con un altro nome.
Aprilo un'ultima volta e dai un'occhiata. Le classi, le variabili e i nomi dei metodi sono offuscati e il file SWF protetto è da qualche parte all'interno, crittografato. Questa tecnica potrebbe essere lenta da applicare all'inizio, ma dopo alcune volte richiede solo pochi minuti.
Qualche tempo fa ho creato un'utilità automatica per iniettare per me il file SWF protetto nel file SWF di caricamento e ha funzionato correttamente. L'unico problema è che se può essere iniettato usando un'utilità automatica, può essere decrittografato usando un'altra utility, quindi se l'utente malintenzionato crea un'utilità, otterrà facilmente tutto il tuo SWF. Per questo motivo preferisco proteggere manualmente i file SWF ogni volta, aggiungendo una leggera modifica, quindi sarebbe più difficile automatizzarli.
Un'altra bella applicazione della tecnica è Blocco del dominio. Invece di decodificare il file SWF con una stringa costante, è possibile decrittografarlo con il dominio su cui è attualmente in esecuzione il file SWF. Quindi, invece di avere un'istruzione if per controllare il dominio, è possibile introdurre un modo più efficace per proteggere il file SWF dal posizionamento su altri siti.
Ultima cosa, potresti voler sostituire il codice di crittografia con la tua implementazione. Perché? Abbiamo investito gli sforzi per rendere illegale il codice crittografico, ma il codice che utilizziamo proviene da una popolare libreria open source e l'autore dell'attacco potrebbe riconoscerlo come tale. Scaricherà una copia pulita e tutto il lavoro di offuscamento non sarà più necessario. D'altra parte, l'utilizzo della propria implementazione richiederà che lui aggiusti tutti i nomi illegali prima che possa continuare.
Poiché il furto di SWF è un grosso problema nel mondo di Flash, ci sono altre opzioni per proteggere i file SWF. Ci sono numerosi programmi là fuori per offuscare AS a livello di bytecode (come SafeSWF di Kindisoft). Scambiano il bytecode compilato e quando il decompilatore tenta di emettere il codice, fallirà e talvolta si bloccherà. Ovviamente questa protezione è migliore in termini di sicurezza ma costa $$$, quindi prima di scegliere come proteggere il tuo SWF considera la quantità di sicurezza necessaria. Se si tratta di proteggere un algoritmo proprietario, il tuo studio Flash da 50 dipendenti è stato sviluppato negli ultimi due anni, puoi prendere in considerazione qualcosa di meglio, rinominando le variabili. D'altra parte, se si desidera impedire ai bambini di inviare punteggi alti falsi, si può prendere in considerazione l'utilizzo di questa tecnica.
Quello che mi piace di questa tecnica è il fatto che il tuo SWF protetto non viene toccato durante l'esecuzione. COME l'offuscamento altera il codice byte e potrebbe danneggiare il file SWF e causare errori (anche se non ne ho mai incontrato nessuno).
Questo è tutto per oggi, spero vi sia piaciuto il tutorial e imparato qualcosa di nuovo! Se hai qualche domanda sentiti libero di lasciare un commento.