Wie ProGuard funktioniert


Wenn Sie jemals über die Sicherheit Ihrer Anwendung nachgedacht haben oder Ihren Code irgendwie optimieren wollten, wissen Sie sicher, was ProGuard ist. Vielleicht haben Sie bereits darunter gelitten oder konnten die Dokumentation überwinden, ein paar Artikel in der Weite und herausgefunden, was passiert ist.

In diesem Artikel werde ich nicht darüber sprechen, wie man Regeln beibehält oder über einige nützliche Optionen. Meiner Meinung nach reicht es aus, sich die dazugehörigen Tutorials anzusehen, um ProGuard in ein Projekt zu ziehen. Ich werde analysieren, wie ProGuard in Bezug auf Code funktioniert. Und wenn Sie interessiert sind - willkommen bei cat.

Viele irren sich über ProGuard und glauben fälschlicherweise, dass es sich um einen Verschleierer handelt, was aus mehreren Gründen nicht der Fall ist:

  • Code wird nicht gemischt
  • Der Code wird nicht verschlüsselt.

Die Hauptaufgabe von ProGuard besteht darin, die Namen von Objekten, Klassen und Methoden zu ändern, wodurch die Analyse des Codes für den Reverse Engineer erschwert wird. Darüber hinaus optimiert er den Code und entfernt Ressourcen, die nicht im Programm verwendet werden. Aber letztendlich kann es im klassischen Sinne nicht als Verschleierer bezeichnet werden.

Also, womit haben wir es zu tun?


Im Allgemeinen ist ProGuard ein Open-Source-Dienstprogramm, das mit Java-Code arbeitet. Ja, und es ist auch in Java geschrieben. Die Leute, die sich damit beschäftigen, entwickeln auch DexGuard, das auch in offenen Räumen zu finden ist, wenn Sie ein Geräusch machen, da sie regelmäßig verschmelzen. Im Allgemeinen gilt DexGuard jedoch als bezahlt, da es sich tatsächlich um eine härtere Version desselben ProGuard handelt.

Wir können daraus schließen, dass ProGuard eine JAR-Datei ist, die die Zeichen in unserem Code neu anordnet, optimiert und anscheinend die Sicherheit erhöht. Standardmäßig arbeitet ProGuard mit 26 englischen Groß- und Kleinbuchstaben.

Die Regeln für Proguard werden von Projekt zu Projekt gezogen und gelten als unverletzlich, da sie nicht angefasst werden - es funktioniert, oder es können höllisch rote Linien entstehen, die niemand zu beheben weiß und nicht wissen möchte. Und ein Orakel hat diese Regeln aufgestellt, um zu gelangen, zu denen man sich sieben Mal umdrehen, sich in einen Vogel verwandeln und zwei Stunden und dreiundvierzig Minuten nach Südwesten fliegen muss.

Nun, da niemand dorthin gehen will und niemand dorthin gehen muss, lassen Sie uns in eine Art gehen .

Wenn Sie sich die Verzeichnisse des Proguard-Projekts ansehen, können Sie dessen Hauptfunktionen sofort identifizieren.


Bisher scheint alles klar zu sein. Schauen wir uns also die Hauptklasse an.

public class ProGuard { //… private final MultiValueMap<String, String> injectedClassNameMap = new MultiValueMap<String, String>(); //... /** * The main method for ProGuard. */ public static void main(String[] args) { if (args.length == 0) { System.out.println(VERSION); System.out.println("Usage: java proguard.ProGuard [options ...]"); System.exit(1); } // Create the default options. Configuration configuration = new Configuration(); try { // Parse the options specified in the command line arguments. ConfigurationParser parser = new ConfigurationParser(args, System.getProperties()); //... 

Lassen Sie uns hier auf die offensichtliche Hauptmethode stoßen, in der wir sehen, wie die Standardregeln festgelegt und die vom Entwickler selbst festgelegten Regeln analysiert werden.

Darüber hinaus gibt es das erwartete Objekt injiziertClassNameMap, mit dem wir die Datei build / output / proguard / release / Mapping.txt erhalten, die ungefähr so ​​aussieht:


Wenn wir also plötzlich unseren eigenen Code öffnen und ihn lesbar machen möchten, können wir dies mit Mapping.txt tun. Dazu müssen Sie beim Veröffentlichen der apk-Datei die Version von maps.txt in die Google Play Console herunterladen.

Jetzt können Sie sich den Parser der vom Entwickler festgelegten Konfigurationen ansehen.

