DexPatcher: Patch Android APKs mit Java

Sie haben wahrscheinlich geänderte Anwendungen gesehen oder installiert, sei es ein gepatchter Dialer für Ihre Auflösung oder eine benutzerdefinierte WhatsApp-Version mit zusätzlichen Funktionen. Wie machen Entwickler das? Häufig ist der Quellcode der Anwendungen nicht verfügbar. Wie funktioniert das? Wir werden das zuerst sehen, dann einen Blick auf ein neues Tool werfen, das den Prozess wesentlich vereinfachen soll, und es schließlich mit dem beliebten Xposed-Framework vergleichen, um zu sehen, wie sie sich unterscheiden.

Vielleicht haben Sie schon gehört, wie APKs in der Regel geändert werden - Entwickler schließen sich der Matrix an, sehen sich alles in Smali an und können Dinge mit der Kraft der Quelle ändern. Ein Anruf genügt, um sie zu erreichen, sobald dies erledigt ist. Dann sind sie bereit, die glänzenden neuen APKs zu teilen.

Ernsthafter… Fangen wir am Anfang an. Wenn Sie nicht mit dem Modden von Android-Anwendungen vertraut sind, fragen Sie sich möglicherweise, was smali ist. Entwickler verwenden normalerweise die Programmiersprache Java, um Android-Apps zu codieren. Ein Programm (der Compiler) „übersetzt“ diesen Code in ein anderes Format, das für Ihr Gerät geeignet ist, was zu .dex-Dateien führt, die im Anwendungspaket (oder APK) enthalten sind.

Zu diesem Zeitpunkt können Sie nicht mehr auf den ursprünglichen Quellcode zugreifen (es sei denn, Sie sind Entwickler oder die Anwendung ist Open Source). Was Sie jedoch haben, ist das APK, da es auf Ihrem Gerät installiert ist. Daraus können Sie die Dex-Dateien (normalerweise classes.dex) abrufen und dann versuchen, sie in ein verständliches Format zurück zu übersetzen. Hier kommt smali ins Spiel, als lesbarere, aber treuere Übersetzung. Sie können einen Schritt weiter gehen und es wieder in Java übersetzen, obwohl dieser Prozess nicht genau genug ist - Sie erhalten ein verständliches Ergebnis, aber es besteht die Möglichkeit, dass Sie es nicht noch einmal in die andere Richtung übersetzen können wird auf dem Weg verloren gehen. Mit anderen Worten, alle Änderungen, die Sie vornehmen, sind umsonst, da Sie es nicht wieder in ein APK verwandeln können, um es auf Ihrem Gerät zu installieren. Zumindest nicht ohne großen Aufwand.

smali / baksmali ist eigentlich ein Assembler / Dissembler für das Dex-Format - das bedeutet es wörtlich auf Isländisch. Normalerweise beziehen wir uns jedoch auf das Format, das smali versteht, wenn wir 'Smali' sagen (stellen Sie sich das als Anweisungen vor, die jedes Detail definieren, auch wenn es nicht alle von uns Menschen benötigt werden - es ist daher ausführlicher als Java). Beachten Sie auch, dass die obige Erklärung ein bisschen vereinfacht ist, aber eine enge Analogie sein sollte, während sie dennoch leicht zu verstehen ist.

Was muss ein Entwickler dann tun, um eine App zu ändern (ohne Zugriff auf die Quelle)? Der Prozess ist mehr oder weniger wie folgt:

  1. Holen Sie sich die APK (aus dem Internet oder vom Gerät).
  2. Verwenden Sie so etwas wie apktool, um die APK nach Smali zu dekompilieren. (apktool verwendet smali / baksmali, erleichtert aber das Dekompilieren und Neuerstellen von APKs erheblich und kümmert sich auch um das Dekodieren von Ressourcen wie XML-Dateien.)
  3. Extrahieren Sie classes.dex aus der APK, verwenden Sie dann dex2jar und schließlich einen Java-Dekompiler, um (unvollständigen, oft defekten, aber meistens verständlichen) Java-Code zu erhalten. (Dies ist optional, kann aber hilfreich sein, da Smali viel schwieriger zu verstehen ist.)
  4. Identifizieren Sie, was geändert werden soll.
  5. Ändern Sie es tatsächlich, indem Sie den Smali-Code direkt bearbeiten.
  6. Alternativ können Sie die Änderung in Java schreiben, kompilieren, erneut in Smali dekompilieren und dann den resultierenden Smali-Code kopieren.
  7. Wenn alles vorbei ist, benutze apktool erneut, um die APK neu zu erstellen.
  8. Signieren Sie das APK (um die Identität des Autors zu überprüfen; alle Pakete müssen signiert sein) und installieren Sie es schließlich.

