Skip to content

Ofuscando aplicativos Android

brunobastosg edited this page Jul 19, 2012 · 1 revision

Habilitando a ofuscação

Ao contrário do que muita gente pensa, um aplicativo Android NÃO é ofuscado por padrão. Para habilitar a ofuscação do código, basta editar o arquivo project.properties*, localizado na raiz de seu projeto Android e incluir a seguinte linha:

proguard.config=proguard.cfg

Sendo que proguard.cfg é o caminho completo para o seu arquivo de configuração do Proguard. Neste caso, ele também está localizado na raiz do projeto. Por isso, só colocamos o nome do arquivo, sem caminho algum.

Feito isso, toda vez que você gerar um .APK clicando no projeto com o botão direito, indo em Android Tools e depois em Export Signed Application Package, o código será ofuscado. O mesmo vale para a opção Export Unsigned Application Package.

Agora que já vimos como habilitar a ofuscação, falta o mais difícil: configurá-la através do arquivo proguard.cfg.

* Este tutorial assume que o desenvolvedor está utilizando o Eclipse com o plugin ADT.

Configurando a ofuscação

Para prosseguirmos com a ofuscação do aplicativo, precisamos criar o arquivo proguard.cfg e incluir as regras de ofuscação. Este arquivo vai informar para o Proguard se o código será otimizado ou apenas ofuscado, que tipo de otimização será feita, que classes não serão ofuscadas, entre outras coisas.

Mas por quê não ofuscar classes, se o objetivo é justamente impedir (ou pelo menos dificultar) a obtenção do código através da decompilação do mesmo?

Basicamente, a razão principal para uma classe não ser ofuscada é que ela, ou algum atributo ou método dela, é acessada via reflexão. No Android, diversas classes especiais são acessadas via reflexão e, por isso, nunca devem ser ofuscadas, sob a pena de o aplicativo parar de funcionar.

A notícia boa é que não precisaremos escrever este arquivo do zero. O SDK do Android disponibiliza dois exemplos de arquivos de configuração: um com otimização e um sem otimização. Neste tutorial, usaremos a versão sem otimização, pois algumas otimizações não funcionam em todas as versões da máquina virtual Dalvik, utilizada pelo Android. Então, é mais seguro não otimizar. Além disso, nosso objetivo principal é dificultar a obtenção do código por questões de segurança, e não otimizar.

Para obter este arquivo, vá até a pasta onde o seu SDK Android está instalado e entre na pasta tools/proguard/. Nela, você verá os arquivos proguard-android.txt e proguard-android-optimize.txt. Conforme dito anteriormente, vamos utilizar a versão sem otimização. Então, copie o proguard-android.txt para a raiz do seu projeto com o nome proguard.cfg.

Abaixo, uma cópia da versão deste arquivo conforme a versão R20 do SDK (a mais atual, na data em que este tutorial foi escrito). Os comentários foram removidos para facilitar a leitura.

-dontusemixedcaseclassnames 
-dontskipnonpubliclibraryclasses 
-verbose 

-dontoptimize 
-dontpreverify 

-keepattributes *Annotation* 
-keep public class com.google.vending.licensing.ILicensingService 
-keep public class com.android.vending.licensing.ILicensingService 

-keepclasseswithmembernames class * { 
    native <methods>; 
} 

-keepclassmembers public class * extends android.view.View { 
   void set*(***); 
   *** get*(); 
} 

-keepclassmembers class * extends android.app.Activity { 
   public void *(android.view.View); 
} 

-keepclassmembers enum * { 
    public static **[] values(); 
    public static ** valueOf(java.lang.String); 
} 

-keep class * implements android.os.Parcelable { 
  public static final android.os.Parcelable$Creator *; 
} 

-keepclassmembers class **.R$* { 
    public static <fields>; 
} 

-dontwarn android.support.**

Se seu aplicativo Android for simples e não utiliza nenhuma biblioteca externa, provavelmente este arquivo será suficiente para que a ofuscação seja efetuada com sucesso. Agora, vamos destrinchar este arquivo, explicando o que significa cada uma dessas opções.


-dontusemixedcaseclassnames