 public class ConfigurationParser { //... /** * Parses and returns the configuration. * @param configuration the configuration that is updated as a side-effect. * @throws ParseException if the any of the configuration settings contains * a syntax error. * @throws IOException if an IO error occurs while reading a configuration. */ public void parse(Configuration configuration) throws ParseException, IOException { while (nextWord != null) { lastComments = reader.lastComments(); // First include directives. if (ConfigurationConstants.AT_DIRECTIVE .startsWith(nextWord) || ConfigurationConstants.INCLUDE_DIRECTIVE .startsWith(nextWord)) configuration.lastModified = parseIncludeArgument(configuration.lastModified); else if (ConfigurationConstants.BASE_DIRECTORY_DIRECTIVE .startsWith(nextWord)) parseBaseDirectoryArgument(); // Then configuration options with or without arguments. else if (ConfigurationConstants.INJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, false); else if (ConfigurationConstants.OUTJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, true); //… else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, true, null); else if (ConfigurationConstants.PRINT_SEEDS_OPTION .startsWith(nextWord)) configuration.printSeeds = parseOptionalFile(); // After '-keep'. else if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION .startsWith(nextWord)) configuration.keepDirectories = parseCommaSeparatedList("directory name", true, true, false, true, false, true, true, false, false, configuration.keepDirectories); //... 

Äh, ich habe nicht verschleiert gesagt, es verschleiert nicht, aber hier sehen Sie das gesamte verschleierte Verzeichnis. Wie so?


Wenn Sie sich den Bildschirm oben ansehen, können Sie leicht die Klassen finden, die für das Umbenennen von Objekten verantwortlich sind (SimpleNameFactory, ClassRenamer ...). Wie oben erwähnt, werden standardmäßig 26 lateinische Zeichen verwendet.

 public class SimpleNameFactory implements NameFactory { private static final int CHARACTER_COUNT = 26; private static final List cachedMixedCaseNames = new ArrayList(); private static final List cachedLowerCaseNames = new ArrayList(); private final boolean generateMixedCaseNames; private int index = 0; //… 

In der SimpleNameFactory-Klasse gibt es eine spezielle Methode zum Überprüfen von printNameSamples (), die uns die erwarteten Werte liefert

  public static void main(String[] args) { System.out.println("Some mixed-case names:"); printNameSamples(new SimpleNameFactory(true), 60); System.out.println("Some lower-case names:"); printNameSamples(new SimpleNameFactory(false), 60); System.out.println("Some more mixed-case names:"); printNameSamples(new SimpleNameFactory(true), 80); System.out.println("Some more lower-case names:"); printNameSamples(new SimpleNameFactory(false), 80); } private static void printNameSamples(SimpleNameFactory factory, int count) { for (int counter = 0; counter < count; counter++) { System.out.println(" ["+factory.nextName()+"]"); } } 

 Some mixed-case names: [a] [b] [c] [d] [e] [f] [g] [h] [i] [j] [k] ... 

Die Obfuscator-Klasse ist für die „Verschleierung“ verantwortlich, bei der es eine einzige Ausführungsmethode gibt, bei der der gesamte gesammelte Klassenpool des Projekts selbst und alle hinzugefügten Bibliotheken übertragen werden.

 public class Obfuscator { private final Configuration configuration; //... public void execute(ClassPool programClassPool, ClassPool libraryClassPool) throws IOException { // Check if we have at least some keep commands. if (configuration.keep == null && configuration.applyMapping == null && configuration.printMapping == null) { throw new IOException("You have to specify '-keep' options for the obfuscation step."); } //... 

Zusätzlich zu ProGuard gibt es eine Optimierung, die von der Optimizer-Klasse gestartet wird, wodurch eine sehr wichtige Funktion zum Bereinigen nicht betriebsbereiter Ressourcen ausgeführt wird. Es berücksichtigt auch die vom Entwickler festgelegten Parameter. Wenn Sie also sicher sein möchten, dass der Code sicher ist, können Sie jederzeit Regeln dafür vorschreiben. Die Optimierung wird über die ProGuard-Klasse gestartet.

