Deux fois par mois, nous revoyons certains des articles préférés de nos lecteurs dans l’histoire d’Activetuts +. Ce tutoriel a été publié pour la première fois en février 2010.
Dans ce tutoriel, je vais démontrer une technique que j'utilise pour protéger le code et les actifs contre le vol.
Les décompilateurs sont un réel souci pour ceux qui créent du contenu Flash. Vous pouvez faire beaucoup d'efforts pour créer le meilleur jeu sur le marché, puis quelqu'un peut le voler, remplacer le logo et le mettre sur leur site sans vous le demander. Comment? Utiliser un décompilateur flash. Si vous ne protégez pas votre fichier SWF, vous pouvez le décompiler en appuyant simplement sur un bouton. Le décompilateur générera un code source lisible..
J'ai utilisé un de mes petits projets pour montrer à quel point les fonds souverains sont vulnérables à la décompilation. Vous pouvez le télécharger et vous tester via le lien source ci-dessus. J'ai utilisé Sothink SWF Decompiler 5 pour décompiler le fichier SWF et regarder sous son capot. Le code est assez lisible et vous pouvez le comprendre et le réutiliser assez facilement.
J'ai mis au point une technique permettant de protéger les fichiers SWF des décompilateurs et je vais vous en faire la démonstration dans ce didacticiel. Nous devrions pouvoir produire ceci:
Le code décompilé est en réalité le code de déchiffrement du contenu et n'a rien à voir avec votre code principal. De plus, les noms sont illégaux et ne seront donc pas compilés. Essayez de le décompiler vous-même.
Avant de commencer, je tiens à souligner que ce didacticiel ne convient pas aux débutants et que vous devez posséder de solides connaissances en AS3 si vous souhaitez suivre. Ce tutoriel traite également de la programmation de bas niveau impliquant des octets, ByteArrays et de la manipulation de fichiers SWF avec un éditeur hexadécimal..
Voici ce dont nous avons besoin:
Ouvrez un nouveau projet ActionScript 3.0 et configurez-le pour la compilation avec Flex SDK (j'utilise FlashDevelop pour écrire du code). Choisissez un fichier SWF que vous souhaitez protéger et intégrez-le sous forme de données binaires à l'aide de la balise Embed:
[Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream")] // source = chemin du fichier swf que vous souhaitez protéger, contenu var privé: Class;
Maintenant, le SWF est intégré en tant que ByteArray dans le chargeur SWF et il peut être chargé à travers Loader.loadBytes ().
var loader: Loader = new Loader (); addChild (chargeur); loader.loadBytes (nouveau contenu (), nouveau LoaderContext (false, nouveau ApplicationDomain ()));
En fin de compte, nous devrions avoir ce code:
package import flash.display.Loader; import flash.display.Sprite; import flash.system.ApplicationDomain; import flash.system.LoaderContext; [SWF (width = 640, height = 423)] // les dimensions doivent être identiques à celles de la classe publique Main du swf chargé etend Sprite [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream") ] // source = chemin du fichier swf que vous souhaitez protéger, contenu var privé: Class; fonction publique Main (): void var loader: Loader = new Loader (); addChild (chargeur); loader.loadBytes (nouveau contenu (), nouveau LoaderContext (false, nouveau ApplicationDomain ()));
Compilez et voyez si cela fonctionne (cela devrait). À partir de maintenant, je vais appeler le fichier SWF intégré le "fichier SWF protégé" et le fichier SWF que nous venons de compiler le "fichier SWF de chargement"..
Essayons de décompiler et de voir si cela fonctionne.
Yey! Les actifs et le code d'origine ont disparu! Ce qui est montré maintenant est le code qui charge le fichier SWF protégé et non son contenu. Cela arrêterait probablement la plupart des attaquants débutants qui ne sont pas trop familiarisés avec Flash, mais cela ne suffit pas encore pour protéger votre travail des attaquants qualifiés, car le fichier SWF protégé les attend intacts dans le fichier SWF en cours de chargement..
Ouvrons le fichier SWF de chargement avec un éditeur hexadécimal:
Cela devrait ressembler à des données binaires aléatoires car elles sont compressées et devraient commencer par ASCII "CWS". Nous devons le décompresser! (Si votre fichier SWF commence par "FWS" et que vous voyez des chaînes significatives dans le fichier SWF, il est probable qu'il ne soit pas compressé. Vous devez activer la compression pour suivre.).
Au début, cela peut sembler difficile, mais ce n’est pas le cas. Le format SWF est un format ouvert et un document le décrit. Téléchargez-le sur adobe.com et faites défiler jusqu'à la page 25 du document. Il existe une description de l’en-tête et de la compression du fichier SWF afin que nous puissions le décompresser facilement..
Ce qui est écrit ici est que les 3 premiers octets sont une signature (CWS ou FWS), l’octet suivant est la version Flash, les 4 prochains octets sont la taille du fichier SWF. Le reste est compressé si la signature est CWS ou non compressé si la signature est FWS. Écrivons une fonction simple pour décompresser un fichier SWF:
fonction de décompression privée (data: ByteArray): ByteArray en-tête var: ByteArray = new ByteArray (); var compressé: ByteArray = new ByteArray (); var décompressed: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // lit l'en-tête non compressé, à l'exclusion de la signature comprim.writeBytes (data, 8); // lit le reste, compressé compressé. décompresser (); decompressed.writeMultiByte ("FWS", "us-ascii"); // marque comme décompressé decompressed.writeBytes (en-tête); // réécrit l'en-tête decompressed.writeBytes (compressé); // écrit le contenu maintenant non compressé return decompressed;
La fonction fait quelques choses:
Nous allons ensuite créer un utilitaire pratique dans Flash pour la compression et la décompression de fichiers SWF. Dans un nouveau projet AS3, compilez la classe suivante en tant que classe de document:
package import flash.display.Sprite; import flash.events.Event; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; Classe publique Compressor étend Sprite private var ref: FileReference; fonction publique Compressor () ref = new FileReference (); ref.addEventListener (Event.SELECT, charge); ref.browse ([new FileFilter ("Fichiers SWF", "* .swf")]); charge de fonction privée (e: Event): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load (); fonction privée processSWF (e: événement): void var swf: ByteArray; switch (ref.data.readMultiByte (3, "us-ascii")) case "CWS": swf = decompress (ref.data); Pause; case "FWS": swf = compresser (ref.data); Pause; défaut: throw Error ("Pas SWF?"); Pause; new FileReference (). save (swf); fonction privée compresser (données: ByteArray): ByteArray en-tête var: ByteArray = new ByteArray (); var décompressed: ByteArray = new ByteArray (); var compressé: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // lit l'en-tête, à l'exception de la signature decompressed.writeBytes (data, 8); // lit le reste decompressed.compress (); comprimée.writeMultiByte ("CWS", "us-ascii"); // marque comme compressé compressé.writeBytes (en-tête); comprimé.writeBytes (décompressé); retour compressé; fonction privée décompresser (data: ByteArray): ByteArray en-tête var: ByteArray = new ByteArray (); var compressé: ByteArray = new ByteArray (); var décompressed: ByteArray = new ByteArray (); header.writeBytes (data, 3, 5); // lit l'en-tête non compressé, à l'exclusion de la signature comprim.writeBytes (data, 8); // lit le reste, compressé compressé. décompresser (); decompressed.writeMultiByte ("FWS", "us-ascii"); // marque comme décompressé decompressed.writeBytes (en-tête); // réécrit l'en-tête decompressed.writeBytes (compressé); // écrit le contenu maintenant non compressé return decompressed;
Comme vous l'avez probablement remarqué, j'ai ajouté 2 choses: le chargement de fichier et la fonction de compression.
La fonction de compression est identique à la fonction de décompression, mais en sens inverse. Le chargement du fichier est effectué à l'aide de FileReference (FP10 requis) et le fichier chargé est compressé ou non compressé. Notez que vous devez exécuter le fichier SWF localement à partir d’un lecteur autonome, FileReference.browse () doit être appelé par interaction utilisateur (mais le lecteur autonome local permet de l'exécuter sans).
Pour tester l'outil, lancez-le, sélectionnez le fichier SWF de chargement, puis choisissez l'emplacement où l'enregistrer. Puis ouvrez-le avec un éditeur hexadécimal et parcourez-le. Vous devriez voir les chaînes ascii à l'intérieur comme ceci:
Revenons à l’étape 2. Bien que le décompilateur n’affiche aucune information utile sur le fichier SWF protégé, il est assez facile d’obtenir le fichier SWF à partir du programme de chargement non compressé; il suffit de rechercher la signature "CWS" (si le fichier SWF protégé est non compressé, de rechercher "FWS") et d'afficher les résultats suivants:
Ce que nous avons trouvé est une balise DefineBinaryData contenant le fichier SWF protégé et dont l'extraction est extrêmement simple. Nous sommes sur le point d’ajouter une autre couche de protection sur le fichier SWF chargé: chiffrement.
Pour rendre le fichier SWF protégé moins "accessible", nous allons ajouter une sorte de cryptage. J'ai choisi d'utiliser as3crypto et vous pouvez le télécharger à partir de code.google.com. Vous pouvez utiliser n'importe quelle bibliothèque de votre choix (ou votre propre implémentation, encore mieux), la seule condition requise est de pouvoir crypter et décrypter des données binaires à l'aide d'une clé.
La première chose à faire est d'écrire un utilitaire pour chiffrer le fichier SWF protégé avant de l'intégrer. Cela nécessite une connaissance très basique de la bibliothèque as3crypto et c'est assez simple. Ajoutez la bibliothèque dans le chemin de votre bibliothèque et commençons par écrire ce qui suit:
var aes: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // assurez-vous qu'il peut être divisé par 16, mettez à zéro les 4 derniers octets de (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);
Que se passe t-il ici? Nous utilisons une classe de as3crypto appelée AESKey pour chiffrer le contenu. La classe chiffre 16 octets à la fois (128 bits), et nous devons effectuer une boucle sur les données pour tout chiffrer. Notez la deuxième ligne: data.length & ~ 15. Il s'assure que le nombre d'octets cryptés peut être divisé par 16 et nous ne manquons pas de données lorsque nous appelons. aes.encrypt ().
Remarque: Il est important de comprendre le problème du cryptage dans ce cas. Ce n'est pas vraiment un cryptage, mais plutôt une obfuscation puisque nous incluons la clé dans le fichier SWF. Le but est de transformer les données en déchets binaires, et le code ci-dessus fait son travail, bien qu'il puisse laisser jusqu'à 15 octets non cryptés (ce qui n'a pas d'importance dans notre cas). Je ne suis pas un cryptographe et je suis tout à fait sûr que le code ci-dessus pourrait paraître boiteux et faible du point de vue d'un cryptographe, mais comme je l'ai dit, il est tout à fait hors de propos puisque nous incluons la clé dans le SWF..
Il est temps de créer un autre utilitaire qui nous aidera à chiffrer les fichiers SWF. C'est presque la même chose que le compresseur que nous avons créé plus tôt, alors je ne vais pas en parler beaucoup. Compilez-le dans un nouveau projet en tant que classe de document:
package import com.hurlant.crypto.symmetric.AESKey; import flash.display.Sprite; import flash.events.Event; import flash.net.FileReference; import flash.utils.ByteArray; Classe publique Encryptor étend Sprite clé privée var: String = "activetuts"; // J'ai codé en dur la clé private var ref: FileReference; fonction publique Encryptor () ref = new FileReference (); ref.addEventListener (Event.SELECT, charge); ref.browse (); charge de fonction privée (e: Event): void ref.addEventListener (Event.COMPLETE, encrypt); ref.load (); fonction privée encrypt (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = new ByteArray (); binKey.writeUTF (clé); // AESKey nécessite une clé binaire var aes: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // assurez-vous qu'il peut être divisé par 16, remettez à zéro les 4 derniers octets de (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);
Exécutez-le maintenant et créez une copie chiffrée du fichier SWF protégé en le sélectionnant d'abord, puis en l'enregistrant sous un autre nom..
Revenez au projet de chargement SWF. Le contenu étant maintenant crypté, nous devons modifier le fichier SWF de chargement et y ajouter du code de décryptage. N'oubliez pas de modifier le code src dans la balise incorporée pour qu'il pointe vers le fichier SWF crypté..
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 (width = 640, height = 423)] // les dimensions doivent être identiques à celles de la classe publique Main du swf chargé etend Sprite [Embed (source = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // source = chemin du fichier swf que vous souhaitez protéger, contenu var privé: Class; clé var privée: String = "activetuts"; fonction publique Main (): void var data: ByteArray = nouveau contenu (); var binKey: ByteArray = new ByteArray (); binKey.writeUTF (clé); // AESKey nécessite une clé binaire var aes: AESKey = new AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); // assurez-vous qu'il peut être divisé par 16, remettez à zéro les 4 derniers octets de (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()));
C’est la même chose que précédemment, sauf que le code de décryptage est bloqué au milieu. Maintenant, compilez le fichier SWF de chargement et testez-le. Si vous avez suivi attentivement jusqu'à présent, le fichier SWF protégé devrait se charger et s'afficher sans erreur..
Ouvrez le nouveau fichier SWF de chargement avec un décompilateur et jetez un œil.
Il contient plus de mille lignes de code de cryptage robuste et il est probablement plus difficile d’en extraire le fichier SWF protégé. Nous avons ajouté quelques étapes supplémentaires que l'attaquant doit entreprendre:
Le problème est que la création d'un utilitaire est aussi simple que de copier-coller du décompilateur dans l'éditeur de code et de modifier légèrement le code. J'ai essayé de casser ma protection moi-même, et c'était assez facile - j'ai réussi à le faire en 5 minutes environ. Nous allons donc devoir prendre des mesures contre elle.
Nous avions d’abord mis le fichier SWF protégé dans le fichier SWF de chargement, puis nous l’avions chiffré. Nous allons maintenant mettre la dernière main au fichier SWF de chargement. Nous allons renommer les classes, les fonctions et les variables en noms illégaux..
En disant noms illégaux Je veux dire des noms tels que,!! @@, ^ # ^ et (^ _ ^). La chose intéressante est que cela compte pour le compilateur mais pas pour le Flash Player. Lorsque le compilateur rencontre des caractères illégaux dans des identificateurs, il ne parvient pas à les analyser et le projet ne parvient donc pas à se compiler. D'autre part, le joueur n'a aucun problème avec ces noms illégaux. Nous pouvons compiler le fichier SWF avec des identificateurs légaux, le décompresser et le renommer en un ensemble de symboles illégaux sans signification. Le décompilateur générera du code illégal et l'attaquant devra parcourir manuellement des centaines de lignes de code, en supprimant les identifiants illégaux avant de pouvoir le compiler. Il le mérite!
Voici à quoi cela ressemble avant toute obfuscation de chaîne:
Commençons! Décompressez le fichier SWF de chargement à l'aide de l'utilitaire créé précédemment et lancez un éditeur hexadécimal.
Essayons de renommer la classe de document. En supposant que vous ayez laissé le nom d'origine (Main), recherchons-le dans le fichier SWF non compressé du chargeur avec un éditeur hexadécimal:
Renommer "Principale" à ;;;;. Maintenant, recherchez d’autres "Main" et renommez-les en ;;;; aussi.
Lorsque vous renommez, assurez-vous de ne pas renommer les chaînes inutiles, sinon le fichier SWF ne s'exécutera pas..
Enregistrez et exécutez le fichier SWF. Ça marche! Et regardez ce que dit le décompilateur:
La victoire!! :)
Continuez à renommer le reste de vos cours. Choisissez un nom de classe et recherchez-le en le remplaçant par des symboles illégaux jusqu'à la fin du fichier. Comme je l'ai dit, la chose la plus importante ici est d'utiliser votre bon sens, assurez-vous de ne pas gâcher votre fichier SWF. Après avoir renommé les classes, vous pouvez commencer à renommer les packages. Notez que lorsque vous renommez un paquet, vous pouvez aussi effacer les points et en faire un nom de paquet illégal. Regarde ce que j'ai fait:
Une fois que vous avez fini de renommer les classes et les packages, vous pouvez commencer à renommer les fonctions et les variables. Ils sont même plus faciles à renommer car ils apparaissent généralement une seule fois, dans un seul grand nuage. Encore une fois, assurez-vous de ne renommer que "vos" méthodes et non les méthodes Flash intégrées. Assurez-vous de ne pas effacer la clé ("activetuts" dans notre cas).
Une fois le changement de nom effectué, vous souhaiterez probablement compresser le fichier SWF afin qu’il soit plus petit. Aucun problème, nous pouvons utiliser l'utilitaire de compression que nous avons créé auparavant et il fera le travail. Exécutez l'utilitaire, sélectionnez le fichier SWF et enregistrez-le sous un autre nom..
Ouvrez-le une dernière fois et jetez un coup d'oeil. Les classes, les variables et les noms de méthodes sont obscurcis et le fichier SWF protégé est quelque part à l'intérieur, chiffré. Cette technique peut être lente à appliquer au début, mais après quelques fois, cela ne prend que quelques minutes.
Il y a quelque temps, j'ai créé un utilitaire automatique pour injecter le fichier SWF protégé pour moi dans le fichier SWF de chargement, et cela a bien fonctionné. Le seul problème est que s'il peut être injecté à l'aide d'un utilitaire automatique, il peut être déchiffré à l'aide d'un autre utilitaire. Par conséquent, si l'attaquant crée un utilitaire à cet effet, il obtiendra facilement tous vos fichiers SWF. Pour cette raison, je préfère protéger les fichiers SWF manuellement à chaque fois, en y ajoutant une légère modification pour rendre plus difficile l’automatisation..
Une autre belle application de la technique est Verrouillage de domaine. Au lieu de déchiffrer le fichier SWF avec une chaîne constante, vous pouvez le déchiffrer avec le domaine sur lequel le fichier SWF est actuellement exécuté. Ainsi, au lieu d’avoir une instruction if pour vérifier le domaine, vous pouvez introduire un moyen plus puissant de protéger le fichier SWF de son emplacement sur d’autres sites..
Dernière chose, vous voudrez peut-être remplacer le code de cryptage par votre propre implémentation. Pourquoi? Nous avons déployé des efforts considérables pour rendre le code cryptique illégal, mais le code que nous utilisons provient d'une bibliothèque open source populaire et l'attaquant pourrait le reconnaître comme tel. Il téléchargera une copie vierge et tous les travaux d’obscurcissement seront inutiles. D'autre part, l'utilisation de votre propre implémentation l'obligera à corriger tous les noms illégaux avant de pouvoir continuer..
Le vol de fichiers SWF étant un problème majeur dans le monde Flash, il existe d'autres options pour protéger les fichiers SWF. Il existe de nombreux programmes permettant de masquer AS au niveau du bytecode (comme le secureSWF de Kindisoft). Ils bousillent le bytecode compilé et lorsque le décompilateur tente de générer du code, il échoue et peut même planter. Bien sûr, cette protection est meilleure en termes de sécurité, mais elle coute $$$, donc, avant de choisir le moyen de protéger votre SWF, prenez en compte le niveau de sécurité requis. S'il s'agit de protéger un algorithme propriétaire développé par votre studio Flash depuis 50 ans depuis 50 ans, vous pouvez envisager quelque chose de mieux que de renommer les variables. D'autre part, si vous souhaitez empêcher les enfants de soumettre de faux meilleurs scores, vous pouvez envisager d'utiliser cette technique..
Ce qui me plaît dans cette technique est le fait que votre fichier SWF protégé n’est pas touché lorsqu’il est exécuté. AS obfuscation altère le code des octets, ce qui pourrait endommager le fichier SWF et causer des erreurs (bien que je n’en ai pas rencontré moi-même).
C'est tout pour aujourd'hui, j'espère que vous avez apprécié le tutoriel et appris quelque chose de nouveau! Si vous avez des questions, n'hésitez pas à laisser un commentaire.