Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 25 Dienstprogramme für die Java-Umgebung
gp 25.1 Die Werkzeuge im Überblick
gp 25.2 Der Compiler javac
gp 25.2.1 Der Java-Interpreter java
gp 25.2.2 Der Unterschied zwischen java.exe und javaw.exe
gp 25.3 Das Archivformat Jar
gp 25.3.1 Das Dienstprogramm Jar benutzen
gp 25.3.2 Das Manifest
gp 25.3.3 Jar-Archive für Applets und Applikationen
gp 25.4 Mit JavaDoc und Doclets dokumentieren
gp 25.4.1 Mit JavaDoc Dokumentationen erstellen
gp 25.4.2 Wie JavaDoc benutzt wird
gp 25.4.3 Eine Dokumentation erstellen
gp 25.4.4 JavaDoc und Doclets
gp 25.5 Konvertierung von Java-Bytecode in ein Windows-Exe mit JET
gp 25.6 Manteln von Java-Klassen in ein Windows-Exe mit JexePack
gp 25.7 Decompiler
gp 25.7.1 Jad, ein schneller Decompiler
gp 25.7.2 SourceAgain
gp 25.7.3 Decompilieren erschweren
gp 25.8 Obfuscate Programm RetroGuard
gp 25.9 Sourcecode Beautifier
gp 25.10 Ant
gp 25.10.1 Bezug und Installation von Ant
gp 25.10.2 Properties
gp 25.10.3 Externe und vordefinierte Properties
gp 25.10.4 Weitere Leistungen


Galileo Computing

25.7 Decompilerdowntop

Ein Decompiler wandelt Java-Klassendateien in Java-Quellcodedateien zurück. Er verdreht also die Arbeitsweise eines Compilers, der aus der Quellcodedatei eine ausführbare Datei beziehungsweise Klassendatei erzeugt. Obwohl es für die verschiedensten Sprachen Decompiler gibt (unter anderen C und Smalltalk), ist für Java die Zurückübersetzung relativ einfach. Der Java-Compiler erstellt für einen virtuellen Prozessor Bytecode, und dieser Bytecode enthält viele wertvolle Informationen, die in einem herkömmlichen Maschinencode nicht auftauchen. Darunter sind etwa Typinformationen oder Hinweise, ob ein Methodenaufruf virtuell ist oder nicht. Sie sind für den Interpreter ebenfalls wichtig, um Programme als sicher oder unsicher zu erkennen. Dies macht es einfach, verlorenen Quellcode wiederzubeleben oder an fehlende Informationen aus Paketen von Fremdherstellern zu gelangen.

Ein weites Anwendungsgebiet sind Optimierungen am Quellcode. Über die Rückübersetzung lässt sich die Arbeitsweise eines Compilers gut verstehen, und wir können an einigen Stellen optimieren, wenn wir beispielsweise sehen, dass eine Multiplikation mit der Zahl 16 doch nicht zu einem Shift optimiert wurde. Das Übersetzen ist dabei verhältnismäßig einfach. Ein Decompiler benötigt lediglich die Klassendatei, wobei einige Decompiler auch direkt einen Verbund von Klassen aus einem Jar-Archiv unterstützen. Aus dem Java Bytecode für eine Methode baut er dann einen Kontrollfluss-Graphen auf und versucht, Ausdrücke zu erkennen, die bei der Übersetzung bestimmter Sprachkonstrukte entstanden sein müssten. Da Variablennamen durch einen Obfuscator eventuell ungültig gemacht worden sind, muss ein guter Decompiler diese illegalen Bezeichnernamen korrigieren. Somit sollte der Quelltext auch noch gut lesbar sein, obwohl er verunstaltet wurde. Diese Umbenennung ändert den Algorithmus nicht, und ein Decompiler hat es bei dieser Art von Verschleierung einfach. Es sind allerdings andere Techniken in der Entwicklung, die den Bytecode erst zur Laufzeit entschlüsseln, und dieser ist dann in der Klassendatei nicht mehr lesbar.

Da mittlerweile auch andere Compiler auf dem Markt sind, die Java-Bytecode erzeugen - etwa aus EIFFEL-Programmen oder aus diversen LISP-Dialekten - ist über den Umweg Compiler/Klassendatei/Decompiler ein Crosscompiling denkbar. Hier sind aber einige Einschränkungen bezüglich der sich auf dem Markt befindenden Decompiler erkennbar. Denn fremde Compiler, die Java-Bytecode erstellen, haben andere Techniken, die dann vom Decompiler nicht immer passend übersetzt werden können.

