ClassyShark verrät, was sich im APK versteckt

Android-Apps funktionieren so lange problemlos, bis man auf Größenbeschränkungen der Runtime trifft. Das vom Google Developer Advocate Boris Farber entwickelte Analysetool ClassyShark hilft – nicht nur in diesem Fall – bei der Suche nach Problemstellen.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen
ClassyShark verrät, was sich im APK versteckt
Lesezeit: 8 Min.
Von
  • Tam Hanna
Inhaltsverzeichnis

ClassyShark entstand aus der Frustration heraus, an sich einfach zu lösende Probleme im Code nicht finden zu können. Laut Farber geht es hier um die folgenden vier Aufgabengebiete:

  • Obfuscation
  • Dependencies
  • MultiDex
  • Native Code

Alle vier Problemgattungen lassen sich in der IDE nur schwer nachvollziehen. Arbeitet man mit dem Endergebnis, ist die Situation wesentlich einfacher – leider gab es bisher kein Werkzeug, das auf die Analyse von Android-Kompilaten spezialisiert war.

ClassyShark ist eine komplett in Java vorliegende Applikation. Wer unter Ubuntu 14.04 arbeitet, besucht die Download-Webseite und lädt die aktuellste Version herunter (der Artikel basiert auf einem Pre-Release der Version 6.2). Nach dem Herunterladen ist die .jar-Datei als ausführbar zu kennzeichnen:

tamhan@tamhan-elitebook:~/Downloads$ chmod +x ClassyShark.jar 
tamhan@tamhan-elitebook:~/Downloads$ ./ClassyShark.jar

ClassyShark ist ein komplett portables Produkt, das auch unter anderen Betriebssystemen funktioniert. Mit Windows und OS X arbeitende Entwickler führen die .jar-Datei wie jedes beliebige andere Programm aus.

ClassyShark bearbeitet sowohl .dex- als auch .apk-Dateien: Die folgenden Schritte basieren auf einer APK-Datei mit einer kostenlosen Version von SoftMaker Office. Nach Klick auf das Öffnen-Symbol wählt man das File in der Common-Dialog-Box aus, um den Ladeprozess anzustoßen.

Nach dem Laden der Datei präsentiert ClassyShark zwei Ansichten: Abbildung 1 zeigt die für die Analyse des Programminhalts vorgesehene Baumansicht, während Abbildung 2 das interaktive Tortendiagramm veranschaulicht. Doppelklicks auf Tortenteile zeigen ihre Inhalte in vergrößerter Darstellung.

Der Baum verrät mehr über den Aufbau der App ... (Abb. 1)

... während die Torte beim Jagen von Methoden-Slot-Fressern hilft (Abb. 2).

Neben dem Tortendiagramm findet sich eine spezielle Form des Klassenbaumes, die die Inhalte der APK-Datei anhand des Method Count sortiert. Wer nach einem bestimmten Teil des Programms sucht, muss hier auf die kontextsensitive Suche setzen: Durch Anklicken des Baums und nach Eingabe des Namens der gesuchten Klasse lässt sich die Anzeige einschränken.

Mehr Infos

Tortenbäcker

Das in ClassyShark verwendete Tortendiagramm isteine Eigenentwicklung: Wer ein ähnliches Diagramm in seinen Produkten benötigt, sollte den Quellcode studieren.

Das Anklicken einer .dex-Datei liefert einen Überblick über die Anzahl der darin enthaltenen Elemente: Besonders wichtig ist die Anzahl der Methoden, deren Überschreiten die Nutzung von MultiDex voraussetzt. ClassyShark rechnet dabei wie die JVM von Android: Im Rahmen von Kompilierung und Optimierung können sich die von der IDE berechneten Method Counts um mehr als 25 Prozent ändern.

Klassen lassen sich in der Baumansicht markieren: Der Lohn der Mühen ist eine detaillierte Disassemblage, die Methodenkörper, Felder, Konstruktoren und die Vererbungshierarchie in Form einer "reduzierten Codedatei" zeigt.

Java-Code ist dekompilierbar. Entwickler begegnen dieser Situation durch Nutzung von Programmen, die die Klassennamen in ausgeliefertem Code unkenntlich machen (Obfuscation). Das ist so lange erfreulich, bis ein Stacktrace eintrifft, der nach dem Schema a.a.a method c aufgebaute Informationen enthält.

ClassyShark hilft in diesem Fall beim Korrelieren von Fehlerbild und -quelle. Nach Eingabe des gesuchten Klassenpfads in der Baumansicht sollte sich die relevante Klasse finden lassen. Anhand des dekompilierten Codes lässt sich die relevante Methode dann erreichen – das ist mit Sicherheit nicht bequem, aber besser als die Rückführung von Hand zu erledigen. Besonders hilfreich ist dabei ein Ausdruck des Stacktrace, auf dem man Schritt für Schritt die gefundenen Klassen einträgt. Derzeit arbeitet Farber daran, die von ProGuard erzeugten Mapping Files in den Baum einzubinden und so die manuelle Suche unnötig zu machen.

Gradle mag nicht universell beliebt sein, leistungsfähig ist das Buildsystem auf jeden Fall. Ein interessantes Feature ist das Ausschließen von Bibliotheken, die von Untermodulen eingebunden werden. Das hilft sowohl bei der Reduktion der Methodenzahl als auch beim Kleinhalten der Download-Größe (Stichwort: Nur-WLAN-Download).

Diese Abbildung senkt die Download-Wahrscheinlichkeit (Abb. 3).

ClassyShark ist von der Genauigkeit her IDEs überlegen, da es mit den generierten Dateien arbeitet. Jim Baca beschreibt einen Fall, in dem Gradle einige von Drittanbieterbibliotheken mitgebrachte Abhängigkeiten nicht erreichen konnte.