Das Schreiben von Smali-Code ist ziemlich schwierig und fehleranfällig. Kleinere Änderungen können in Smali vorgenommen werden, das Hinzufügen neuer Funktionen ist jedoch schwieriger. Außerdem treten keine Fehler bei der Kompilierung auf, sodass Tippfehler möglicherweise nur zur Laufzeit erkannt werden. Das Erweitern und Freigeben von Smali-Patches kann ebenfalls problematisch sein, da Unterschiede für eine bestimmte APK-Version in der Regel sehr spezifisch sind. Obwohl es einige Tools gibt, die Teile des oben erläuterten Prozesses vereinfachen (Virtuous Ten Studio fällt mir ein), kann es dennoch ermüdend werden.

DexPatcher von Senior Member Lanchon zielt darauf ab, diese Probleme zu beheben, indem es den Prozess vereinfacht und Entwicklern ermöglicht, den Umgang mit Smali vollständig zu vermeiden. Stattdessen können Entwickler Patches nur in Java schreiben und DexPatcher alles andere erledigen lassen.

Dies hat den Hauptvorteil, leicht lesbare und verwaltbare Patch-Dateien zu haben. Das Patchen von APKs wird im Allgemeinen auch praktischer. Wir werden in Kürze ein vollständiges Beispiel zur Verwendung von DexPatcher sehen, aber hier ist ein kurzer Überblick über das, was es zuerst bietet:

  • Open Source.
  • Plattformübergreifend: Es sollte unter Linux, Mac und Windows laufen.
  • Patch-Dateien: Änderungen, die Sie vornehmen, sind in Java-Patch-Dateien enthalten, die Sie unabhängig voneinander freigeben können.
  • Java: Es ist nicht Smali.

Sie profitieren auch von der Fehlerprüfung während der Erstellung, sodass Fehler früh im Entwicklungszyklus auftreten. Das kompilierte Java bietet die übliche Überprüfung der Kompilierungszeit (mit Zugriff auf die ursprünglichen APK-Symbole), und DexPatcher erzwingt die Kompatibilität von Quelle und Patch beim Patchen, bietet hilfreiche Informationen und gibt Warnungen, wenn Sie etwas rechtmäßiges, aber fauleres tun.

Darüber hinaus enthält DexPatcher eine Reihe von Hilfsskripten (nur unter Linux verfügbar, sie können jedoch auch auf andere Plattformen portiert werden). Diese kümmern sich darum, den Arbeitsbereich einzurichten, die Klassen und Ressourcen des Ziel-APK zu extrahieren, die Klassen in Java zu dekompilieren (der CFR-Java-Dekompiler wird für letzteres verwendet) und schließlich das gepatchte APK zu erstellen und zu signieren, sobald Sie fertig sind.

Schauen wir uns ein Beispiel an (unter Linux):

Installieren Sie die DexPatcher-Skripte

 $ # Erstelle ein Verzeichnis, in dem wir Sachen testen können und betrete es. $ mkdir xda-test $ cd xda-test $ git klonen //github.com/Lanchon/DexPatcher-scripts.git dexpatcher # Klonen Sie das DexPatcher-Hilfsskript-Repo. $ cd dexpatcher $ chmod + x dxp- * # Nicht erforderlich, aber aus Gründen der Übersichtlichkeit: Wir müssen sicherstellen, dass die Dateien, die wir später aufrufen, ausführbar sind. 

Konfigurieren Sie die DexPatcher-Skripte

Öffnen Sie die Datei dxp.config in Ihrem bevorzugten Texteditor und passen Sie die erforderlichen Variablen an Ihr System an. Sie müssen nur die folgende Zeile ändern, um stattdessen auf das Installationsverzeichnis Ihres Android SDK zu verweisen:

 dxp_android_sdk_dir = (~ / android / sdk) 

(DexPatcher wählt automatisch die höchste verfügbare Plattformversion aus. Außerdem können Sie andere Konfigurationsoptionen ändern, um eigene Versionen einiger Tools anstelle der mitgelieferten Standardeinstellungen zu verwenden.)

Um den Zugriff zu vereinfachen, können wir das Verzeichnis dexpatcher unserem PATH hinzufügen oder die verschiedenen dxp- * -Skripte sogar mit einem Speicherort in Ihrem PATH verknüpfen, z. B. ~ / bin:

 export PATH = $ PWD: $ PATH 