Wir werden uns im Folgenden näher mit Jad beschäftigen. Ein weiterer Open-Source-Decompilator und -Obfuscator, JODE (Java Optimize and Decompile Environment), findet sich unter http://jode.sourceforge.net/. Interessante Artikel und eine Liste gängiger Decompiler finden sich etwa unter

gp http://www.softpanorama.org/Algorithms/decompilation.shtml
gp http://www.csee.uq.edu.au/~csmweb/decompilation/halstead.htm

Ist das Decompilieren legal?

Wenn der Decompiler auf den eigenen Programmcode losgelassen wird, weil etwa der Quellcode verschwunden ist, dann ist die Anwendung kein Problem. Das Reverse Engineering von vollständigen Anwendungen, die unter Urheberschutz stehen, ist dabei auch nicht unbedingt das Problem. Vielmehr beginnt die Straftat, wenn dieser Quelltext verändert und als Eigenleistung verkauft wird.


Galileo Computing

25.7.1 Jad, ein schneller Decompilerdowntop

Jad (von Java Decompiler) ist ein frei verfügbarer Decompiler von Pavel Kouznetsov und oft1 unter der nachfolgenden Adresse zu beziehen: http://kpdus.tripod.com/jad.html. Da Jad zu 100 % in C++ geschrieben ist2, decompiliert das Tool die Dateien sehr schnell, viel schneller als seine in Java programmierten Konkurrenten. Jad macht es einfach, schon vorhandene Klassendateien zu »reengineeren«3. Die Dateien der Laufzeitbibliothek lassen sich somit auch im Quellcode beäugeln. (Da allerdings beim SDK viele Klassen im Quellcode vorliegen, lohnt sich das nur bei Klassen, die nicht öffentlich sind.)

Da Jad nicht vom Java-Laufzeitsystem abhängig ist, ist das Setzen des CLASSPATH nicht notwendig und ein Setup wird nicht benötigt. Da Jad auf jeden Fall den Bytecode auseinandernehmen muss, lässt sich zum Disassemblieren der Java-Bytecode auch in den Quellcodezeilen einblenden; eine prima Möglichkeit, Bytecode zu lernen und zu sehen, wie der Compiler funktioniert. Einen weiteren Pluspunkt bekommt Jad im Umgang mit Klassendateien, die durch einen Obfuscator4 verunstaltet wurden. Daraus wird wieder gültiger Java-Code erzeugt. Manchmal muss jedoch auch Jad aufgeben, und dann verschwinden beispielsweise Zuweisungen.

Vom Download zur Installation

Jad liegt zur Zeit in der Version 1.5.8 vor. Obwohl Jad im Quelltext nicht existiert, ist die Unterstützung von verschiedenen Betriebssystemen gewährleistet, unter anderem von Windows 9x/NT/2000 und Linux .

