Na parte 01 dessa série, vimos que toda instalação do Java e do Android já possuem ferramentas para facilitar a engenharia reversa de código compilado. Porém, como podemos usar isso para testar a segurança do nosso aplicativo?
Vamos pensar em um fluxo imaginário com o que vimos até agora:
- Preciso pegar um APK que eu baixei da loja (Google Play);
- Preciso acessar o “classes.dex” que há dentro deste pacote;
- Preciso executar o hexdump nele para convertê-lo em bytecode Java;
- Preciso executar o javap no resultado para converter em source code Java;
- Faço alguma alteração neste arquivo e executo o javac (compilador) nele;
- Executamos o dx para recriar o “classes.dex”;
- Executamos o aapt para regerar o apk final.
Espero que com o que vimos até agora, o leitor já veja como isso tudo é possível. Porém, faltam alguns passos importantes aí no meio do caminho…
Em primeiro lugar, todo pacote é assinado e alinhado. Isso quer dizer que antes de você subir um APK para a loja, você precisa assiná-lo com uma chave. Assim, os recursos dentro do APK ficam criptografados de acordo com a sua chave. Vamos ver isso em um APK real? Claro!
Escolha um aplicativo instalado no seu device e vamos tentar achar onde esse “mardito” APK fica. Para isso, vamos abrir um “shell” no nosso device:
- Habilite o modo de desenvolvedor no seu device;
- Vá até a pasta “platform-tools” dentro da SDK do Android;
- Execute: adb -d shell
.
Pronto! Estamos dentro de um shell do nosso device. Lembre-se de que o Android é baseado em Linux, portanto, muitos comandos de shell de Linux funcionam nele.
Agora queremos listar todos os pacotes instalados no nosso device. Para isso executamos o comando:
pm list packages
Estamos usando um utilitário de linha de comando do Android que é um grande conhecido nosso: o Package Manager. Dentro do shell do Android podemos passar comandos para ele usando o binário pm.
Ao executar o comando acima, vemos uma lista gigante de pacotes. Vamos escolher um. No meu caso, será o aplicativo sample da nossa biblioteca de padrões brasileiros no Android, chamada de Canarinho. Poderíamos escolher qualquer pacote que baixamos do Google Play ou instalamos localmente. O pacote da aplicação neste caso é br.com.concretesolutions.canarinho.sample.
Agora vamos descobrir o caminho do APK desta aplicação instalada no device. Para isso, basta executar:
pm path br.com.concretesolutions.canarinho.sample
Outro comando do Package Manager para mostrar um caminho do arquivo dentro do device. Por exemplo:
/data/app/br.com.concretesolutions.canarinho.sample-2/base.apk
Agora, só precisamos puxar este arquivo do aparelho. Para isso, vamos sair do shell digitando ‘exit’ (ou apertando CTRL + D). De fora do shell do aparelho, mas ainda em um shell da nossa máquina, vamos usar o adb novamente para pegar o arquivo. Basta digitar:
adb -d pull /data/app/br.com.concretesolutions.canarinho.sample-2/base.apk
Lembre-se que o caminho para o arquivo a gente verificou anteriormente. Estou usando meu exemplo aqui do aplicativo de sample do Canarinho. Use o caminho do aplicativo que você escolheu.
Pronto! Sem root e sem ferramentas externas além do kit de desenvolvimento do próprio Android, nós conseguimos baixar um arquivo de aplicativo baixado da loja (ou instalado localmente).
Agora temos o arquivo que queremos ‘fuçar’ (termo técnico para analisar). Vamos começar do mais simples e ver o que temos. Primeiro, eu já disse que todo arquivo ‘.apk’ é simplesmente um arquivo zip que segue uma certa organização dos arquivos. Vamos ver isso: criem um diretório para nossos testes e coloquem o APK lá dentro. Pode ser qualquer diretório. Apenas não queremos sujar o diretório em que o APK está atualmente, pois ao processar os comandos abaixo vamos desempacotar diversos arquivos que compõem o APK.
Dentro deste diretório de teste, vamos desempacotar o APK. Executem:
unzip base.apk
Agora temos o APK desempacotado no nosso diretório de testes. Algo como a figura abaixo:
Ao olhar o resultado pela primeira vez, vimos que já temos logo de cara o AndroidManifest.xml! Será que conseguimos pegar o conteúdo dele já? Tentemos, irmãos!
cat AndroidManifest.xml
Obs: se não está em uma plataforma Unix, tente abrir o arquivo com um editor de texto qualquer.
O resultado é quase tudo em binário =( Estava fácil demais para conseguirmos…
Mas nem tudo está perdido. Mesmo antes de tentarmos algumas ferramentas mais avançadas de “hackers”, vemos que com um simples cat algumas informações sobre o build do binário já estão disponíveis. No meu caso, já consigo ver a versão do app entre outras coisas.
Tudo bem… Vamos em frente que logo, logo esse manifesto estará lindo e transparentemente traduzido para nós.
Nos outros arquivos temos:
- classes.dex: o conjunto de todas as classes do aplicativo (incluindo suas dependências);
- META-INF: diretório para metadados de binários JAVA (e que não deveria ser usado por aplicações Android);
- res: os resources (XMLs assinados) do aplicativo;
- resources.arsc: todos os recursos indexados e compilados.
Ou seja, parece que temos todos os arquivos para começarmos nossa engenharia reversa; mas todos estão ou assinados ou não acessíveis para nós.
No entanto, até esse momento só usamos as ferramentas padrões de uma instalação da JDK e do Android SDK. Precisamos de ferramentas mais pesadas para continuar. Vejamos uma hoje apenas para apetecer nossos leitores.
A Apktool é, sem sombra de dúvida, a ferramenta mais fundamental para quem quer começar a estudar engenharia reversa. Basicamente, ela nos dará tudo o que queríamos até agora: reverter a assinatura dos arquivos e nos dar quase tudo de mão aberta.
Para usá-la, baixe o jar no site e execute o seguinte comando:
java -jar apktool_2.1.0.jar d base.apk
Neste caso, estou usando a versão 2.1.0 do Apktool para descompilar (opção “d”) um APK chamado base.apk. Por padrão, ele coloca o resultado em uma pasta com o mesmo nome do APK (no meu caso: “base”).
Parece mágica, mas vamos olhar o AndroidManifest dentro deste diretório “base”:
cat base/AndroidManifest.xml
Agora sim! Temos um manifesto legível! Não tivemos quase nenhum esforço e já revertemos o manifesto. Pense em quantas aplicações colocam dados em um manifesto… Todos estes dados estão acessíveis agora para nós.
Neste ponto, é importante lembrar do “Uncle Ben”:
Traduzindo: nosso intuito com esta série é mostrar como a segurança do Android funciona e exemplificar ferramentas de análise comumente usadas para testar a segurança de nossos aplicativos.
Traduzindo mais diretamente: não usem essas ferramentas com intuitos maléficos.
E por enquanto, é só. Testem este fluxo em aplicativos da loja que vocês já tenham publicado e vejam como é interessante começar nesse mundo de engenharia reversa.
Dúvidas ou sugestões? Deixem seus comentários!