Reverse Engineering von Executables erschweren

26.07.2022

Möchte man seine Applikationen vor bösen Absichten schützen, gibt es die Möglichkeit Source Code zu obfuskieren. Ziel hierbei ist nicht den eigenen Code zu verstecken, sondern vielmehr ein erschwerter Einstieg ins Reverse Engineering und somit erhöhte Sicherheit vor Angriffen.

Das Obfuskieren von Code

Source Code kann auf verschiedene Art und Weisen geschützt werden, diverse Programme helfen dabei das Reverse Engineering deines Codes zu erschweren. In Zusammenhang mit diesem Blogbeitrag habe ich mich für das Obfuskieren mit ProGuard, dem Open Source Java-Obfuscator von Guardsquare, entschieden.

 

ProGuard beinhaltet nicht nur einen Java-Obfuscator sondern auch einen Class-Shrinker. Das heißt, es scannt die Java-App zuerst nach unbenutzten Klassen, Methoden und Attributen und entfernt diese anschließend. Danach verschleiert ProGuard den Code, indem es die übrig geblieben Klassen, Methoden und Attribute umbenennt.

Dekompiliert man die obfuskierte Jar nun, sind die Namen der Klassen etc. völlig bedeutungslos und das Auslesen des Quellcodes wird erschwert. Außerdem verringert die Shrinker-Funktion die Größe der erstellten Jar.[1]

Beispiel mit ProGuard

Java Projekt

Hierfür habe ich ein einfaches Java Projekt erstellt, welches verschiedene Dinge, wie Vererbung, Dead Code/Store und System Calls beinhaltet, um die verschiedenen Features von ProGuard zu testen. Den vollständigen Source Code zu diesem Projekt findest du in diesem Git-Repository.

CI/CD mit Maven

ProGuard lässt sich am einfachsten über die zur Verfügung gestellte GUI nutzen. Da Automatisierung in der Softwareentwicklung aber eine große Rolle spielt – und die Obfuskation somit nicht jedes Mal manuell erfolgen sollte – beschäftigen wir uns heute mit automatischer Obfuskation im Build-Prozess. Dies kann sowohl mit Maven als auch über einen Ant-Task erfolgen.

Der einfachste Weg ProGuard mit Maven auszuführen, ist eine Config Datei mit Hilfe der ProGuard-GUI zu erstellen und diese anschließend im Maven Build aufzurufen. Die Einbindung erfolgt mit Hilfe des ProGuard-Maven Plugins:

<plugin>
    <groupId>com.github.wvengen</groupId>
    <artifactId>proguard-maven-plugin</artifactId>
    <version>2.5.1</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>proguard</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- Jar die obfuskiert werden soll -->
        <injar>${project.build.finalName}.jar</injar>
        <!-- Name der obfuskierten Jar -->
        <outjar>${project.build.finalName}-obfuscated.jar</outjar>
        <!-- Einbinden der ProGuard Config -->
        <proguardInclude>proguard.conf</proguardInclude>
        <!-- Vorgabe für die Umbenennung der Klassen etc. -->
        <options>
            <option>-obfuscationdictionary 'obfuscationDictionary.txt'</option>
            <option>-classobfuscationdictionary 'obfuscationDictionary.txt'</option>
            <option>-packageobfuscationdictionary 'obfuscationDictionary.txt'</option>
        </options>
        <!--  Angabe der genutzten System Packages. Hier z.B. java.util -->
        <libs>
            <lib>${java.home}/jmods/java.base.jmod</lib>
        </libs>
    </configuration>
</plugin>

Für das Renaming der Klassen etc. gibt es hier die Möglichkeit sogenannte Obfuscation Dictionaries, also Vorlagen für die Umbenennung, mitzugeben. Dictionaries sind einfache Text Files die Namen beinhalten, die für das Renaming erlaubt sind, hierbei gilt: ein Name pro Zeile. Möchte man zufällige Namen, die nur aus bestimmten Zeichen bestehen, empfehle ich den String Generator von Advameg.

Ergebnisse der Obfuskation

Nachdem wir unseren Sourcecode obfuskieren und anschließend die daraus entstandene Jar-Datei mit dem Java Decompiler wieder lesbar machen, fällt schnell auf, dass sich einiges getan hat. Unsere Variablen, sowie Klassen und Packages haben völlig andere Namen. Auch dead Code und leere Schleifen wurden entfernt. Außerdem wurde der Inhalt der Methode doMaths() verändert, indem dort kein Ergebnis aus den übergebenen Parametern mehr berechnet wird, sondern bereits das Ergebnis steht.

Für unser Beispiel hat das ganze keinen Einfluss auf die Berechnung. Zudem wurde unser Code durch ProGuard optimiert, die Größe hat abgenommen. Das von ProGuard erzeugte Jar-File ist 5KB groß, wohingegen unsere ursprüngliche Jar 6KB groß ist. Bereits bei einem so minimalen Beispiel sparen wir uns also schon einen Kilobyte an Speicher.

Fazit – Erschweren von Reverse Engineering durch Obfuskation

Source Code kann mit einem Obfuskator verschleiert werden. Dies erhöht die Sicherheit und erschwert die Lesbarkeit, bietet jedoch nie vollständigen Schutz. Mit dem richtigen Zeitaufwand kann jeder Code rekonstruiert werden. Gerade bei sensiblen Daten sollte hier zusätzlich eine Art der Verschlüsselung eingefügt werden. Des Weiteren birgt komplexeres Verschleiern von Code auch einige Nachteile. Je nach Intensität der Obfuskation steigt die Größe der Applikation.[2] Das Hinzufügen eines zusätzlichen Schritts erhöht außerdem auch die Build-Dauer.


Co-Author: Felix Steck

Quellen

[1] Open Source Obfuscators in Java. Java-Source.net. https://java-source.net/open-source/obfuscators Abgerufen am 17. Dezember 2021, 13:17.

[2] F. Plange, Importance of Code Obfuscation. Sweetcode.Io. https://sweetcode.io/importance-of-code-obfuscation/ Abgerufen am 20. Dezember 2021, 08:32.

 

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*Pflichtfelder

*