Um dos resultados da ofuscação é que as classes, métodos e atributos são renomeados para nomes incompreensíveis (ou MENOS compreensíveis), como A, B, C, Aa, Ab, aA, aB, etc. Porém, se você estiver em um sistema operacional case insensitive, como o Windows, as classes Aa e aA serão consideradas o mesmo arquivo, causando um erro. Logo, utilizando esta opção, você garante que as classes geradas terão apenas letras minúsculas. Se estiver no Linux ou no Mac OS, esta opção não será necessária.


-dontskipnonpubliclibraryclasses

Esta opção faz o Proguard não ignorar as classes não-públicas das bibliotecas utilizadas no projeto. A partir da versão 4.5 do Proguard, esta opção já está habilitada por padrão. Porém, em versões anteriores, se esta opção não fosse especificada, o Proguard não iria processar classes que não fossem públicas das bibliotecas, tornando o processo de ofuscação mais rápido, principalmente se o projeto utilizar muitas bibliotecas.


-verbose

Apenas facilita na depuração, caso ocorra algum erro no processo de ofuscação.


-dontoptimize

Desabilita a otimização de código. Por padrão, o código é otimizado. Logo, é importante utilizar esta opção.


-dontpreverify

Desabilita a pre-verificação, que é irrelevante para o compilador dex e para a máquina virtual Dalvik.


-keepattributes *Annotation*

Não ofusca as anotações de código.


-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

Essas duas opções só são necessárias caso seu aplicativo utilize o serviço de licenças do Android.


-keepclasseswithmembernames class * { 
    native <methods>; 
}

Não ofusca os métodos nativos (métodos escritos em C/C++, por exemplo). Essa opção é necessária, pois o SDK Android possui métodos nativos internamente.


-keepclassmembers public class * extends android.view.View { 
   void set*(***); 
   *** get*(); 
}

Não ofusca os getters e setters de classes que herdem de android.view.View. Isto é necessário se você utiliza animações numa View, pois o framework de animações do Android utiliza reflexão para tal.


-keepclassmembers class * extends android.app.Activity { 
   public void *(android.view.View); 
}

Não ofusca métodos de Activities que recebam um android.view.View como parâmetro. A intenção desta configuração é não ofuscar os métodos onClick, pois os mesmos podem ser chamados através de um XML, via reflexão.


-keepclassmembers enum * { 
    public static **[] values(); 
    public static ** valueOf(java.lang.String); 
}

Se seu aplicativo utiliza enums, é necessário não ofuscar os métodos values() e valueOf(java.lang.String), pois a máquina virtual chama esses métodos via reflexão. No caso do Android, esta opção é obrigatória, pois nas suas classes existem diversas enums.


-keep class * implements android.os.Parcelable { 
  public static final android.os.Parcelable$Creator *; 
}

A interface android.os.Parcelable é o correspondente Android da interface Serializable, caso deseje serializar objetos. Não podemos ofuscar classes que implementem Parcelable e seus atributos estáticos, pois são acessados via reflexão.


-keepclassmembers class **.R$* { 
    public static <fields>; 
}

Não ofusca os atributos das classes internas oriundas da classe R. Isto é necessário pois estes atributos têm o mesmo nome dos XML's correspondentes. Se fossem ofuscados, ficariam diferentes dos XML's.


-dontwarn android.support.**

Se seu projeto utiliza a biblioteca Android Support, para fazer uso de Fragments e outras funcionalidades da versão 4.0 do Android, o Proguard lança vários warnings ao tentar ofuscar o código. Esta entrada serve para ele ignorar esses warnings, fazendo a ofuscação concluir com sucesso. Segundo a própria documentação do Android, é seguro fazer isso.

Ofuscando aplicativos que utilizam bibliotecas externas

O arquivo proguard.cfg exibido anteriormente vem como exemplo no SDK Android. Porém, ele assume que seu aplicativo não utiliza nenhuma biblioteca externa.

Vamos supor que nosso aplicativo utilize as bibliotecas RoboGuice (para injeção de dependência), KSoap2 (para trabalhar com webservices SOAP) e AlienDroid (para persistência). Abaixo, as entradas que teremos que adicionar ao arquivo proguard.cfg, e suas explicações.


-keep class com.google.inject.Binder