Ändern Sie eine Anwendung

In diesem Beispiel verwenden wir eine einfache und Open Source-Anwendung. In diesem Fall wäre es natürlich möglich, den Quellcode direkt zu patchen, aber das macht überhaupt keinen Spaß!

Wir übernehmen die Anwendung "Get ID" von basil2style, eine Anwendung, die Ihnen einige Details zu Ihrem Gerät anzeigt. Unser Ziel ist es, die Schaltfläche "Kopieren" für die "Geräte-ID" zu ändern und stattdessen diese ID freizugeben:

  • Laden Sie zunächst die APK herunter, die geändert werden soll: ID abrufen.
  • Dekompilieren Sie die Anwendung.
  • Erstellen Sie den Signaturschlüssel, den wir später zum Signieren des APK verwenden werden.

Wir können alles auch über die Shell mit den Hilfsskripten erledigen:

 $ cd dexpatcher # Gehen Sie in unser Arbeitsverzeichnis. $ curl -O //f-droid.org/repo/makeinfo.com.getid_1.apk # Laden Sie die APK herunter. $ dxp-setup-for-apk makeinfo.com.getid_1.apk # Entpacke und dekompiliere die APK. $ cd makeinfo.com.getid_1 # Gehe in das neu erstellte Verzeichnis, in das alles entpackt / dekompiliert wird. $ dxp-create-keystore # Erstellt den APK-Signaturschlüssel. Drücken Sie 6 Mal (oder füllen Sie die Informationen aus) und dann "Ja". 

Sie werden dort ein paar verschiedene Verzeichnisse bemerken:

  • decode: Hier finden Sie die von apktool dekodierten Ressourcen und Smali.
  • src : Leeres Verzeichnis. Hier legen wir unsere Patch-Dateien ab.
  • src-cfr : Hier dekompiliert cfr die App (zusammen mit Fehlern). Ein guter Ort, an dem Sie nachsehen können, um zu entscheiden, was geändert werden soll (Sie benötigen möglicherweise auch Ressourcen und deren IDs aus dem obigen Dekodierungsverzeichnis, jedoch nicht für dieses Beispiel).
  • src-cfr-nodecode : Wie oben, enthält jedoch nur leere Stubs (kein Code, nur Skelette). Sie können diese Dateien als Grundlage für Ihren Patch verwenden, wie wir gleich sehen werden.

Wie bereits erwähnt, möchten wir die Schaltfläche "Kopieren" der Geräte-ID ändern, um stattdessen den ID-Text freizugeben. Wenn wir uns im Quellcode umschauen, werden wir feststellen, dass die Schaltfläche zum Kopieren der Geräte-ID (device_copy) im Ereignis onClick von einer anonymen Klasse in src-cfr / makeinfo / com / getid / MainActivity.java behandelt wird. Während wir es hier modifizieren könnten, ist es normalerweise besser, eine alternative Methode zu finden, da anonyme Klassen numerische Namen (MainClassName $ SomeNumber, z. B. MainActivity $ 3) haben, die sich möglicherweise unvorhersehbar zwischen den Versionen ändern.

Stattdessen registrieren wir unsere eigene Klasse für das Ereignis, indem wir die MainActivity-Klasse ändern. Kopieren wir zunächst die "Skeleton" -Version von src-cfr-nocode / makeinfo / com / getid / MainActivity.java nach src / makeinfo / com / getid / MainActivity.java (denken Sie daran, dass src der Speicherort unseres Patches ist). (Sie können auch die Version mit dem vollständigen Code kopieren, wenn Sie dies vorziehen, dies ist nur Geschmackssache.)

