De două ori pe lună, revizuim câteva postări preferate ale cititorilor noștri de-a lungul istoriei Activetuts +. Acest tutorial a fost publicat pentru prima oară în februarie 2010.
În acest tutorial voi demonstra o tehnică pe care o folosesc pentru a proteja codul și bunurile de furt.
Decompilarele reprezintă o problemă reală pentru persoanele care creează conținut Flash. Puteți depune mult efort pentru a crea cel mai bun joc acolo, atunci cineva îl poate fura, înlocui logo-ul și pune-l pe site-ul lor fără a vă întreba. Cum? Folosind un Flash Decompiler. Cu excepția cazului în care puneți o anumită protecție peste SWF-ul dvs., acesta poate fi decomprimat printr-o apăsare de buton, iar decompilatorul va emite cod sursă citit.
Am folosit un mic proiect al meu pentru a demonstra cât de vulnerabile sunt SWF-urile la decompilare. Puteți să o descărcați și să vă testați prin linkul sursă de mai sus. Am folosit Sothink SWF Decompiler 5 pentru a decompila SWF și uita-te sub capota lui. Codul este destul de lizibil și îl puteți înțelege și reutiliza destul de ușor.
Am venit cu o tehnică pentru protejarea SWF-urilor de decompilatoare și o voi demonstra în acest tutorial. Ar trebui să fim capabili să producem acest lucru:
Codul care este decomprimat este de fapt codul pentru decriptarea conținutului și nu are nimic de-a face cu codul principal. În plus, numele sunt ilegale, așa că nu se vor compila înapoi. Încercați să o decompilați singuri.
Înainte de a merge, vreau să subliniez că acest tutorial nu este potrivit pentru începători și ar trebui să aveți cunoștințe solide despre AS3 dacă doriți să urmați. Acest tutorial este, de asemenea, despre programe de nivel scăzut care implică octeți, ByteArrays și manipularea fișierelor SWF cu un editor hex.
Iată ce avem nevoie:
Deschideți un nou proiect ActionScript 3.0 și setați-l să se compileze cu Flex SDK (folosesc FlashDevelop pentru a scrie cod). Alegeți un SWF pe care doriți să îl protejați și îl încorporați ca date binare utilizând eticheta Embed:
[Embed (sursă = "VerletCloth.swf", mimeType = "aplicație / octet-stream")] // source = calea spre swf pe care doriți să o protejați conținut privat var: Class;
Acum SWF este încorporat ca a ByteArray în SWF încărcător și poate fi încărcat prin Loader.loadBytes ().
var loader: Loader = încărcător nou (); addChild (încărcător); loader.loadBytes (noul conținut (), noul LoaderContext (false, noul ApplicationDomain ()));
În final, ar trebui să avem acest cod:
pachet import flash.display.Loader; import flash.display.Sprite; import flash.system.ApplicationDomain; import flash.system.LoaderContext; [SWF (lățime = 640, înălțime = 423)] // dimensiunile ar trebui să fie aceleași ca și clasa publică încărcată a swf-ului. Extensie principală Sprite [Embed (sursă = "VerletCloth.swf", mimeType = "aplicație / octet-stream") ] // source = calea catre swf pe care doriti sa o protejati privat content var: Class; funcția publică Main (): void var loader: Loader = încărcător nou (); addChild (încărcător); loader.loadBytes (noul conținut (), noul LoaderContext (false, noul ApplicationDomain ()));
Compilați și vedeți dacă funcționează (ar trebui). De acum încolo voi numi SWF-ul încorporat "SWF protejat" și SWF-ul pe care tocmai l-am compilat "SWF de încărcare".
Să încercăm să decomprimăm și să vedem dacă funcționează.
Yey! Activele și codul original au dispărut! Ce se arată acum este codul care încarcă SWF protejat și nu conținutul său. Acest lucru ar opri, probabil, majoritatea atacatorilor pentru prima dată care nu sunt prea familiarizați cu Flash, dar încă nu este suficient de bun pentru a vă proteja munca de la atacatori calificați deoarece SWF protejat le așteaptă să fie neatinsi în interiorul SWF de încărcare.
Să deschidem SWF de încărcare cu un editor hexagonal:
Ar trebui să arate ca date binare aleatorii, deoarece este comprimat și ar trebui să înceapă cu ASCII "CWS". Trebuie să o decomprimăm! (Dacă SWF începe cu "FWS" și vedeți șiruri semnificative în SWF, probabil că nu a fost comprimat. Trebuie să activați compresia de urmat).
La început s-ar putea suna dificil, dar nu este. Formatul SWF este un format deschis și există un document care îl descrie. Descărcați-l de la adobe.com și derulați în jos până la pagina 25 din document. Există o descriere a antetului și modul în care SWF este comprimat, astfel încât să îl putem decomprima ușor.
Ce este scris acolo este că primii 3 octeți sunt o semnătură (CWS sau FWS), următorul octet este versiunea Flash, următorii 4 octeți sunt dimensiunea SWF. Restul este comprimat dacă semnătura este CWS sau necomprimată dacă semnătura este FWS. Să scriem o funcție simplă pentru a decomprima un SWF:
funcția privată decomprimare (date: ByteArray): ByteArray var header: ByteArray = nou ByteArray (); var comprimat: ByteArray = ByteArray nou (); var decomprimat: ByteArray = ByteArray nou (); header.writeBytes (date, 3, 5); // citiți antetul necomprimat, excluzând semnătura compressed.writeBytes (date, 8); // citiți restul, comprimate compressed.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // marcați ca necomprimat decompressed.writeBytes (header); // scrieți antetul înapoi decompressed.writeBytes (comprimat); // scrieți decomprimat returnarea de conținut acum necomprimată;
Funcția are câteva lucruri:
Apoi vom crea un utilitar la îndemână în Flash pentru a comprima și decomprima fișierele SWF. Într-un nou proiect AS3, compilați următoarea clasă ca o clasă de documente:
pachet import flash.display.Sprite; importul flash.events.Event; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; public class Compressor extinde Sprite private var ref: FileReference; funcție publică Compressor () ref = new FileReference (); ref.addEventListener (eveniment.SELECT, încărcare); ref.browse ([new FileFilter ("Fișiere SWF", "* .swf")]); încărcarea funcției private (e: Event): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load (); funcția privată processSWF (e: Event): void var swf: ByteArray; switch (ref.data.readMultiByte (3, "us-ascii")) caz "CWS": swf = decompress (ref.data); pauză; cazul "FWS": swf = compress (ref.data); pauză; implicit: arunca Eroare ("Nu SWF?"); pauză; fișier nou (). save (swf); funcția privată compress (date: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var decomprimat: ByteArray = ByteArray nou (); var comprimat: ByteArray = ByteArray nou (); header.writeBytes (date, 3, 5); // citiți antetul, excluzând semnătura decompressed.writeBytes (date, 8); / / citiți restul decompressed.compress (); compressed.writeMultiByte ("CWS", "us-ascii"); // marcați ca comprimat compressed.writeBytes (antet); compressed.writeBytes (decomprimat); retur comprimat; funcția privată decompress (date: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var comprimat: ByteArray = ByteArray nou (); var decomprimat: ByteArray = ByteArray nou (); header.writeBytes (date, 3, 5); // citiți antetul necomprimat, excluzând semnătura compressed.writeBytes (date, 8); // citiți restul, comprimate compressed.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // marcați ca necomprimat decompressed.writeBytes (header); // scrieți antetul înapoi decompressed.writeBytes (comprimat); // scrieți decomprimat returnarea de conținut acum necomprimată;
După cum probabil ați observat am adăugat două lucruri: încărcarea fișierelor și funcția de compresie.
Funcția de compresare este identică cu funcția decompresie, dar în sens invers. Încărcarea fișierelor se face utilizând FileReference (este necesar FP10) iar fișierul încărcat este fie comprimat, fie necomprimat. Rețineți că trebuie să rulați SWF local de la un jucător independent, ca FileReference.browse () trebuie să fie invocată de interacțiunea cu utilizatorul (dar playerul autonom autonom permite rularea acestuia fără).
Pentru a testa instrumentul, închideți-l, selectați SWF de încărcare și alegeți unde să îl salvați. Apoi deschideți-l cu un editor hexagonal și curățați-l. Ar trebui să vedeți șiruri de ascii în interiorul asfel:
Să revenim la pasul 2. În timp ce decompilatorul nu a afișat informații utile despre SWF protejat, este destul de ușor să obțineți SWF-ul de la încărcătorul acum necomprimat; căutați doar semnătura "CWS" (dacă SWF protejat este căutare necomprimată pentru "FWS") și vedeți rezultatele:
Ce am găsit este o etichetă DefineBinaryData care conține fișierul SWF protejat și extragerea de acolo este o bucată de prăjitură. Suntem pe punctul de a adăuga un alt nivel de protecție peste încărcarea SWF: criptare.
Pentru a face SWF protejat mai puțin "accesibil", vom adăuga un fel de criptare. Am ales să utilizez as3crypto și îl puteți descărca de la code.google.com. Puteți utiliza orice bibliotecă pe care o doriți (sau o implementare proprie, chiar mai bună), singura cerință este aceea că ar trebui să fie capabilă să cripteze și să decripteze datele binare utilizând o cheie.
Primul lucru pe care vrem să-l facem este să scriem un utilitar pentru a cripta SWF-ul protejat înainte să îl încorporăm. Este nevoie de cunoștințe de bază despre biblioteca as3crypto și este destul de simplă. Adăugați biblioteca în calea bibliotecii dvs. și începeți prin a scrie următoarele:
var aes: AESKey = nou AESKey (binKey); var bytesToEncrypt: int = (date.length & ~ 15); // asigurați-vă că poate fi împărțit la 16, zero la ultimii 4 octeți pentru (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);
Ce se petrece aici? Utilizăm o clasă de la as3crypto numită AESKey pentru a cripta conținutul. Clasa criptează 16 octeți într-un moment (128 de biți), și trebuie să for-loop peste date pentru a cripta totul. Observați a doua linie: data.length & ~ 15. Se asigură că numărul de octeți criptat poate fi împărțit la 16 și nu expirăm date când sunăm aes.encrypt ().
Notă: Este important să înțelegeți punctul de criptare în acest caz. Nu e chiar criptare, ci mai degraba obfuscatie, deoarece includem cheia din SWF. Scopul este de a transforma datele în gunoi binar, iar codul de mai sus este o slujbă, deși poate lăsa până la 15 octeți necriptați (ceea ce nu contează în cazul nostru). Nu sunt un criptograf și sunt destul de sigur că codul de mai sus ar putea părea labil și slab din perspectiva criptografului, dar așa cum am spus că este destul de irelevant în timp ce includem cheia în interiorul SWF.
Este timpul să creați un alt utilitar care ne va ajuta să criptați fișierele SWF. Este aproape la fel ca și compresorul pe care l-am creat mai devreme, așa că nu voi vorbi prea mult despre asta. Compilați-l într-un nou proiect ca o clasă de documente:
pachet import com.hurlant.crypto.symmetric.AESKey; import flash.display.Sprite; importul flash.events.Event; import flash.net.FileReference; import flash.utils.ByteArray; class public Encryptor extinde Sprite private var cheie: String = "activetuts"; // Am codificat fișierul cheie privat ref: FileReference; funcția publică Encryptor () ref = new FileReference (); ref.addEventListener (eveniment.SELECT, încărcare); ref.browse (); încărcarea funcției private (e: Eveniment): void ref.addEventListener (Event.COMPLETE, encrypt); ref.load (); criptarea funcției private (e: Event): void var data: ByteArray = ref.data; var binKey: ByteArray = ByteArray nou (); binKey.writeUTF (cheie); // AESKey necesită chei binare var aes: AESKey = nou AESKey (binKey); var bytesToEncrypt: int = (date.length & ~ 15); // asigurați-vă că poate fi împărțit la 16, zero la ultimii 4 octeți pentru (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);
Acum rulați-l și faceți o copie criptată a SWF protejat selectând-o mai întâi și apoi salvând-o sub un alt nume.
Reveniți la proiectul SWF de încărcare. Deoarece conținutul este acum criptat, trebuie să modificăm SWF de încărcare și să adăugăm codul de decriptare în el. Nu uitați să schimbați src în eticheta Embed pentru a indica SWF criptată.
pachet 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 (lățime = 640, înălțime = 423)] // dimensiunile ar trebui să fie identice cu clasa publică încărcată a swf-ului. Extensie principală Sprite [Embed (sursa = "VerletClothEn.swf", mimeType = "aplicație / octet-stream") ] // source = calea catre swf pe care doriti sa o protejati privat content var: Class; cheie private var: String = "activetuts"; funcție publică (): void var data: ByteArray = conținut nou (); var binKey: ByteArray = ByteArray nou (); binKey.writeUTF (cheie); // AESKey necesită chei binare var aes: AESKey = nou AESKey (binKey); var bytesToDecrypt: int = (date.length & ~ 15); // asigurați-vă că poate fi împărțit la 16, zero la ultimii 4 octeți pentru (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()));
Acest lucru este la fel ca înainte, cu excepția cazului în care codul de decriptare este blocat în mijloc. Acum compilați SWF de încărcare și testați dacă funcționează. Dacă ați urmat cu atenție până acum, SWF protejat trebuie încărcat și afișat fără erori.
Deschideți noul SWF de încărcare cu un decompilator și aruncați o privire.
Conține peste o mie de linii de cod de criptare cu aspect dur și este probabil mai greu să scoți SWF-ul protejat. Am adăugat câțiva pași pe care trebuie să le întreprindă atacatorul:
Problema este că crearea unui utilitar este la fel de simplă ca copierea-lipirea de la decompilator în editorul de cod și ameliorarea codului un pic. Am incercat sa-mi rup protectia, si era destul de usor - am reusit sa o fac in aproximativ 5 minute. Deci, va trebui să luăm niște măsurători împotriva ei.
Mai întâi am pus SWF-ul protejat în SWF de încărcare, apoi l-am criptat, iar acum vom pune ultimele atingeri SWF de încărcare. Vom redenumi clasele, funcțiile și variabilele la nume ilegale.
Spunand nume ilegale Vreau să spun nume precum:;! @@, ^ # ^ și (^ _ ^). Lucrul cool este că acest lucru contează pentru compilator, dar nu pentru Flash Player. Când compilatorul întâlnește caractere ilegale în identificatori, nu reușește să le parseze și astfel proiectul nu reușește să se compileze. Pe de altă parte, jucătorul nu are probleme cu aceste nume ilegale. Putem compila SWF-ul cu identificatori legali, îl decomprimăm și îi redenumim la o mulțime de simboluri ilegale fără sens. Decompilatorul va scoate codul ilegal, iar atacatorul va trebui să treacă manual sute de linii de cod, eliminând identificatorii ilegali înainte ca acesta să-l compileze. El merita asta!
Acesta este modul în care se vede înainte de orice obfuscation de șir:
Să începem! Decomprimați SWF-ul de încărcare utilizând utilitarul creat anterior și lansați un editor hexagonal.
Să încercăm să redenumim clasa de documente. Presupunând că ați lăsat numele original (Main), să îl căutăm în SWF încărcător necomprimat cu un editor hexagonal:
Redenumiți "Principal" la ;;;;. Căutați acum alte "principale" și le redenumiți ;;;; de asemenea.
Când redenumiți, asigurați-vă că nu redenumiți șiruri inutile sau dacă SWF nu va fi rulat.
Salvați și executați SWF. Functioneaza! Și uite ce spune decompilatorul:
Victorie!! :)
Mențineți redenumirea celorlalte clase. Alegeți un nume de clasă și căutați-l, înlocuindu-l cu simboluri ilegale până când ajungeți la sfârșitul fișierului. Așa cum am spus, cel mai important lucru aici este să vă folosiți simțul comun, să vă asigurați că nu vă încurcați SWF-ul. După redenumirea claselor, puteți începe redenumirea pachetelor. Rețineți că atunci când redenumiți un pachet, puteți să ștergeți perioadele și să faceți un nume lung de pachet ilegal. Uite ce am făcut:
După ce terminați redenumirea clasei și a pachetelor, puteți începe redenumirea funcțiilor și a variabilelor. Ele sunt chiar mai ușor de redenumit, deoarece apar, de obicei, o singură dată, într-un nor mare. Din nou, asigurați-vă că redenumiți numai metodele "dvs." și nu metodele Flash încorporate. Asigurați-vă că nu ștergeți cheia ("activetuts" în cazul nostru).
După ce terminați redenumirea, probabil că doriți să comprimați SWF, astfel încât să fie mai mică. Nicio problemă, putem folosi utilitatea de comprimare pe care am creat-o înainte și va face treaba. Rulați utilitarul, selectați SWF-ul și salvați-l sub alt nume.
Deschideți-o ultima dată și aruncați o privire. Clasele, variabilele și numele de metode sunt obfuscate, iar SWF protejat este undeva înăuntru, criptat. Această tehnică ar putea fi lentă la început, dar după câteva ori este nevoie de doar câteva minute.
Acum ceva timp am creat un utilitar automat pentru a injecta SWF-ul protejat pentru mine în SWF de încărcare și a funcționat bine. Singura problemă este că, dacă poate fi injectată utilizând o utilitate automată, poate fi decriptată folosind un alt utilitar, astfel încât dacă atacatorul face un utilitar pentru că el va primi toate SWF-ul cu ușurință. Din acest motiv prefer să protejez SWF-urile manual de fiecare dată, adăugând o ușoară modificare, astfel încât ar fi mai greu de automatizat.
O altă aplicație frumoasă a tehnicii este Închiderea domeniului. În loc să decriptați SWF-ul cu un șir constant, îl puteți decripta cu domeniul în care rulează SWF. Deci, în loc să aveți o declarație if pentru a verifica domeniul, puteți introduce o modalitate mai puternică de a proteja SWF-ul de plasare pe alte site-uri.
Ultimul lucru, este posibil să doriți să înlocuiți codul de criptare cu propria implementare. De ce? Am investit eforturi în a face codul cripto ilegal, dar codul pe care îl folosim este dintr-o bibliotecă populară open source și atacatorul a putut recunoaște acest lucru ca atare. El va descărca o copie curată și toate lucrările de obfuscatie vor deveni inutile. Pe de altă parte, folosind propria implementare, el va trebui să repare toate numele ilegale înainte de a putea continua.
Deoarece furtul SWF este o mare problemă în lumea Flash, există și alte opțiuni pentru protejarea SWF-urilor. Există numeroase programe acolo pentru a umbla AS pe nivelul bytecode (cum ar fi secureSWF Kindisoft's). Împiedică compilarea bytecode-ului și atunci când decompilatorul încearcă să emită un cod, acesta va eșua și chiar uneori se va prăbuși. Desigur, această protecție este mai bună din punct de vedere al securității, dar costă $ $ $, deci înainte de a alege cum să vă protejați SWF, luați în considerare cantitatea de securitate necesară. Dacă este vorba despre protejarea unui algoritm de proprietate, studioul dvs. Flash cu 50 de angajați se dezvoltă în ultimii doi ani, puteți considera ceva mai bun decât redenumirea variabilelor. Pe de altă parte, dacă doriți să împiedicați copiii să prezinte scoruri false ridicate, puteți lua în considerare utilizarea acestei tehnici.
Ceea ce îmi place despre această tehnică este faptul că SWF-ul dvs. protejat este lăsat neatins atunci când alergați. AS confuzie cu tampere cu codul de octet și ar putea afecta SWF și provoca bug-uri (deși nu am întâlnit nici mine).
Asta e tot pentru astăzi, sper că te-ai bucurat de tutorial și ai învățat ceva nou! Dacă aveți întrebări, nu ezitați să renunțați la un comentariu.