Concrete Logo
Hamburger button

Breve panorama sobre a segurança para apps nativos Android – parte 2

  • Blog
  • 17 de Maio de 2016

Na parte 1 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 app?

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 seja 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 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:

APK desempacotado

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

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

Agooooooooora 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”:

Grandes poderes, grandes responsabilidades

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 ESTAS FERRAMENTAS PARA 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!

E se você é desenvolvedor Android e quer fazer parte do nosso time, acesse aqui. Até a próxima!