Cómo funciona ProGuard


Si alguna vez pensó en la seguridad de su aplicación, o de alguna manera quiso optimizar su código, entonces, seguro, ya sabe qué es ProGuard. Quizás ya lo haya sufrido o haya podido superar la documentación, un par de artículos en el vasto y descubrió lo que estaba sucediendo.

En este artículo no hablaré sobre cómo preservar las reglas de mantenimiento ni sobre algunas opciones útiles. En mi opinión, para arrastrar ProGuard a un proyecto, es suficiente mirar los tutoriales adjuntos. Analizaré cómo funciona ProGuard en términos de código. Y si estás interesado, bienvenido a cat.

Muchos están equivocados acerca de ProGuard, creyendo erróneamente que es un ofuscador, lo cual no es por varias razones:

  • No mezcla código
  • No encripta el código.

La tarea principal de ProGuard es cambiar los nombres de objetos, clases, métodos, lo que complica el análisis del código para el ingeniero inverso. Además, también optimiza el código, eliminando recursos no utilizados en el programa. Pero, en última instancia, en el sentido clásico, no puede llamarse ofuscador.

Entonces, ¿con qué estamos tratando?


En general, ProGuard es una utilidad de código abierto que funciona con código Java. Sí, y también está escrito en java. Los chicos que se ocupan de él también están desarrollando DexGuard, que también se puede encontrar en espacios abiertos, si se susurra, ya que se fusionan periódicamente. Pero en general, DexGuard se considera pagado, de hecho, es una versión más difícil del mismo ProGuard.

Por lo tanto, podemos concluir que ProGuard es un archivo jar que reorganiza los caracteres de nuestro código, lo optimiza y, al parecer, mejora la seguridad. Por defecto, ProGuard funciona con 26 letras en inglés mayúsculas y minúsculas.

Las reglas para la protección se arrastran de un proyecto a otro y se consideran inviolables, porque no se tocan: así es como funciona, o de lo contrario podrían comenzar algunas líneas rojas infernales, que nadie sabe arreglar, y no quiere saber. Y algún oráculo elaboró ​​estas reglas, para llegar a lo que debes dar la vuelta siete veces, convertirte en un pájaro y volar hacia el sudoeste durante dos horas cuarenta y tres minutos.

Bueno, como nadie quiere ir allí y nadie necesita ir allí, vamos a ponernos en orden.

Si observa los directorios del proyecto proguard, puede identificar inmediatamente sus funciones principales.


Hasta ahora, todo parece estar claro. Así que echemos un vistazo a la clase principal.

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

Bueno, veamos el método principal obvio aquí, en el que vemos cómo se establecen las reglas predeterminadas y se analizan las establecidas por el propio desarrollador.

Además, existe el objeto esperado injectedClassNameMap, con el que obtenemos el archivo build / salidas / proguard / release / mapping.txt que se parece a esto:


Entonces, si de repente queremos abrir nuestro propio código y hacerlo legible, usando mapping.txt podemos hacer esto. Para hacer esto, al publicar el archivo apk, debe descargar la versión de mapping.txt en Google Play Console.

Ahora puede ver el analizador de configuraciones que establece el desarrollador.

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

Uh, no dije ofuscador, no ofusca, pero aquí puedes ver todo el directorio de ofuscación. ¿Cómo es eso?


Si mira la pantalla de arriba, puede encontrar fácilmente las clases responsables del cambio de nombre de los objetos (SimpleNameFactory, ClassRenamer ...). Como dije anteriormente, 26 caracteres latinos se usan por defecto.

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

En la clase SimpleNameFactory, hay un método especial para verificar printNameSamples () que nos dará los valores esperados

  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 clase Obfuscator es responsable de la "ofuscación", en la que hay un único método de ejecución, donde se transfiere todo el conjunto de clases recopiladas del proyecto en sí y todas las bibliotecas agregadas.

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

Además de ProGuard, existe una optimización que lanza la clase Optimizer, por lo que realiza una función muy importante para limpiar recursos no operativos. También tiene en cuenta los parámetros establecidos por el desarrollador. Entonces, si desea estar seguro de la seguridad del código, siempre puede prescribir reglas para ello. La optimización se inicia desde la clase 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); } 

El trabajo de proguard se puede dividir en varias etapas:

  1. Leer reglas preestablecidas
  2. Optimización
  3. Eliminar activos marcados para optimización
  4. Renombrar objetos
  5. Registro del proyecto en el directorio especificado en forma revisada

Puede iniciar proguard manualmente con el comando:

 java -jar proguard.jar @android.pro 

Donde proguard.jar es el proyecto ensamblado por ProGuard, y android.pro son las reglas para trabajar con parámetros de entrada y salida.

Por qué escribir tu propio ProGuard es demasiado doloroso


De hecho, mientras estaba escalando el código ProGuard, solo vi un nombre en la columna del autor: Eric Lafortune. Al buscar rápidamente en Google, encontré su sitio personal , si alguien está interesado, puede familiarizarse con él.

Google nos ofrece ProGuard como la única solución para optimizar y proteger su código, y de hecho es la única. Todas las demás soluciones son pagas o en github y están cubiertas de polvo, y personalmente no le recomendaría que intente usarlas en sus proyectos, porque el principal problema con la minificación, la ofuscación, el reempaquetado y la optimización es que en cualquier momento puede haber una colisión, ya que es difícil prever todas las opciones que podrían sucederle al código. Además, dicha utilidad debe cubrirse lo más estrictamente posible con las pruebas, y ¿a quién le gusta hacer esto? :) Desafortunadamente, a todos les gusta hablar sobre las pruebas, pero no escribirlas.

Por qué usar ProGuard es inútil


ProGuard funciona de acuerdo con reglas que se sabe que son conocidas, y si no establece las reglas y simplemente las incluye en su proyecto, acceder al código para un atacante no será difícil, porque los inversores se han escrito durante mucho tiempo y son de dominio público. Por supuesto, si estudia el tema con más detalle y agrega las reglas, será más difícil, pero muy poco. ¿Cuál es la salida?

Las empresas para las cuales ocultar su código es una prioridad, se adhieren a ProGuard y lo modifican para que se ajuste a sus necesidades, obteniendo así una solución única.

¿Por qué, por qué, no tienes nada que hacer?


En general, proguard no es una gran utilidad y no sucede nada sobrenatural allí, por lo que es muy posible estudiar la fuente pasando un par de noches con una taza de té y acariciando a un gato. ¿Por qué necesitas esto? Para conocer con más detalle las herramientas con las que trabaja y comprender qué hacen con su código, ¿realmente las necesita? Esto se aplica no solo a ProGuard, sino también a cualquier otro código de terceros que utilice en su proyecto. Su código es su área de responsabilidad y manténgalo limpio; de lo contrario, ¿de qué sirve desarrollar?

El artículo fue escrito hace unos seis meses, y Proguard está en constante evolución, porque algunos fragmentos ya no pueden coincidir.

PD: publico todas las colecciones como siempre en el canal de telegramas @paradisecurity , y el enlace se puede encontrar en mi perfil, o en la búsqueda de telegramas por nombre.

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


All Articles