Das Programm ist schnell geladen und auch genauso rasch entpackt. Danach offenbaren sich zwei Dateien, eine ausführbare mit dem Namen jad.exe (Windows 95/NT) oder nur jad (Unix). Daneben gibt es noch eine Liesmich-Datei readme.txt, welche eine kurze Anleitung für Jad enthält. Eine grafische Oberfläche wie FrontEnd Plus (http://kpdus.tripod.com/frontend/FrontEnd.zip) vereinfacht die Anwendung noch weiter, denn sie kommt zusammen mit Jad in einem leicht zu nutzenden Windows-Programm daher. Unter http://sourceforge.net/projects/jadclipse ist auch ein Plugin für Eclipse erhältlich. Doch bleiben wir bei der Kommandozeile.

Jad benutzen

Um eine Klassendatei zu decompilieren, wird Jad einfach auf diese Datei losgelassen:

$ jad example1.class

Ohne zusätzliche Option wird dann eine Datei example1.jad im Arbeitsverzeichnis angelegt. Jad verwendet nicht den Namen der Datei als Ausgabe, sondern den Namen der Klasse. Enthält also example1.class keine Klasse mit dem Namen example1, sondern mit expl1, so heißt die Datei auch expl1.jad und nicht example1.jad. Existiert schon eine Datei mit diesem Namen, dann fragt Jad vorher nach, ob sie überschrieben werden kann. Die Option -o überschreibt ohne zu fragen. Jad lässt auch Wildcards zum Aussortieren der Dateien zu. Ist der Standardname nicht sinnvoll, so lässt sich über den Schalter -s eine andere Extension erzeugen, statt der Endung jad im folgenden Beipiel .java:

$ jad -sjava example1.class

Beim gleichzeitigen Verwenden der beiden Schalter -o und -sjava muss darauf geachtet werden, dass jad beim Experimentieren die originale Quelldatei nicht überschreibt.

Sollen alle Java-Klassen eines ganzen Dateibaums decompiliert werden, eignet sich dafür der Schalter -r ganz gut. Denn dieser erstellt auch Unterverzeichnisse im angegebenen Stammverzeichnis (Option -d):

$ jad -o -r -sjava -dsrc tree/**/*.class        (WIN)
$ jad -o -r -sjava -dsrc 'tree/**/*.class'      (Unix)

Gibt es also in der Datei tree/a/b/c.class die Klasse »c« vom Paket a.b, dann geht die Ausgabe in src/a/b/c.java.

Optionen der Kommandozeile

Die Optionen sind in der folgenden Tabelle zusammengestellt. Alle Optionen mit Ein/Ausschaltcharakteristik lassen sich in drei Kategorien einteilen:

gp -o.
Negiert den Wert der Option.
gp -o+
Setzt den Wert auf »wahr« oder »an«.
gp -o-
Setzt den Wert auf »falsch« oder »aus«.

Schalter Wirkung
-a JVM-Bytecode wird als Kommentar mit in den Quelltext eingesetzt.
-af Wie -a, nur mit voll qualifizierten Namen
-b Redundante Klammern um Anweisungen, standardmäßig aus.
Beispiel: if(a) { b(); }
-disass Nur den Bytecode disassemblieren ohne Quellcodeerzeugung
-d <dir> Verzeichnis der Ausgabedateien (wird - wenn erforderlich - auch angelegt)
-f Voll qualifizierte Namen für Klassen, Variablen und Methoden überall im Quelltext verwenden
-i Nicht finale Variablen werden alle initialisiert.
-l<num> String in zwei Hälften teilen, wenn er länger als <num> ist, nicht voreingestellt
-nl Strings mit Newline-Zeichen aufspalten, nicht voreingestellt
-o Ausgabedatei überschreiben
-p Code in die Standardausgabe lenken, sinnvoll fürs Pipen
-r Verzeichnisstruktur vom Paket erhalten
-s <ext> Endung der Ausgabedateien bestimmen
-t Tabulatoren statt Leerzeichen zum Einrücken verwenden
-t<num> Breite der Einrückungen, voreingestellt sind vier Leerzeichen
-v Zeigt die Methodennamen, die decompiliert werden, zur Kontrolle an.
-8 Unicode-String in 8-Bit-Strings nach der ANSI-Code-Tabelle (nur Win32) umwandeln

Tabelle 25.4 Schalter von Jad (Forts.)


Galileo Computing

25.7.2 SourceAgaindowntop

SourceAgain und SourceAgain Professional (http://www.ahpah.com/cgi-bin/suid/~pah/demo.cgi) sind zwei kommerzielle Dienstprogramme zum Decompilieren. Da die Qualität beziehungsweise die Leistung von SourceAgain mit Jad vergleichbar und Jad ein freies Programm ist, soll SourceAgain hier nicht vertieft betrachtet werden. SourceAgain existiert allerdings in einer Web-Version, und der Benutzer kann eine URL mit einem Java Applet angeben, das dann eingelesen und übersetzt wird. Falls mehrere Applets auf einer Seite existieren, bietet das Programm einen Auswahldialog. Ähnlich wie bei Jad lassen sich verschiedene Parameter setzen. Für kurze Überprüfungen ist dies eine gute Wahl, da wir die Applets nicht erst auf die Festplatte kopieren und dann in einem zweiten Schritt umwandeln müssen.


Galileo Computing

25.7.3 Decompilieren erschwerentoptop

Obwohl wir Decompilierung verhindern können, so können wir immer noch nicht das bloße Disassemblieren des Bytecodes stören. Es gibt allerdings einige einfache Tipps, die dem motivierten Angreifer schnell die Lust am Bytecode verderben. Wir beschreiben einige Regeln, die das Entschlüsseln von Bytecode oder das Umgehen einer Seriennummereingabe erschweren. Dazu gehört:

gp Verwendung von Methodennamen und Klassennamen aus Nullen, dem Buchstaben O, Einsen und dem Buchstaben l. Einige freie Obfuscatoren lassen sich leicht anpassen, so dass sie solche Bezeichner erstellen.
gp Es sollten keine lesbaren Zeichenketten im Quellcode vorkommen. Füge jeder Klasse eine individuelle Entschlüsselungsfunktion zu.
gp Konstanten sollten über mathematische Funktionen errechnet werden. Erwartet das Programm zum Beispiel die Taste KeyEvent.VK_F1, wird der Compiler automatisch für die Konstante den Wert 0x70 einsetzen. Wir können natürlich diesen Wert stattdessen auch dynamisch berechnen.
gp Verwendung vieler bedingter Sprünge, die aber unnütz sind
gp Wenn die Möglichkeit besteht, den Bytecode direkt zu manipulieren, können legale, aber unsinnige Bytecode-Sequenzen eingefügt werden. Dynamischer Java-Bytecode hat sich (noch) nicht durchgesetzt, wäre aber möglich. Denkbar ist, an bestimmten Stellen einer Klassendatei Zeichen zu schreiben, dann diese Datei über den Klassenlader zu laden und anschließend wieder die Information zurückzuschreiben. An der bestimmten Position könnte etwa eine Variable stehen.
gp Teile des Programmcodes sollten auf Tabellen zurückgreifen, die dynamisch berechnet werden.
gp Ein Startbildschirm mit Grafik und Text (Splash-Screen beziehungsweise Nag-Screen) kann leicht zurückverfolgt werden. Daher sollte besser darauf verzichtet werden.
gp Bei einer Seriennummer und einem Ablaufdatum lässt sich die Systemzeit natürlich zurücksetzen. Wir können kontinuierlich in eine Datei verschlüsselt ein Datum schreiben und dies immer erhöhen. Ist das Datum des Rechners kleiner als das Datum in der Datei, hat der Benutzer versucht zu schummeln. Ein alternativer Weg ist plattformabhängig: Einige Systemdateien weisen das Datum vor dem Zurückstellen auf, wie etwa SYSTEM.DAT.
gp Werden Seriennummern eingegeben, so sucht der Angreifer nach Textfeldern. Besser ist in diesem Fall eine Lowlevel-Event-Verarbeitung. Wir legen einen Listener zum Beispiel auf etwas ganz anderes, beispielsweise ein Label. Die Ereignisse werden abgefangen und bearbeitet. Aus dem AWT-Event holen wir dann die Zeichen heraus. Da der Angreifer aber auch nach Eventhandlern sucht, ließe sich auch die System-Queue abhorchen.
gp Checksummen von Klassen bilden, die eventuell angezapft werden könnten. In einer anderen Klasse lässt sich dann diese Checksumme testen. Zum Entschlüsseln von Daten können auch weitere Klassendateien verwendet werden.

Und mein Lieblingstipp: Zeitmessungen vornehmen um zu erkennen, ob jemand versucht, das Programm zu debuggen. Zudem lassen sich Cracker leicht befriedigen, indem ihnen eine offensichtliche Prüfung einer eingegebenen Seriennummer angeboten wird. An weiteren Stellen können aber ein Test auf die Seriennummer und subtile Fehlfunktionen ins Programm eingewoben sein. Das Programm hat kleine Fehler oder Unzulänglichkeiten, falls die Seriennummer diese Tests nicht auch noch erfüllt.






1 Das Kalkül der Anbieter von freiem Webspace besteht darin, immer nur Seiten anzubieten, die niemand kennt. Immer dann, wenn eine Seite bekannt ist und oft aufgerufen wird, muss der Freespacer tiefer in die Tasche greifen. Dann macht er die Seite einfach mit einer netten Meldung »The Tripod page you are trying to reach has exceeded its hourly bandwidth limit. The site will be available again in 2 hours!« dicht.

2 Wollte der Autor verhindern, dass auch sein Tool entschlüsselt wird?

3 Abkürzung für reverse engineering - das Zurückverwandeln von unstrukturiertem Binärcode beziehungsweise Quellcode

4 Ein Obfuscator benennt Methoden- und Klassennamen in unsinnige Bezeichner um. Er verschlüsselt Zeichenketten und versucht im Code solche Bytecode-Kombinationen einzubauen, die von einem normalen Decompiler nicht wieder umgewandelt werden können.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de