Mehr Infos

DEX-Limit?

Sowohl Dalvik als auch ART (Android Runtime) nutzen ein Repräsentationsformat, das auf 65.563 Methodenreferenzen pro .dex-Datei beschränkt ist. Zur Umgehung des Problems bietet sich ein als MultiDex bezeichnetes Verfahren an, das allerdings nicht unproblematisch ist. Es gibt jedoch ein Werkzeug, das den Umfang öffentlich zugänglicher Bibliotheken analysiert – die Play Services kommen allein auf über 20.000 Einträge.

Etliche Bibliotheken bringen eine Vielzahl von Abhängigkeiten mit, die nur in Sonderfällen notwendig sind. Wer die komplette APK in ClassyShark öffnet, kann die von den Paketen eingebundenen Bibliotheken finden.

Diese Bibliothek setzt auf OAuth (Abb. 4).

Eine weitere klassische Ursache für zu große .apk-Dateien ist das versehentliche Einbinden von Testklassen in die Release-Klasse. ClassyShark hilft hier mit intelligenter Suche weiter – dafür muss man einfach "Test" eingeben und schauen, was das Programm zu Tage fördert. Derzeit gibt es keine
Möglichkeit zur automatischen Suche von Duplikatklassen. Farber bittet Entwickler um das Einsenden von Ideen für einen Suchalgorithmus.

Stub Classes helfen beim Unit Testing: Sie ermöglichen das Ersetzen von Systemteilen, deren korrektes Funktionieren für einen spezifischen Unit-Test nicht notwendig ist oder aufgrund der komplexen Konfiguration sogar kontraproduktiv wäre. Sie zu erzeigen, ist leider alles andere als einfach, zumindest wenn man nicht auf ClassyShark setzt.

Wer eine beliebige Klasse in der Baumansicht markiert und daraufhin auf das Export-Symbol klickt, wird von ClassyShark mit einem Komplettabbild der jeweiligen Klasse belohnt. Funktionen entstehen mit leeren Parameter-Bodies. Als Beispiel dafür zeigt das Listing, was bei der Reflektion von XmlHttpParser entsteht:

@java.lang.Deprecated 
public class XmlHttpParser extends Object implements HttpParser
{
. . .
//======================== M E T H O D S ================

public static XmlHttpParser$Builder builder(XmlNamespaceDictionary) { ... }
public final String getContentType() { ... }
public final XmlNamespaceDictionary getNamespaceDictionary() { ... }
@dalvik.annotation.Signature
public Object parse(HttpResponse,
Class) { ... }
}

Wer ClassyShark erstmals nutzt, wundert sich mitunter darüber, dass das Produkt nach dem Anklicken des Exportknopfs keinen CommonDialog einblendet. Das Verhalten ist normal – die Dateien landen immer im Arbeitsverzeichnis des Programms. Beim weiter oben gezeigten Aufruf aus der Konsole heraus wäre das ~\Downloads.

Angemerkt sei, dass dieses Feature nicht nur zum Erzeugen von Testharnischen geeignet ist. Fehlerhafte Compiler-Einstellungen – das Generieren synthetischer Akzessoren für private Member wäre ein Klassiker – lassen sich am einfachsten aufspüren, wenn man die Solution um eine einfache Klasse mit einem einzelnen privaten Member ergänzt. Nach dem Kompilieren des Resultats zeigt ClassyShark sofort, ob der Compiler wieder mal eigensinnig ist: Wenn ja, lässt sich die DEX-Tabelle mit einigen Klicks entschlacken.

Die in ClassyShark enthaltenen Funktionen laden auf den ersten Blick zur Bejagung privater APIs ein: Steht ein benötigtes Feature nicht über eine Drittanbieter-API zur Verfügung, ist das Disassemblieren des jeweiligen Systemprogramms verlockend.

Die Google-Developer-Experten warnen in Präsentationen regelmäßig davor, diese APIs zur Realisierung von Produkten zu verwenden. Das liegt daran, dass sich ihre Implementierung oft von Gerät zu Gerät ändert – der immense Testaufwand ist für "kleinere" Entwickler nur schwer bewältigbar.

ClassyShark ist kein reines GUI-Tool: Das Produkt lässt sich auch von der Kommandozeile aus steuern. Die Nutzung des optionalen Classname-Parameters öffnet die angegebene Klasse in der GUI:

java jar ClassyShark.jar open <BINARY_FILE> <FULLY_QUALIFIED_CLASS_NAME> 

ClassyShark lässt sich in automatische Analysesysteme einspannen. Die Eingabe von java jar ClassyShark.jar export <BINARY_FILE> weist das Produkt dazu an, die vier in der Tabelle genannten Dateien zu generieren.

Datei Inhalt
all_classes.txt Liste aller Klassen
all_methods.txt Liste aller Methoden samt Signaturen
all_strings.txt Liste aller Stringtabellen
AndroidManifest.xml_dump Kopie der Manifestdatei

Bei ClassyShark handelt es sich um ein Produkt, das sich ausschließlich an fortgeschrittene Android-Entwickler richtet, die komplexe Probleme diskutieren. In dieser Situation ist ClassyShark äußerst hilfreich – das kostenlose und quelloffene Produkt beschleunigt das Finden der Problemquelle immens.

Tam Hanna
befasst sich seit der Zeit des Palm IIIc mit Programmierung und Anwendung von Handheldcomputern. Er entwickelt Programme für diverse Plattformen, betreibt Onlinenews-Dienste zum Thema und steht für Fragen, Trainings und Vorträge gern zur Verfügung.
(ane)