É comum associar o Android a problemas de segurança. Porém, muitos destes problemas não são por consequências diretas do Android. Um bug no kernel do Linux, um bug na SDK do Android ou um bug na implementação do Android de quem o customiza são falhas que associamos diretamente à plataforma.
O curioso nisso tudo, no entanto, é que a maioria dos problemas de ataques aos aplicativos é porque os desenvolvedores ajudaram muito os “hackers”. Existem diversos ataques “preguiçosos” que simplesmente focam em implementações problemáticas feitas pelos desenvolvedores. Queremos, nesta série, ilustrar o panorama da segurança no Android (de forma breve) e mostrar como detalhes da implementação de toda arquitetura (do servidor ao cliente) são os verdadeiros responsáveis por problemas de segurança.
De forma geral, vemos duas grandes áreas de ataque nos aplicativos nativos Android:
1. Ataques à implementação
2. Ataques à comunicação dos aplicativos com os servidores
O primeiro ponto se refere aos ataques locais, ou seja, tudo aquilo que um hacker consegue fazer tendo em mãos o binário da sua aplicação: procurar valores fixos no código, injetar código para imprimir mais informações que as necessárias, inverter lógicas simples, alterar configurações globais etc.
Já o segundo caso trata de manipular como o aplicativo se comunica com os servidores: técnicas de man in the middle, ou seja, de escutar a comunicação e ver o conteúdo das requisições e respostas. Muitas vezes os atacantes percebem falhas nos protocolos de backend monitorando como o aplicativo se comunica com o servidor. Estes casos podem gerar ataques de negação de serviço, roubo de sessão, falsificação de pagamentos, alteração de dados cadastrados e muito mais.
Todo esse trabalho parece altamente especializado e executado apenas por desenvolvedores altamente especializados que escrevem códigos obscuros de garagens mantidas pelo tráfico mundial. Um ponto importante desta série é desmistificar esse ponto de vista. Vamos mostrar como existe muita documentação pública sobre o assunto e que como muitas ferramentas simples automatizaram a parte difícil. Hoje em dia não é necessário entender de criptografia profundamente para analisar um aplicativo qualquer. E pior: nem sequer é necessário um aparelho desbloqueado (com acesso de root). A única coisa que é preciso é estudar com calma as ferramentas que já nos estão disponíveis, entender o ambiente de desenvolvimento normal de Android e só aí procurar pelas ferramentas que nos ajudam a distorcer as regras do jogo.
Neste artigo introdutório vamos falar um pouco do ambiente de desenvolvimento Java e Android e conhecer as ferramentas que nem sempre ouvimos falar tanto.
Para desenvolver uma aplicação Android, basta abrir o Android Studio, configurar o Gradle e apertar o botão “executar” depois de ter escrito algum código. Porém, olhemos com mais calma o processo do texto até a execução na tela do usuário:
- Salvamos nosso código em um arquivo “.java”
- Chamamos o compilador “javac” que irá ler o arquivo e o transformar em uma representação binária intermediária (a famosa linguagem da máquina virtual). Aqui já não temos quase nada do arquivo Java original. Mostrando um exemplo do que se trata essa linguagem binária, seria algo como:
public class SimpleProgram extends java.lang.Object{ public SimpleProgram(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
- Este ainda não é o formato final que o Android entende (mas é bem parecido). O Android não executa uma máquina virtual Java. Ele executa uma máquina virtual chamada Dalvik ou, recentemente, ART. Para isso o gradle chama a ferramenta dx que irá gerar a linguagem intermediária que as máquinas virtuais do Android entendem. Fica algo parecido com:
.class public Lcom/hello/Main; .super Landroid/app/Activity; .source "Main.java" # direct methods .method public constructor <init>()V .locals 0 .prologue .line 6 invoke-direct {p0}, Landroid/app/Activity;-><init>()V return-void .end method # virtual methods .method public onCreate(Landroid/os/Bundle;)V .locals 1 .parameter "savedInstanceState" .prologue .line 10 invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V .line 11 const/high16 v0, 0x7f03 invoke-virtual {p0, v0}, Lcom/hello/Main;->setContentView(I)V .line 12 return-void .end method
Fica claro que o que a IDE (Android Studio) faz é automatizar o uso destas ferramentas: primeiro o “javac” e em seguida o “dx”. Isso apenas olhando para o código das classes! Não podemos esquecer que o Android ainda possui outras ferramentas para gerar nossos recursos e assinar o pacote final, entre outras que podem intermediar este processo todo.
Essas ferramentas existem com ou sem IDE. Poderíamos fazer tudo na mão pela linha de comando se quiséssemos, porém seria muito custoso e anti-produtivo. Nossa produtividade com o Android Studio é altamente superior e ganhamos diversas outras vantagens, como transferência e instalação para os emuladores ou dispositivos, monitor de consumo de memória, interface para fazer a depuração dos aplicativos etc.
Porém, é aí que mora o risco: se nos acostumamos com tanto conforto, esquecemos de procurar mais a fundo o funcionamento das coisas. Quer um exemplo? Quais são as ferramentas executáveis que vêm junto com o Java? Melhor dizendo, além do “java” e do “javac” o que ganhamos por padrão em todas as instalações do Java?
Vejamos algumas ferramentas que todo desenvolvedor Android já possui:
- aapt: Android Asset Packaging Tool. Responsável por criar, visualizar e atualizar pacotes binários, além de compilar recursos para formato binário (a famosa classe R e nosso Manifesto que é comprimido e transformado em binário);
- dx: Ttransforma arquivos .class em um grande arquivo “classes.dex”;
- zipalign: Faz o alinhamento binário de todos os arquivos. Basicamente ajuda a comprimir o conteúdo e deixá-lo mais eficiente em termos de alocação de memória;
- dexdump: Equivalente ao Javap para o Android. É o descompilador de “.dex”.
- adb: Famoso Android Debug Bridge. Nos ajuda a “falar” com um emulador ou device.
Assim, por padrão, sem recorrer para ferramentas obscuras vendidas a troco de órgãos, vemos que temos um belo arsenal para analisar código. Tanto o Java, quanto o Android possuem um descompilador por padrão na instalação. Claro que eles não geram arquivos “.java” diretamente, mas veremos que não é lá tão complicado ler o código gerado.
Na continuação desta série veremos mais a fundo como nos utilizar destas ferramentas para começar a distorcer as regras do jogo. Como já foi dito, queremos desmistificar a manipulação de aplicativos nativos.
Tem alguma dúvida ou algum comentário a fazer? Aproveite os campos abaixo.
Até a próxima!