Fonctionnement de ProGuard


Si vous avez déjà pensé à la sécurité de votre application ou que vous vouliez en quelque sorte optimiser votre code, vous savez certainement ce qu'est ProGuard. Peut-être que vous en avez déjà souffert ou que vous avez pu surmonter la documentation, quelques articles dans le vaste et compris ce qui se passait.

Dans cet article, je ne parlerai pas de la façon de conserver les règles de conservation ni de certaines options utiles. À mon avis, pour faire glisser ProGuard dans un projet, il suffit de regarder les tutoriels qui y sont attachés. J'analyserai le fonctionnement de ProGuard en termes de code. Et si vous êtes intéressé - bienvenue au chat.

Beaucoup se trompent sur ProGuard, croyant à tort qu'il s'agit d'un obfuscateur, ce qui n'est pas le cas pour plusieurs raisons:

  • Il ne mélange pas le code
  • Il ne crypte pas le code.

La tâche principale de ProGuard est de changer les noms des objets, classes, méthodes, compliquant ainsi l'analyse du code pour le reverse engineering. De plus, il optimise également le code, supprimant les ressources non utilisées dans le programme. Mais, en fin de compte, au sens classique, il ne peut pas être qualifié d'obscurcisseur.

Alors, de quoi s'agit-il?


En général, ProGuard est un utilitaire open source qui fonctionne avec du code java. Oui, et il est également écrit en java. Les gars qui s'en occupent développent également DexGuard, qui peut également être trouvé dans des espaces ouverts, si vous faites du bruit, car ils fusionnent périodiquement. Mais en général, DexGuard est considéré comme payé, en fait étant une version plus dure du même ProGuard.

Nous pouvons donc conclure que ProGuard est un fichier jar qui réorganise les caractères de notre code, l'optimise et, semble-t-il, améliore la sécurité. Par défaut, ProGuard fonctionne avec 26 lettres anglaises majuscules et minuscules.

Les règles de proguard sont glissées d'un projet à l'autre et sont considérées comme inviolables, car ne les touchez pas - cela fonctionne, sinon des lignes rouges infernales pourraient commencer, que personne ne sait réparer et ne veut pas savoir. Et un oracle a établi ces règles, pour arriver à ce que vous devez tourner sept fois, vous transformer en oiseau et voler vers le sud-ouest pendant deux heures quarante-trois minutes.

Eh bien, puisque personne ne veut y aller et que personne n'a besoin d'y aller, passons aux choses .

Si vous regardez les répertoires du projet proguard, vous pouvez immédiatement identifier ses principales fonctions.


Jusqu'à présent, tout semble clair. Jetons donc un coup d'œil à la classe principale.

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()); //... 

Eh bien, passons à la méthode principale évidente ici, dans laquelle nous voyons comment les règles par défaut sont définies et celles définies par le développeur lui-même sont analysées.

De plus, il y a l'objet attendu injectedClassNameMap, avec lequel nous obtenons le fichier build / outputs / proguard / release / mapping.txt qui ressemble à ceci:


Donc, si nous voulons soudainement ouvrir notre propre code et le rendre lisible, en utilisant mapping.txt, nous pouvons le faire. Pour ce faire, lors de la publication du fichier apk, vous devez télécharger la version de mapping.txt dans la console Google Play.

Vous pouvez maintenant regarder l'analyseur de configurations que le développeur définit.

 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); //... 

Euh, je n'ai pas dit d'obscurcisseur, il ne fait pas d'obscurcissement, mais ici vous pouvez voir tout le répertoire d'obscurcissement. Comment ça?


Si vous regardez l'écran ci-dessus, vous pouvez facilement trouver les classes chargées de renommer les objets (SimpleNameFactory, ClassRenamer ...). Comme je l'ai dit plus haut, 26 caractères latins sont utilisés par défaut.

 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; //… 

Dans la classe SimpleNameFactory, il existe une méthode spéciale pour vérifier printNameSamples () qui nous donnera les valeurs attendues

  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] ... 

