Concrete Logo
Hamburger button

Breve panorama sobre a segurança para aplicativos nativos Android

  • Blog
  • 1 de Fevereiro de 2016

É 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 são 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 de posts que começamos hoje, 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.

Android Security

De forma geral, vemos duas grandes áreas de ataque nos aplicativos nativos Android:

1 – Ataques à implementação

Descompilando Android
2 – Ataques à comunicação dos aplicativos com os servidores

Man in the middle

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 e 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 post 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:

  1. Salvamos nosso código em um arquivo “.java”
  2. 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:

    [code language=”java”]
    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 }
    [/code]

  3. Este ainda não é o formato final que o Android entende! O formato final é bem parecido, porém diferente. 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:

    [code]
    .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
    [/code]

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 outrasque 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 e 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:

Java bin folder

  • javap: ferramenta essencial para desconstruir código em Java. Trata-se do “disassembler” do Java. O resultado dele é o que eu utilizei de exemplo no passo 2 do processo de compilação do Android.
  • javah: usado no desenvolvimento de código nativo integrado com código C. Essa integração exige alguns protocolos como o método nativo precisa seguir uma certa nomenclatura. Essa ferramenta facilita este processo.
  • jarsigner: usada para assinar um pacote java. Dado um usuário com senha ele gera um pacote fechado que só deveria ser aberto para quem tem o usuário e senha.
  • keytool: usada nas operações criptográficas de chave pública. Ela pode gerar chaves público/privadas e também é responsável por gerenciar certificados autorizados.

Todas estas ferramentas são usadas pelas IDEs para o desenvolvimento Android. E, como já falamos, são ferramentas que já temos em nossa instalação Java. Na verdade, temos muitas outras. É só listar os executáveis da pasta “bin” dentro da raíz da sua JDK (Java Development Kit). Muitas delas são específicas para o desenvolvimento Java e principalmente Java backend.

O mesmo acontece com a instalação padrão do Android. Para começar temos diversas pastas cheias de executáveis. É preciso entender qual o papel de cada uma delas. Numa instalação atual da SDK (Jan/2016) temos:

  • build-tools: encontramos ferramentas para compilar e gerar os pacotes Android
  • tools: ferramentas para gerenciamento de emuladores, gerenciamento da própria SDK e assistentes para tratamento de assets (ProGuard, 9patch, etc)

Fora outras pastas com o código do Android de cada versão, repositórios de suporte, binários que são do Google exclusivamente (como Play Services) e etc.

Vejamos o que temos dentro da versão 23 de build-tools:

android_build_tools

  • 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: transforma 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!