Zweimal im Monat besuchen wir einige der Lieblingsbeiträge unserer Leser aus der gesamten Geschichte von Activetuts +. Dieses Tutorial wurde erstmals im Februar 2010 veröffentlicht.
In diesem Tutorial werde ich eine Technik demonstrieren, die ich zum Schutz von Code und Assets vor Diebstahl verwende.
Decompiler sind eine echte Sorge für Menschen, die Flash-Inhalte erstellen. Sie können sich viel Mühe geben, um das beste Spiel zu kreieren. Dann kann jemand es stehlen, das Logo ersetzen und es auf ihrer Website platzieren, ohne Sie zu fragen. Wie? Flash Decompiler verwenden. Wenn Sie Ihre SWF-Datei nicht mit einem gewissen Schutz versehen, kann sie mit einem Tastendruck dekompiliert werden, und der Decompiler gibt lesbaren Quellcode aus.
Ich habe an einem kleinen Projekt von mir gezeigt, wie verwundbar SWFs für die Dekompilierung sind. Sie können es herunterladen und über den Quelllink oben testen. Ich habe Sothink SWF Decompiler 5 verwendet, um die SWF-Datei zu dekompilieren und unter ihre Haube zu schauen. Der Code ist gut lesbar und kann leicht verstanden und wiederverwendet werden.
Ich habe eine Technik zum Schutz von SWFs vor Dekompilern entwickelt, die ich in diesem Tutorial demonstrieren werde. Wir sollten in der Lage sein, dies zu produzieren:
Der dekompilierte Code ist eigentlich der Code zum Entschlüsseln des Inhalts und hat nichts mit Ihrem Hauptcode zu tun. Darüber hinaus sind die Namen illegal, sodass sie nicht zurückkompiliert werden. Versuchen Sie es selbst zu dekompilieren.
Bevor wir loslegen, möchte ich darauf hinweisen, dass dieses Tutorial nicht für Anfänger geeignet ist und Sie über solide Kenntnisse in AS3 verfügen sollten, wenn Sie mitmachen möchten. In diesem Tutorial geht es auch um die Programmierung auf niedriger Ebene, die Bytes, ByteArrays und das Bearbeiten von SWF-Dateien mit einem Hex-Editor umfasst.
Was wir brauchen:
Öffnen Sie ein neues ActionScript 3.0-Projekt, und legen Sie fest, dass es mit Flex SDK kompiliert wird (zum Schreiben von Code verwende ich FlashDevelop). Wählen Sie eine SWF-Datei aus, die Sie schützen möchten, und binden Sie sie mithilfe des Embed-Tags als Binärdaten ein:
[Einbetten (source = "VerletCloth.swf", mimeType = "application / octet-stream")]] // source = Pfad zu der SWF, die der private var-Inhalt geschützt werden soll: Klasse;
Nun ist die SWF als eingebettet ByteArray in den Loader SWF und es kann durch geladen werden Loader.loadBytes ().
var loader: Loader = neuer Loader (); addChild (loader); loader.loadBytes (neuer Inhalt (), neuer LoaderContext (false, neuer ApplicationDomain ()));
Am Ende sollten wir diesen Code haben:
package import flash.display.Loader; import flash.display.Sprite; import flash.system.ApplicationDomain; import flash.system.LoaderContext; [SWF (width = 640, height = 423)] // Die Dimensionen sollten der öffentlichen Klasse des geladenen swf entsprechen. Main erweitert Sprite [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream") ] // source = Pfad zu der SWF, die Sie für den privaten Inhalt von var schützen möchten: Class; öffentliche Funktion Main (): void var loader: Loader = neuer Loader (); addChild (loader); loader.loadBytes (neuer Inhalt (), neuer LoaderContext (false, neuer ApplicationDomain ()));
Kompilieren und sehen, ob es funktioniert (sollte es). Ab jetzt nenne ich die eingebettete SWF die "geschützte SWF", und die SWF haben wir gerade die "Lade-SWF" kompiliert..
Versuchen wir zu dekompilieren und zu sehen, ob es funktioniert.
Ja! Die Assets und der Originalcode sind weg! Was jetzt gezeigt wird, ist der Code, der die geschützte SWF-Datei lädt und nicht den Inhalt. Dies würde wahrscheinlich die meisten der ersten Angreifer stoppen, die sich mit Flash nicht so gut auskennen, aber es ist immer noch nicht gut genug, um Ihre Arbeit vor geschickten Angreifern zu schützen, da die geschützte SWF-Datei darauf wartet, dass sie in der geladenen SWF-Datei nicht berührt wird.
Öffnen wir die Lade-SWF mit einem Hex-Editor:
Es sollte wie zufällige Binärdaten aussehen, da es komprimiert ist und mit ASCII "CWS" beginnen sollte. Wir müssen es dekomprimieren! (Wenn Ihre SWF-Datei mit "FWS" beginnt und Sie sinnvolle Zeichenfolgen in der SWF-Datei sehen, ist es wahrscheinlich, dass sie nicht komprimiert wurde. Sie müssen die Komprimierung aktivieren, um mitzukommen.).
Am Anfang hört sich das vielleicht schwierig an, ist es aber nicht. Das SWF-Format ist ein offenes Format und es gibt ein Dokument, das es beschreibt. Laden Sie es von adobe.com herunter und scrollen Sie im Dokument auf Seite 25 nach unten. Es gibt eine Beschreibung des Headers und wie die SWF-Datei komprimiert wird, sodass wir sie leicht dekomprimieren können.
Dort wird geschrieben, dass die ersten 3 Bytes eine Signatur (CWS oder FWS) sind, das nächste Byte die Flash-Version ist und die nächsten 4 Bytes die Größe der SWF-Datei sind. Der Rest wird komprimiert, wenn die Signatur CWS oder unkomprimiert ist, wenn die Signatur FWS ist. Lassen Sie uns eine einfache Funktion zum Dekomprimieren einer SWF-Datei schreiben:
private Funktion dekomprimieren (Daten: ByteArray): ByteArray var header: ByteArray = neues ByteArray (); var komprimiert: ByteArray = neues ByteArray (); var dekomprimiert: ByteArray = neues ByteArray (); header.writeBytes (data, 3, 5); // Lesen Sie den unkomprimierten Header ohne die Signatur. compress.writeBytes (data, 8); // den Rest lesen, komprimiert komprimiert.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // als unkomprimiert markieren decompressed.writeBytes (header); // schreibe den Header zurück decompressed.writeBytes (komprimiert); // schreibe den jetzt unkomprimierten Inhalt return dekomprimiert;
Die Funktion macht einige Dinge:
Als Nächstes erstellen wir in Flash ein praktisches Hilfsprogramm zum Komprimieren und Dekomprimieren von SWF-Dateien. Übersetzen Sie in einem neuen AS3-Projekt die folgende Klasse als Dokumentklasse:
package import flash.display.Sprite; import flash.events.Event; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; public class Compressor erweitert Sprite private var ref: FileReference; öffentliche Funktion Compressor () ref = new FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse ([new FileFilter ("SWF-Dateien", "* .swf")]); private Funktion laden (e: Event): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load (); private Funktion processSWF (e: Event): void var swf: ByteArray; switch (ref.data.readMultiByte (3, "us-ascii")) Fall "CWS": swf = dekomprimieren (ref.data); brechen; Fall "FWS": swf = compress (ref.data); brechen; Standard: werfen Fehler ("Nicht SWF?"); brechen; new FileReference (). save (swf); private Funktion compress (Daten: ByteArray): ByteArray var-Header: ByteArray = neues ByteArray (); var dekomprimiert: ByteArray = neues ByteArray (); var komprimiert: ByteArray = neues ByteArray (); header.writeBytes (data, 3, 5); // Lesen Sie den Header ohne die Signatur decompressed.writeBytes (data, 8); // den Rest lesen decompressed.compress (); compress.writeMultiByte ("CWS", "us-ascii"); // als komprimiert markieren komprimierte.writeBytes (Header); compress.writeBytes (dekomprimiert); zurückgepresst; private Funktion dekomprimieren (Daten: ByteArray): ByteArray var header: ByteArray = neues ByteArray (); var komprimiert: ByteArray = neues ByteArray (); var dekomprimiert: ByteArray = neues ByteArray (); header.writeBytes (data, 3, 5); // Lesen Sie den unkomprimierten Header ohne die Signatur. compress.writeBytes (data, 8); // den Rest lesen, komprimiert komprimiert.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // als unkomprimiert markieren decompressed.writeBytes (header); // schreibe den Header zurück decompressed.writeBytes (komprimiert); // schreibe den jetzt unkomprimierten Inhalt return dekomprimiert;
Wie Sie wahrscheinlich bemerkt haben, habe ich zwei Dinge hinzugefügt: Datei laden und die Komprimierungsfunktion.
Die Komprimierungsfunktion ist identisch mit der Dekomprimierungsfunktion, jedoch umgekehrt. Das Laden der Datei erfolgt mit FileReference (FP10 erforderlich) und die geladene Datei wird entweder komprimiert oder nicht komprimiert. Beachten Sie, dass Sie die SWF-Datei lokal von einem Standalone-Player aus ausführen müssen FileReference.browse () muss durch Benutzerinteraktion aufgerufen werden (der lokale Standalone-Player kann jedoch ohne ausgeführt werden).
Um das Tool zu testen, starten Sie es, wählen Sie die ladende SWF-Datei aus und legen Sie den Speicherort fest. Dann öffnen Sie es mit einem Hex-Editor und scrub durch. Sie sollten Ascii-Strings wie folgt sehen:
Kehren wir zu Schritt 2 zurück. Obwohl der Decompiler keine nützlichen Informationen über die geschützte SWF-Datei angezeigt hat, ist es recht einfach, die SWF-Datei vom jetzt nicht komprimierten Loader abzurufen. Suchen Sie einfach nach der Signatur "CWS" (wenn die geschützte SWF-Datei unkomprimiert ist, suchen Sie nach "FWS") und sehen Sie die Ergebnisse:
Was wir gefunden haben, ist ein DefineBinaryData-Tag, das die geschützte SWF-Datei enthält, und von dort heraus zu extrahieren ist ein Kinderspiel. Wir sind dabei, eine weitere Schutzschicht über die ladende SWF-Datei hinzuzufügen: Verschlüsselung.
Um die geschützte SWF-Datei weniger "zugänglich" zu machen, werden wir eine Art Verschlüsselung hinzufügen. Ich entschied mich für as3crypto und kann von code.google.com heruntergeladen werden. Sie können stattdessen eine beliebige Bibliothek verwenden (oder Ihre eigene Implementierung, noch besser). Die einzige Voraussetzung ist, dass binäre Daten mit einem Schlüssel ver- und entschlüsselt werden können.
Als erstes wollen wir ein Dienstprogramm schreiben, um die geschützte SWF-Datei zu verschlüsseln, bevor wir sie einbetten. Es erfordert sehr grundlegende Kenntnisse der as3crypto-Bibliothek und ist ziemlich unkompliziert. Fügen Sie die Bibliothek zu Ihrem Bibliothekspfad hinzu und schreiben Sie zunächst Folgendes:
var aes: AESKey = neuer AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // Stellen Sie sicher, dass es durch 16 geteilt werden kann, und setzen Sie die letzten 4 Bytes für (null): (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);
Was ist denn hier los? Wir verwenden eine Klasse von as3crypto namens AESKey, um den Inhalt zu verschlüsseln. Die Klasse verschlüsselt 16 Bytes gleichzeitig (128 Bit), und wir müssen die Daten for-loopen, um sie alle zu verschlüsseln. Beachten Sie die zweite Zeile: data.length & ~ 15. Dadurch wird sichergestellt, dass die Anzahl der verschlüsselten Bytes durch 16 geteilt werden kann und dass beim Aufruf keine Daten ausgehen aes.encrypt ().
Hinweis: In diesem Fall ist es wichtig, den Punkt der Verschlüsselung zu verstehen. Es ist nicht wirklich eine Verschlüsselung, sondern eher eine Verschleierung, da wir den Schlüssel in die SWF-Datei aufnehmen. Der Zweck besteht darin, die Daten in binären Müll zu verwandeln, und der obige Code erfüllt seine Aufgabe, obwohl er bis zu 15 unverschlüsselte Bytes hinterlassen kann (was in unserem Fall keine Rolle spielt). Ich bin kein Kryptograf, und ich bin mir ziemlich sicher, dass der obige Code aus der Sicht eines Kryptografen lahm und schwach aussehen könnte, aber wie gesagt, es ist völlig irrelevant, da wir den Schlüssel in die SWF aufnehmen.
Zeit zum Erstellen eines anderen Dienstprogramms, mit dem SWF-Dateien verschlüsselt werden können. Es ist fast das gleiche wie der Kompressor, den wir zuvor erstellt haben, deshalb werde ich nicht viel darüber reden. Kompilieren Sie es in einem neuen Projekt als Dokumentklasse:
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 erweitert Sprite privater var-Schlüssel: String = "activetuts"; // Ich habe den Schlüssel private var ref codiert: FileReference; öffentliche Funktion Encryptor () ref = new FileReference (); ref.addEventListener (Event.SELECT, load); ref.browse (); private Funktion laden (e: Event): void ref.addEventListener (Event.COMPLETE, encrypt); ref.load (); private Funktion encrypt (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = neues ByteArray (); binKey.writeUTF (Schlüssel); // AESKey erfordert einen binären Schlüssel var aes: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // Vergewissern Sie sich, dass es durch 16 geteilt werden kann, und setzen Sie die letzten 4 Bytes für (null: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);
Führen Sie es jetzt aus und erstellen Sie eine verschlüsselte Kopie der geschützten SWF-Datei, indem Sie sie zuerst auswählen und dann unter einem anderen Namen speichern.
Kehren Sie zum ladenden SWF-Projekt zurück. Da der Inhalt jetzt verschlüsselt ist, müssen Sie die ladende SWF-Datei ändern und Entschlüsselungscode hinzufügen. Vergessen Sie nicht, die src im Embed-Tag so zu ändern, dass sie auf die verschlüsselte SWF-Datei verweist.
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)] // Die Dimensionen sollten der öffentlichen Klasse des geladenen swf entsprechen. Main erweitert Sprite [Embed (source = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // source = Pfad zu der SWF, die Sie für den privaten Inhalt von var schützen möchten: Class; privater var key: String = "activetuts"; öffentliche Funktion Main (): void var data: ByteArray = neuer Inhalt (); var binKey: ByteArray = neues ByteArray (); binKey.writeUTF (Schlüssel); // AESKey erfordert einen binären Schlüssel var aes: AESKey = new AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); // Vergewissern Sie sich, dass es durch 16 geteilt werden kann, und setzen Sie die letzten 4 Bytes für (null: 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()));
Dies ist dasselbe wie zuvor, außer wenn der Entschlüsselungscode in der Mitte steckt. Kompilieren Sie nun die Lade-SWF und testen Sie, ob sie funktioniert. Wenn Sie bisher sorgfältig befolgt haben, sollte die geschützte SWF-Datei fehlerfrei geladen und angezeigt werden.
Öffnen Sie die neue Lade-SWF-Datei mit einem Decompiler und sehen Sie sich das an.
Es enthält über tausend Zeilen hart aussehenden Verschlüsselungscodes, und es ist wahrscheinlich schwieriger, die geschützte SWF-Datei herauszuholen. Wir haben ein paar weitere Schritte hinzugefügt, die der Angreifer durchführen muss:
Das Problem ist, dass das Erstellen eines Dienstprogramms so einfach ist wie das Kopieren und Einfügen aus dem Decompiler in den Code-Editor und das Optimieren des Codes. Ich habe versucht, meinen Schutz selbst zu brechen, und es war ziemlich einfach - ich habe es in etwa 5 Minuten geschafft. Wir müssen also ein paar Maßnahmen dagegen durchführen.
Zuerst haben wir die geschützte SWF-Datei in die Lade-SWF-Datei eingefügt, dann verschlüsselt, und jetzt werden wir die Lade-SWF-Datei mit dem letzten Schliff versehen. Wir benennen Klassen, Funktionen und Variablen in illegale Namen um.
Mit sagen illegale Namen Ich meine Namen wie,! @@, ^ # ^ und (^ _ ^). Das Coole ist, dass dies für den Compiler wichtig ist, nicht für den Flash Player. Wenn der Compiler auf ungültige Zeichen in Bezeichnern stößt, kann er sie nicht analysieren und das Projekt kann daher nicht kompiliert werden. Auf der anderen Seite hat der Spieler keine Probleme mit diesen illegalen Namen. Wir können die SWF-Datei mit legalen Bezeichnern kompilieren, dekomprimieren und in eine Reihe von bedeutungslosen illegalen Symbolen umbenennen. Der Decompiler gibt illegalen Code aus, und der Angreifer muss die Hunderte von Codezeilen manuell durchgehen und illegale Bezeichner entfernen, bevor er ihn kompilieren kann. Er verdient es!
So sieht es vor jeder Verschleierung von Strings aus:
Lasst uns beginnen! Dekomprimieren Sie die Lade-SWF-Datei mit dem zuvor erstellten Dienstprogramm und starten Sie einen Hex-Editor.
Versuchen wir, die Dokumentenklasse umzubenennen. Angenommen, Sie haben den ursprünglichen Namen (Main) verlassen, suchen wir ihn in der unkomprimierten Loader-SWF-Datei mit einem Hex-Editor:
Umbenennen "Main"zu ;;;;. Suchen Sie nun nach anderen "Main" s und benennen Sie diese um ;;;; auch.
Stellen Sie beim Umbenennen sicher, dass Sie keine unnötigen Zeichenfolgen umbenennen, da sonst die SWF-Datei nicht ausgeführt wird.
Speichern Sie die SWF-Datei und führen Sie sie aus. Es klappt! Und schau, was der Decompiler sagt:
Sieg!! :)
Benennen Sie den Rest Ihrer Klassen um. Wählen Sie einen Klassennamen aus und suchen Sie ihn nach. Ersetzen Sie ihn durch unzulässige Symbole, bis Sie das Ende der Datei erreichen. Wie gesagt, das Wichtigste ist, dass Sie Ihren gesunden Menschenverstand einsetzen und sicherstellen, dass Sie Ihre SWF-Dateien nicht durcheinander bringen. Nach dem Umbenennen der Klassen können Sie die Pakete umbenennen. Beachten Sie, dass Sie beim Umbenennen eines Pakets auch die Punkte löschen und einen langen ungültigen Paketnamen angeben können. Schau, was ich gemacht habe:
Nachdem Sie die Klassen und Pakete umbenannt haben, können Sie Funktionen und Variablen umbenennen. Sie sind noch einfacher umzubenennen, da sie normalerweise nur einmal in einer großen Wolke erscheinen. Stellen Sie sicher, dass Sie nur "Ihre" Methoden umbenennen und nicht die integrierten Flash-Methoden. Stellen Sie sicher, dass Sie den Schlüssel nicht auslöschen (in unserem Fall "activetuts").
Nach dem Umbenennen möchten Sie wahrscheinlich die SWF-Datei komprimieren, damit sie kleiner wird. Kein Problem, wir können das zuvor erstellte Komprimierungsprogramm verwenden, das die Arbeit erledigen wird. Führen Sie das Dienstprogramm aus, wählen Sie die SWF-Datei aus und speichern Sie sie unter einem anderen Namen.
Öffne es ein letztes Mal und schau es dir an. Die Klassen, die Variablen und die Methodennamen werden verschleiert, und die geschützte SWF-Datei ist irgendwo in der Datenbank verschlüsselt. Diese Technik kann zunächst langsam angewendet werden, dauert jedoch nach einigen wenigen Minuten nur wenige Minuten.
Vor einiger Zeit habe ich ein automatisches Dienstprogramm erstellt, um die geschützte SWF-Datei für mich in die Lade-SWF-Datei zu injizieren, und es hat gut funktioniert. Das einzige Problem ist, dass es, wenn es mit einem automatischen Dienstprogramm injiziert werden kann, mit einem anderen Dienstprogramm entschlüsselt werden kann. Wenn der Angreifer ein Dienstprogramm dafür erstellt, erhält er alle SWF-Dateien. Aus diesem Grund ziehe ich es vor, die SWF-Dateien jedes Mal manuell zu schützen, indem eine geringfügige Änderung vorgenommen wird, sodass die Automatisierung schwieriger ist.
Eine weitere schöne Anwendung der Technik ist Domainsperrung. Anstatt die SWF-Datei mit einer konstanten Zeichenfolge zu entschlüsseln, können Sie sie mit der Domäne entschlüsseln, auf der die SWF-Datei gerade ausgeführt wird. Anstatt eine if-Anweisung zur Überprüfung der Domäne zu haben, können Sie eine leistungsfähigere Methode einführen, um die SWF-Datei vor der Platzierung auf anderen Websites zu schützen.
Als letztes möchten Sie vielleicht den Verschlüsselungscode durch Ihre eigene Implementierung ersetzen. Warum? Wir haben uns bemüht, den Krypto-Code illegal zu machen, aber der von uns verwendete Code stammt aus einer beliebten Open-Source-Bibliothek, und der Angreifer könnte ihn als solchen erkennen. Er wird eine saubere Kopie herunterladen und alle Verschleierungsarbeiten werden unnötig. Wenn Sie jedoch Ihre eigene Implementierung verwenden, muss er alle illegalen Namen korrigieren, bevor er fortfahren kann.
Da der Diebstahl von SWF-Dateien in der Flash-Welt ein großes Problem darstellt, gibt es andere Optionen zum Schutz von SWF-Dateien. Es gibt zahlreiche Programme, mit denen AS auf Bytecode-Ebene verschleiert werden kann (wie Kindisofts secureSWF). Sie machen den kompilierten Bytecode durcheinander, und wenn der Decompiler versucht, Code auszugeben, schlägt er fehl und stürzt manchmal sogar ab. Natürlich ist dieser Schutz in Bezug auf die Sicherheit besser, aber er kostet $$$. Bevor Sie sich also für den Schutz Ihrer SWF entscheiden, sollten Sie die erforderliche Sicherheitsstufe in Betracht ziehen. Wenn es um den Schutz eines proprietären Algorithmus geht, den Ihr 50-Mitarbeiter-Flash-Studio in den letzten zwei Jahren entwickelt hat, denken Sie vielleicht etwas besser als das Umbenennen der Variablen. Auf der anderen Seite, wenn Sie verhindern möchten, dass die Kleinen falsche Highscores übermitteln, sollten Sie diese Technik in Betracht ziehen.
Was mir an dieser Technik gefällt, ist die Tatsache, dass Ihre geschützte SWF-Datei beim Ausführen unberührt bleibt. AS-Verschleierung stört den Byte-Code und es könnte möglicherweise die SWF-Datei beschädigt und Fehler verursacht werden (obwohl ich selbst noch keinen gefunden habe).
Das ist alles für heute, ich hoffe, Sie haben das Tutorial genossen und etwas Neues gelernt! Wenn Sie Fragen haben, können Sie gerne einen Kommentar hinterlassen.