Não ofusca a interface com.google.inject.Binder. Segundo a documentação do RoboGuice, não devemos ofuscar esta interface. Provavelmente, é acessada via reflexão.


-keepclassmembers class * { 
    void *(**On*Event); 
}

Se seu aplicativo utilizar a funcionalidade de manipulação de eventos do RoboGuice, esta entrada é necessária para não ofuscar os métodos que tratam os eventos.


-keepattributes *Annotation*,Signature

Anteriormente, utilizamos o a entrada -keepattributes Annotation para manter as anotações não ofuscadas. Porém, para o RoboGuice funcionar, precisamos adicionar a opção Signature para que o generics funcione.


-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
    *** get*(); 
}

Esta entrada é parecida com uma que vimos anteriormente, que não ofusca os getters e setters das Views. No caso do RoboGuice, precisamos que a própria View não seja ofuscada, e nem seus construtores.


-keep class roboguice.** { *; }
-keep class com.google.inject.** { *; }
-keep class javax.inject.** { *; }
-keep class javax.annotation.** { *; }

Estas quatro opções não ofuscam as classes do RoboGuice e suas dependências. Como é uma biblioteca externa, não faz sentido ofuscar.


-dontwarn com.xtremelabs.**
-dontwarn org.junit.**
-dontwarn roboguice.activity.RoboMapActivity

Essas três opções são para ignorar alguns warnings causados pelo RoboGuice, quando seu aplicativo não possui testes unitários com JUnit e Roboelectric, e/ou quando não utiliza a API do Google Maps.


-keep class org.xmlpull.** { *; }

Se seu aplicativo utiliza KSoap2, esta opção é necessária, pois há um conflito na classe org.xmlpull.v1.XmlPullParser, presente tanto no Android quanto no KSoap2.


-keep class com.alienlabz.** { *; }

Esta opção ignora as classes da biblioteca AlienDroid, para persistência. As mesmas não serão ofuscadas por serem externas.


-keep public class * extends com.alienlabz.activerecord.Model { *; }

Também não podemos ofuscar as classes do nosso aplicativo que herdem da classe com.alienlabz.activerecord.Model do AlienDroid, pois é utilizada reflexão para persistir e retornar os atributos em/do banco de dados.

Mais algumas opções

Agora veremos mais algumas opções interessantes que podem ser incluídas no proguard.cfg.


-repackageclasses ''

Esta opção pega todas as classes que foram ofuscadas e move para o pacote definido. Neste caso, como passamos uma String vazia, todas as classes perderão o pacote.

Arquivo final

Abaixo, versão final de como fica o arquivo proguard.cfg para um aplicativo Android que utiliza o Android Support V4, além das bibliotecas RoboGuice, KSoap2 e AlienDroid. Pode copiar e colar no seu projeto, se assim desejar. Coloquei comentários para maior clareza do que cada trecho está fazendo.

# CONFIGURAÇÕES PADRÃO DO ANDROID

-dontpreverify

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

-keepclassmembers class **.R$* {
	public static <fields>;
} 

-keepclasseswithmembernames class * {
	native <methods>;
} 

-keepclassmembers class * extends java.lang.Enum {
    public static **[] values();
    public static ** valueOf(java.lang.String); 
 }


# CONFIGURAÇÕES DO ROBOGUICE

-dontwarn com.xtremelabs.**
-dontwarn org.junit.**
-dontwarn roboguice.activity.RoboMapActivity

-keepattributes *Annotation*,Signature

-keep class com.google.inject.Binder

-keepclassmembers class * { 
    void *(**On*Event); 
}

-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}

-keep class roboguice.** { *; }
-keep class com.google.inject.** { *; }
-keep class javax.inject.** { *; }
-keep class javax.annotation.** { *; }


# CONFIGURAÇÕES DO KSOAP2

-dontwarn org.xmlpull.**

-keep class org.xmlpull.** { *; }


# CONFIGURAÇÕES DO ALIENDROID

-keep public class * extends com.alienlabz.activerecord.Model { *; }
-keep class com.alienlabz.** { *; }


# CONFIGURAÇÕES DO ANDROID SUPPORT

-dontwarn android.support.**


# CONFIGURAÇÕES EXTRAS

-dontoptimize
-dontusemixedcaseclassnames 
-repackageclasses ''