Balsamiq Mockups - “Crack” erstellen

Hier mal ein bisschen was aus meinen bisherigen RE Erfahrungen. In dem folgenden Post werde ich zeigen, wie man die Flash-Desktop-Anwendung “Balsamiq Mockups” “cracken” kann, dass sie vollständig aktiviert ist und man keine 7-Tage Testversion mehr hat.

Wer das Tutorial über MMO Bots gelesen hat, weiß, dass man Flash sehr gut decompilen kann. Ich verwende dazu im folgenden den Sothink SWF Decompiler, aber das ist Geschmackssache. Also schauen wir in die SWF rein und finden relativ weit oben die Activate.cs. Klingt doch schonmal gut ;-)

package 
{
    import Activate.*;
    import com.balsamiq.mockups.desktop.licensing.*;
    import com.plus9.mockups.*;
    // [...]
 
    public class Activate extends BaseDialog implements IBindingClient
    {
        var _bindingsByDestination:Object;
        protected var _licenseManager:SevenDayTrialLicenseManager;
        // [...]
    }
    protected function updateActivateEnabledState() : void
    {
       var _loc_1:* = this._licenseManager.cleanKey(this._key.text);
       var _loc_2:* = this._licenseManager.isValidKey(this._name.text, _loc_1);
     // [..]

Ok, sehr viel anderes interessantes finden wir nicht in dieser Klasse. Aber was wir hier schon haben ist der Aufruf der Lizenzcheck Funktion. Und genau hier wollen wir weiter ansetzen. Also schnell in den namespace weiter oben geschaut, dass wir wissen wo der LizenzManager zu finden ist, und den weiter analysieren!

Der folgende Source ist etwas lang, aber hier sind viele Informationen drin. Daher hab ich das ganze ein bisschen aufgeteilt. Fangen wir mit den Klassenmembern an:

package com.balsamiq.mockups.desktop.licensing
{
    import com.balsamiq.keygen.*;
    import com.balsamiq.keygen.descriptors.*;
 
    public class SevenDayTrialLicenseManager extends EventDispatcher
    {
        protected var _sqlConnection:SQLConnection;
        protected var _daysLeft:Number = 0;
        protected var _demoTimer:Timer;
        protected var _blacklist:Object;
        protected var _phase:String;
        protected var _cookieManager:ICookieManager;
        protected var _dbFile:File;
        protected var _licenseKey:String = null;
        public static const PHASE_EXPIRED:String = "expired";
        public static const PHASE_DEMO:String = "demo";
        public static const START_DATE_KEY:String = "sdt-stdt";
        public static const PHASE_REGISTERED:String = "registered";

Ohne tief ins Detail zu gehen, sollten uns schon ein paar Sachen aufgefallen sein:

  1. Es gibt einen Keygen Namespace, der wohl zum erzeugen von Keys ist
  2. Es wird eine SQL-Connection verwendet und ein DB-File
  3. Die verbleibenden Tage werden auch hier abgespeichert
  4. Unser Ziel ist es, “PHASE_REGISTERED” zu erreichen…

 

        public function SevenDayTrialLicenseManager(param1:File = null, param2:ICookieManager = null)
        {
            if (param1 != null)
            {
                this._dbFile = param1;
            }
            else
            {
                this._dbFile = File.applicationStorageDirectory.resolvePath("MockupsOnAir.db");
            }
            if (param2 != null)
            {
                this._cookieManager = param2;
            }
            else
            {
                this._cookieManager = new LocalDBManager();
            }
            this._sqlConnection = new SQLConnection();
            if (!this._dbFile.exists)
            {
                this.startTrial();
                return;
            }
            this.initBlacklist();
            this.updateFromDB();
            if (this._phase == PHASE_DEMO)
            {
                this.startDemoTimer();
            }
            return;
        }// end function

In dieser vorangegangen Funktion sehen wir den Konstruktor. Wenn ein File-Objekt als erster Paramter übergeben wird, so wird dieses als DB-File genommen. Wenn nicht, wird aus dem %APPDATA%-Pfad “MockupsOnAir.db” rausgesucht. Das ist wichtig, also merken :P

Der CookieManager scheint wohl auch etwas mit der DB zu tun haben, aber um einen kleinen Vorgriff zu wagen: Hier wird nur die Startzeit der Trail gespeichert, nicht die Lizenz!

Wenn es kein DB-File gibt, so wird ein neues erstellt indem eine Trail-Periode angefangen wird, dann weiter wird die Blacklist initialisiert. Nun kommt die magische Funktion “updateFromDB()”, welche wohl den Registrierungsstatus abruft und die Phase setzt. Wenn diese die DEMO-Phase ist, so wird der DemoTimer initialisert.

Was können wir jetzt hierzu sagen? Es sollte inzwischen klar geworden sein, dass die Linzenz wohl über eine lokale Datenbank abläuft. Und genau dieses ist mein Ansatz den ich weiter verfolgen werde. Natürlich könnte man auch den Keygen verwenden, die Trail-Zeit auf unendlich setzen usw., aber ich denke dieses ist die komfortabelste Methode.

Aber bevor wir uns “updateFromDB()” anschaun, fällt eine andere Funktion ins Auge: Activate()!

protected function activate(param1:String) : void
        {
            PopUpManager.removePopUp(this._activateDialog);
            if (this._dbFile.exists)
            {
                this._dbFile.deleteFile();
            }
            this._sqlConnection.open(this._dbFile, SQLMode.CREATE);
            var _loc_2:* = new SQLStatement();
            _loc_2.sqlConnection = this._sqlConnection;
            _loc_2.text = "CREATE TABLE activation (id INTEGER PRIMARY KEY, status TEXT);";
            _loc_2.execute();

Wer noch nicht geglaubt hat, dass die Lizenz über eine DB verwaltet wird hat hier den Beweiß :P Intressant ist für uns aber nur der Teil, der zeigt wie die DB aussieht, bzw. auszusehen hat. Es wird eine Table “activation” erzeugt, welche mit einer ID und einem Status gefüllt wird. Ebenfalls im Hinterkopf behalten ;-)

protected function updateFromDB() : void
        {
            var keyInDB:Object;
            var sqlStatement:SQLStatement;
            var res:Array;
            var d:SerialKeyFieldsDescriptor;
            try
            {
                this._sqlConnection.open(this._dbFile, SQLMode.READ);
                sqlStatement = new SQLStatement();
                sqlStatement.sqlConnection = this._sqlConnection;
                sqlStatement.text = "SELECT status FROM activation WHERE id = 0";
                sqlStatement.execute();
                res = sqlStatement.getResult().data;
                if (res.length == 1)
                {
                    keyInDB = res[0].status;
                }
                else
                {
                    keyInDB;
                }
            }
            catch (e:Error)
            {
                keyInDB;
            }
            this._sqlConnection.close();
            this._phase = PHASE_EXPIRED;
            if (keyInDB == null)
            {
                this.startTrial();
            }
            else if (keyInDB is Boolean)
            {
                if (keyInDB == false)
                {
                    this.startTrial();
                }
                else
                {
                    this._phase = PHASE_REGISTERED;
                }
            }
            else if (keyInDB is String)
            {
                if (keyInDB == "null" || keyInDB == "false")
                {
                    this.startTrial();
                }
                else if (keyInDB == "true")
                {
                    this._phase = PHASE_REGISTERED;
                }
                else if (this.isValidKey("", String(keyInDB), false))
                {
                    d = BalsamiqKeyGenerator.decriptKey(String(keyInDB));
                    if (d.commercial)
                    {
                        this._phase = PHASE_REGISTERED;
                    }
                    else
                    {
                        this._cookieManager.setProperty(START_DATE_KEY, d.startDate.getTime());
                        this.startTrial();
                    }
                }
                else
                {
                    this._phase = PHASE_EXPIRED;
                }
            }
            else
            {
                this._phase = PHASE_EXPIRED;
            }
            return;
        }// end function

Ausgerechnet die längste Funktion muss komplett kopiert werden -.- Aber es ist auch die interessanteste, also von daher…

Es wird das DB-File geöffnet und eine Query durchgeführt:

SELECT status FROM activation WHERE id = 0

Für alle Nicht-SQL-Kundigen heißt das: Rufe den Status aus der Tablle activation ab wo die ID = 0 ist.

Dieses Resultat wird in einem Array gespeichert und der Status aus dem Array ausgelesen. Nun wird die Phase auf Standart = Abgelaufen gesetzt und geprüft ob überhaupt etwas aus der Query zurückgegeben wurde. Wenn nicht, wird eine Trail gestartet. Wenn doch, so wird überprüft ob der Wert ein bool ist, also true oder false! Wenn er ungleich false ist, haben wir ein aktiviertes Programm!

Wenn er ein String ist, so wird das ganze durch den Keygenerator gejagt und geprüft ob es ein vaild Key ist. Da wir aber faul sind machen wir die erste Methode.

Ich habe die DB über das Prorgamm SQLite Database Browser erstellt und einfach folgende Werte eingetragen. Diese DB hab ich dann nach %APPDATA%/BalsamiqMockupsForDesktop/LocalStore/MockupsOnAir.db kopiert.

Und siehe da, wir haben ein voll aktiviertes Programm!

Und wer zu faul ist das ganze nachzuvollziehen und nur das Programm aktiviert haben will… Hier ist die Datei:

MockupsOnAir

Greez Easysurfer

11 people like this post.
    • wer auch immer
    • 27. Sep. 2011 7:34pm

    läuft bestens. Vielen Dank!

    • Jerry
    • 21. Okt. 2011 9:28am

    Yay, Danke! Läuft super!

    Frage: Besteht die Möglichkeit, dass die Software Informationen an Balsamiq sendet, e.g. Adobe?

      • Easysurfer
      • 22. Okt. 2011 10:40am

      Ich denke mal nicht, aber wenn Du sicher sein willst, so sollte wie bei allen Adobe Produkten die .hosts Datei geändert werden. Dadurch werden alle Anfragen an Adobe ins Leere laufen. Einfach die Einträge aus diesem und diesem Links verwenden, dann sollte das passen ;-)

    • fluppt
    • 2. Apr. 2012 2:20pm

    Danke für die Mühe!

    • Michael
    • 19. Feb. 2013 4:28pm

    Super Artikel, einfach klasse! :D

  1. Noch keine TrackBacks.


− drei = 6