Como o ProGuard funciona


Se você já pensou na segurança do seu aplicativo ou de alguma forma quis otimizar seu código, certamente sabe o que é o ProGuard. Talvez você já tenha sofrido com isso ou tenha conseguido superar a documentação, alguns artigos no vasto e descobriu o que estava acontecendo.

Neste artigo, não falarei sobre como preservar as regras de manutenção ou sobre algumas opções úteis. Na minha opinião, para arrastar o ProGuard para um projeto, basta examinar os tutoriais anexados a ele. Analisarei como o ProGuard funciona em termos de código. E se você estiver interessado - bem-vindo ao gato.

Muitos estão enganados sobre o ProGuard, acreditando erroneamente que é um ofuscador, o que não ocorre por várias razões:

  • Não mistura código
  • Não criptografa o código.

A principal tarefa do ProGuard é alterar os nomes de objetos, classes, métodos, complicando assim a análise do código para o engenheiro reverso. Além disso, ele também otimiza o código, removendo recursos não utilizados no programa. Mas, finalmente, no sentido clássico, não pode ser chamado de ofuscador.

Então, com o que estamos lidando?


Em geral, o ProGuard é um utilitário de código aberto que funciona com código java. Sim, e também está escrito em java. As pessoas que lidam com isso também estão desenvolvendo o DexGuard, que também pode ser encontrado em espaços abertos, se você mexer, à medida que se fundem periodicamente. Mas, em geral, o DexGuard é considerado pago, na verdade, sendo uma versão mais difícil do mesmo ProGuard.

Portanto, podemos concluir que o ProGuard é um arquivo jar que reorganiza caracteres em nosso código, otimiza-o e, ao que parece, melhora a segurança. Por padrão, o ProGuard trabalha com 26 letras em inglês maiúsculas e minúsculas.

As regras para o progresso são arrastadas de um projeto para o outro e são consideradas invioláveis, porque não a tocam - funciona, ou então podem começar algumas linhas vermelhas infernais, que ninguém sabe corrigir e não quer saber. E algum oráculo elaborou essas regras, para chegar às quais você precisa se virar sete vezes, virar um pássaro e voar para o sudoeste por duas horas e quarenta e três minutos.

Bem, como ninguém quer ir para lá e ninguém precisa ir para lá, vamos entrar em ordem .

Se você olhar os diretórios do projeto proguard, poderá identificar imediatamente suas principais funções.


Até agora, tudo parece estar claro. Então, vamos dar uma olhada na classe 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()); //... 

Bem, vamos nos deparar com o método principal óbvio aqui, no qual vemos como as regras padrão são definidas e as definidas pelo próprio desenvolvedor são analisadas.

Além disso, existe o objeto esperado injectedClassNameMap, com o qual obtemos o arquivo build / outputs / proguard / release / mapping.txt que se parece com isso:


Portanto, se de repente queremos abrir nosso próprio código e torná-lo legível, usando o mapping.txt, podemos fazer isso. Para fazer isso, ao publicar o arquivo apk, você precisa baixar a versão do mapping.txt no Google Play Console.

Agora você pode olhar para o analisador de configurações que o desenvolvedor define.

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

Eu não disse ofuscador, ele não ofuscou, mas aqui você pode ver todo o diretório de ofuscação. Como assim?


Se você observar a tela acima, poderá encontrar facilmente as classes responsáveis ​​por renomear objetos (SimpleNameFactory, ClassRenamer ...). Como eu disse acima, 26 caracteres latinos são usados ​​por padrão.

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

Na classe SimpleNameFactory, existe um método especial para verificar printNameSamples () que nos fornecerá os 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] ... 

A classe Obfuscator é responsável pela "ofuscação", na qual existe um único método de execução, onde são transferidos todo o conjunto de classes coletadas do próprio projeto e todas as bibliotecas adicionadas a ele.

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

Além do ProGuard, há uma otimização iniciada pela classe Optimizer, desempenhando uma função muito importante para limpar recursos não operacionais. Ele também leva em consideração os parâmetros definidos pelo desenvolvedor. Portanto, se você quiser ter certeza da segurança do código, poderá sempre prescrever regras para ele. A otimização é iniciada na 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); } 

O trabalho do proguard pode ser dividido em várias etapas:

  1. Ler regras predefinidas
  2. Otimização
  3. Exclusão de ativos sinalizados para otimização
  4. Renomear objetos
  5. Registro do projeto no diretório especificado em um formulário revisado

Você pode iniciar o programa manualmente com o comando:

 java -jar proguard.jar @android.pro 

Onde proguard.jar é o projeto montado pelo ProGuard e android.pro são as regras para trabalhar com parâmetros de entrada e saída.

Por que escrever seu próprio ProGuard é muito doloroso


De fato, enquanto eu estava escalando o código do ProGuard, vi apenas um nome na coluna do autor - Eric Lafortune. Ao pesquisar rapidamente no Google, encontrei o site pessoal dele , se alguém estiver interessado, você pode se familiarizar com ele.

O Google coloca o ProGuard para nós como a única solução para otimizar e proteger seu código e, de fato, é o único. Todas as outras soluções são pagas ou no github e cobertas de poeira, e pessoalmente eu não aconselharia que você tentasse usá-las em seus projetos, porque o principal problema com minificação, ofuscação, reembalagem e otimização é que, a qualquer momento, pode haver uma colisão, já que é difícil prever todas as opções que podem acontecer com o código. Além disso, esse utilitário precisa ser coberto da melhor maneira possível com os testes, e quem gosta de fazer isso? :) Infelizmente, todo mundo gosta de falar sobre testes, mas não de escrevê-los.

Por que usar o ProGuard é inútil


O ProGuard funciona de acordo com as regras conhecidas e, se você não as definir e simplesmente incluí-las em seu projeto, acessar o código de um invasor não será difícil, porque os inversores foram gravados por um longo tempo e estão em domínio público. Obviamente, se você estudar o tópico com mais detalhes e adicionar as regras, será mais difícil, mas muito pouco. Qual é a saída?

As empresas para as quais ocultar seu código é uma prioridade, permaneçam no ProGuard e modifique-o para atender às suas necessidades, obtendo uma solução exclusiva.

Por que, porque, você não tem nada para fazer?


Em geral, o proguard não é uma grande utilidade e nada sobrenatural acontece por lá; portanto, é bem possível estudar a fonte passando algumas noites com uma xícara de chá e acariciando um gato. Por que você precisa disso? Para conhecer com mais detalhes as ferramentas com as quais você trabalha e entender o que elas fazem com seu código, você realmente precisa delas. Isso se aplica não apenas ao ProGuard, mas também a qualquer outro código de terceiros usado em seu projeto. Seu código é sua área de responsabilidade e deve estar limpo, caso contrário, qual é o sentido de fazer desenvolvimento?

O artigo foi escrito há cerca de seis meses e o progresso está em constante evolução, porque alguns fragmentos podem não coincidir mais.

PS: Publico todas as coleções como sempre no canal de telegrama @paradisecurity , e o link pode ser encontrado no meu perfil ou na pesquisa de telegramas por nome.

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


All Articles