La classe Obfuscator est responsable de l '«obscurcissement», dans lequel il existe une seule méthode d'exécution, où l'ensemble du pool de classes du projet lui-même et toutes les bibliothèques qui y sont ajoutées sont transférés.

 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."); } //... 

En plus de ProGuard, il existe une optimisation que la classe Optimizer lance, exécutant ainsi une fonction très importante pour nettoyer les ressources non opérationnelles. Il prend également en compte les paramètres définis par le développeur. Donc, si vous voulez être sûr de la sécurité du code, vous pouvez toujours lui prescrire des règles. L'optimisation est lancée à partir de la classe ProGuard.

  /** * 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); } 

Le travail de proguard peut être divisé en plusieurs étapes:

  1. Lire les règles prédéfinies
  2. Optimisation
  3. Suppression d'actifs signalés pour optimisation
  4. Renommer des objets
  5. Enregistrement du projet dans le répertoire spécifié sous une forme révisée

Vous pouvez démarrer proguard manuellement avec la commande:

 java -jar proguard.jar @android.pro 

Où proguard.jar est le projet assemblé par ProGuard et android.pro sont les règles de travail avec les paramètres d'entrée et de sortie.

Pourquoi écrire votre propre ProGuard est trop douloureux


En fait, alors que je montais le code ProGuard, je n'ai vu qu'un seul nom dans la colonne de l'auteur - Eric Lafortune. En cherchant rapidement sur Google, j'ai trouvé son site personnel , si quelqu'un est intéressé, vous pouvez vous familiariser avec lui.

Google nous propose ProGuard comme la seule solution pour optimiser et protéger son code, et en fait c'est la seule. Toutes les autres solutions sont payantes ou sur github et couvertes de poussière, et personnellement, je ne vous conseillerais pas d'essayer de les utiliser dans vos projets, car le principal problème avec la minification, l'obscurcissement, le reconditionnement et l'optimisation est qu'à tout moment il peut y avoir une collision, car il est difficile de prévoir toutes les options qui pourraient arriver au code. De plus, un tel utilitaire doit être couvert le plus étroitement possible par des tests, et qui aime le faire? :) Malheureusement, tout le monde aime parler de tests, mais pas de les écrire.

Pourquoi utiliser ProGuard est inutile


ProGuard fonctionne selon des règles connues pour être connues, et si vous ne définissez pas les règles et ne les incluez pas simplement dans votre projet, l'accès au code pour un attaquant ne sera pas difficile, car les onduleurs ont été écrits depuis longtemps et sont dans le domaine public. Bien sûr, si vous étudiez le sujet plus en détail et ajoutez les règles, ce sera plus difficile, mais très peu. Quelle est la sortie?

Les entreprises pour lesquelles masquer leur code est une priorité, restez avec ProGuard et modifiez-le en fonction de leurs besoins, obtenant ainsi une solution unique.

Pourquoi, pourquoi, tu n'as rien à faire?


En général, proguard n'est pas un énorme utilitaire et rien de surnaturel ne s'y passe, il est donc tout à fait possible d'étudier la source en passant quelques soirées avec un verre de thé et en caressant un chat. Pourquoi en avez-vous besoin? Pour en savoir plus sur les outils avec lesquels vous travaillez et comprendre ce qu'ils font avec votre code, en avez-vous vraiment besoin? Cela s'applique non seulement à ProGuard, mais également à tout autre code tiers que vous utilisez dans votre projet. Votre code est votre domaine de responsabilité et soyez clair en lui; sinon, quel est l'intérêt de faire du développement?

L'article a été écrit il y a environ six mois, et proguard évolue constamment, car certains fragments peuvent ne plus coïncider.

PS Je publie toutes les collections comme toujours dans la chaîne de télégramme @paradisecurity , et le lien peut être trouvé dans mon profil, ou trouvé dans la recherche de télégrammes par nom.

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


All Articles