  /** * Performs the optimization step. */ private boolean optimize(int currentPass, int maxPasses) throws IOException { if (configuration.verbose) { System.out.println("Optimizing (pass " + currentPass + "/" + maxPasses + ")..."); } // Perform the actual optimization. return new Optimizer(configuration).execute(programClassPool, libraryClassPool, injectedClassNameMap); } 

Die Arbeit von Proguard kann in mehrere Phasen unterteilt werden:

  1. Lesen Sie die voreingestellten Regeln
  2. Optimierung
  3. Löschen von zur Optimierung gekennzeichneten Assets
  4. Objekte umbenennen
  5. Aufzeichnung des Projekts im angegebenen Verzeichnis in überarbeiteter Form

Sie können Proguard manuell mit dem folgenden Befehl starten:

 java -jar proguard.jar @android.pro 

Wobei proguard.jar das von ProGuard zusammengestellte Projekt ist und android.pro die Regeln für die Arbeit mit Eingabe- und Ausgabeparametern sind.

Warum das Schreiben eines eigenen ProGuard zu schmerzhaft ist


Während ich den ProGuard-Code erklomm, sah ich in der Kolumne des Autors nur einen Namen - Eric Lafortune. Durch schnelles Googeln fand ich seine persönliche Seite . Wenn jemand interessiert ist, können Sie sich mit ihm vertraut machen.

Google stellt ProGuard für uns als einzige Lösung zur Optimierung und zum Schutz seines Codes zur Verfügung. Tatsächlich ist es die einzige. Alle anderen Lösungen werden entweder bezahlt oder auf Github und mit Staub bedeckt, und ich persönlich würde Ihnen nicht raten, sie in Ihren Projekten zu verwenden, da das Hauptproblem bei der Minimierung, Verschleierung, Neuverpackung und Optimierung darin besteht, dass es jederzeit zu einer Kollision kommen kann. da es schwierig ist, alle Optionen vorherzusehen, die mit dem Code passieren könnten. Außerdem muss ein solches Dienstprogramm mit Tests so eng wie möglich abgedeckt werden, und wer macht das gerne? :) Leider sprechen alle gerne über Tests, schreiben sie aber nicht.

Warum ProGuard verwendet wird, ist nutzlos


ProGuard arbeitet nach Regeln, von denen bekannt ist, dass sie bekannt sind. Wenn Sie die Regeln nicht festlegen und einfach in Ihr Projekt aufnehmen, ist der Zugriff auf den Code eines Angreifers nicht schwierig, da Wechselrichter bereits seit langer Zeit geschrieben wurden und gemeinfrei sind. Wenn Sie das Thema genauer studieren und die Regeln hinzufügen, wird es natürlich schwieriger, aber sehr wenig. Was ist der Ausweg?

Unternehmen, bei denen das Verbergen ihres Codes Priorität hat, bleiben bei ProGuard und passen ihn an ihre Bedürfnisse an, um eine einzigartige Lösung zu erhalten.

Warum, warum hast du nichts zu tun?


Im Allgemeinen ist Proguard kein großer Nutzen, und dort passiert nichts Übernatürliches. Daher ist es durchaus möglich, die Quelle zu untersuchen, indem Sie ein paar Abende mit einem Glas Tee verbringen und eine Katze streicheln. Warum brauchst du das? Um die Tools, mit denen Sie arbeiten, genauer zu kennen und zu verstehen, was sie mit Ihrem Code tun, brauchen Sie sie wirklich. Dies gilt nicht nur für ProGuard, sondern auch für jeden anderen Code von Drittanbietern, den Sie in Ihrem Projekt verwenden. Ihr Code liegt in Ihrem Verantwortungsbereich und ist darin sauber. Ansonsten, was bringt die Entwicklung?

Der Artikel wurde vor ungefähr sechs Monaten geschrieben und Proguard entwickelt sich ständig weiter, da einige Fragmente möglicherweise nicht mehr zusammenfallen.

PS Ich veröffentliche alle Sammlungen wie immer im Telegrammkanal @paradisecurity , und der Link kann in meinem Profil oder bei der Suche nach Telegrammen nach Namen gefunden werden.

Source: https://habr.com/ru/post/de436564/


All Articles