Wir können es jetzt wie folgt bearbeiten:

  • Fügen Sie den erforderlichen Import für die DexPatcher-Anmerkung hinzu:
 import lanchon.dexpatcher.annotation. *; 
  • Fügen Sie ein Tag hinzu, um anzuzeigen, dass wir die Klasse bearbeiten. Wir haben auch die Standardaktion für Mitglieder der Patch-Klasse auf IGNORE gesetzt. Dies bedeutet, dass die Mitglieder während der Java-Kompilierung von unserem Code referenziert werden, von DexPatcher jedoch ignoriert werden.
 @DexEdit (defaultAction = DexAction. IGNORE) public class MainActivity // Der Verweis auf ActionBarActivity wird durch Symbole // erfüllt, die beim Erstellen des Patches aus der App extrahiert werden. erweitert ActionBarActivity { 
  • Fügen Sie außerdem leere Körper zum Konstruktor und zur onCreate-Methode sowie zu allen anderen von uns geplanten Methoden hinzu (denken Sie daran, dass sie ignoriert werden, wenn unser Patch tatsächlich angewendet wird. Wir fügen sie nur hinzu, damit wir hier darauf verweisen können wenn wir müssen). Sie können stattdessen auch einfach das native Schlüsselwort hinzufügen.
  • Wir können den Patch bereits an dieser Stelle erstellen, wenn Sie neugierig sind:
     $ dxp-make # Ausgabe: `patched.apk`. 

    Ziemlich einfach, oder? Lass uns trotzdem weitermachen - wir sind immer noch nicht fertig.

  • Bearbeiten wir onCreate jetzt, um einen eigenen OnClickListener zu erstellen, damit wir die Geräte-ID freigeben können, anstatt sie in die Zwischenablage zu kopieren:
     // Benennen Sie die Zielmethode um, sodass wir sie bei // Bedarf immer noch (das Original) aufrufen können. @DexEdit (target = "onCreate") protected void source_onCreate (Bundle var1) {} // Füge unsere neue benutzerdefinierte Methode hinzu. @Override @DexAdd protected void onCreate (Bundle var1) {// Rufe die ursprüngliche Methode auf: source_onCreate (var1); // Ersetze den Text und den Handler: device_copy. setText ("Share"); device_copy. setOnClickListener (neues DeviceCopyOnClick ()); } // Beachten Sie, dass wir keine anonyme Klasse verwenden, um Namenskürzel mit // MainActivity $ 1 zu vermeiden, die bereits vorhanden sind. // Wir hätten auch eine verschachtelte MainActivity.Patch-Klasse definieren und // eine anonyme Klasse in MainActivity.Patch.onCreate () verwenden und dann // MainActivity.Patch.onCreate () von MainActivity.onCreate () aufrufen können. @DexAdd-Klasse DeviceCopyOnClick implementiert View. OnClickListener {@Override public void onClick (Objekt anzeigen) {if (MainActivity. This. Val) {Intent intent = new Intent (Intent. ACTION_SEND); Absicht. setType ("text / plain"); Absicht. putExtra (Intent. EXTRA_SUBJECT, "Geräte-ID"); Absicht. putExtra (Intent. EXTRA_TEXT, device. getText (). toString ()); startActivity (Intent. createChooser (Intent, "Geräte-ID freigeben")); } else {Toast. makeText (MainActivity. this. getApplicationContext (), "Nichts zu teilen", 0). Show (); }}} 
  • Sieht so aus, als wären wir jetzt fertig! Der vollständige Patch sollte so aussehen. Wir können jetzt das gepatchte APK erstellen und installieren:
     $ dxp-make $ adb installiere patched.apk 
  • Schauen wir uns das Ergebnis an:

(Danke an Lanchon für die Hilfe beim Beispielcode!)

Xposed ist immens beliebt und aus gutem Grund - es vereinfacht das Erstellen, Teilen und Installieren von Mods sowohl für Entwickler als auch für Benutzer. Es gibt ein paar Unterschiede zwischen DexPatcher und Xposed, die einige vorziehen können:

  1. Xposed macht seine Magie, indem es Methoden zur Laufzeit einbindet und es Entwicklern ermöglicht, etwas vor, nach oder stattdessen jede Methode auszuführen. DexPatcher hingegen modifiziert alles vor der Laufzeit und erzeugt ein eigenständiges, modifiziertes APK - es ist immer noch möglich, Code vor, nach oder anstelle von Methoden auszuführen, und Sie haben tatsächlich einige zusätzliche Freiheiten.
  2. Wenn Sie eine eigenständige APK erstellen, ist diese nicht von einem externen Framework abhängig. Dies bedeutet auch, dass root nicht zum Ändern von Benutzer-Apps erforderlich ist.
  3. Da Sie mit DexPatcher ein neues APK erstellt haben, wird es anders signiert. Dies bedeutet, dass Benutzer keine offiziellen Updates vom ursprünglichen Autor erhalten können. Wenn die Signaturen überprüft werden, können Probleme mit Apps wie Google Apps auftreten.
  4. Der Quellcode der Module und der DexPatcher-Patches kann problemlos verteilt und geändert werden. Sie haben auch viele Gemeinsamkeiten, wenn Sie sich ein wenig mit jedem vertraut machen.

Wir haben genug über DexPatcher gesprochen. Jetzt sind Sie an der Reihe, es zu versuchen. Besuchen Sie den DexPatcher-Forenthread, um sofort loszulegen!