Home Pentesting Android P.4 - Eng. Reversa
Post
Cancel

Pentesting Android P.4 - Eng. Reversa

Pentesting Android

INTRODUÇÃO

Dando continuidade aos artigos sobre pentesting mobile, vamos discorrer sobre engenharia reversa de aplicativos. Existem infinitas formas de fazer engenharia reversa de um aplicativo, nenhuma delas é a mais correta ou definitiva, para cada caso, uma das formas funcionará melhor tendo como princípio a necessidade.

A engenharia reversa é uma fase importante em um teste, não só pela possibilidade de ver o código-fonte da aplicação, mas pela possibilidade de alterar seu código e conseguir efetuar o bypass de proteções. Além de fornecer entendimento de como e onde a aplicação armazena dados, como é sua comunicação com o back-end e vazar possíveis endereços como URLs e IPs.

ANÁLISE ESTÁTICA x DINÂMICA

Existem basicamente duas frentes no que diz respeito à engenharia reversa de aplicativos, a análise estática e a análise dinâmica.

Na análise estática, a aplicação é analisada sem a necessidade de estar em execução, como um code-review analisando o manifest, as bibliotecas e a descompilação dos arquivos .dex.

Na análise dinâmica, a aplicação é analisada durante sua execução, medindo e enumerando seus comportamentos e fluxos. São utilizadas técnicas de debugging, trace de chamadas de API e syscalls, análise de variáveis em tempo de execução, entre outros.

É importante salientar, que qualquer uma das técnicas usadas individualmente se torna incompleta, uma vez que são complementares.

CÓDIGO SMALI

O código smali é o mais baixo nível de um aplicativo Android. Quando um aplicativo é desenvolvido em Java, uma linguagem de alto nível, este código precisa ser compilado de um apk de forma que a máquina virtual de Dalvik consiga o interpretar.

Este processo de compilação gera os códigos em baixo nível para ser interpretados pela máquina. A este código em baixo nível chamamos de smali.

O código abaixo, mostra um exemplo de smali que imprime um “Hello World!”.

class public h41stur;
.super Ljava/lang/Object;

.method public static main([Ljava/lang/String;)V
    .registers 2
    sget-object v0,         Ljava/lang/System;->out:Ljava/io/PrintStream;
    const-string            v1, "Hello World!"
    invoke-virtual{v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
    return-void
.end method

Para entendermos o processo que chegou a este código smali, podemos criar o script em Java e compilá-lo para se tornar um arquivo .dex que possa ser executado por uma máquina de Dalvik.

O código abaixo, mostra a classe Java correspondente ao “Hello World”.

1
2
3
4
5
public class h41stur{
    public static void main(String[] args){
        System.out.println("Hello World!");
    }
}

Para que este código seja interpretado pelo binário java precisamos compilá-lo com o javac.

Classe compilada

Como podemos ver, após compilar a classe, com o javac, um arquivo “h41stur.class” foi criado, a partir disto, foi possível executar o script com o java imprimindo a mensagem.

Neste ponto, a classe java está compilada e executável pelo java em uma máquina Linux, porém, o sistema operacional Android não interpreta Java, conforme já vimos, e sim smali. Para que possamos gerar o código em baixo nível para o Android interpretar, podemos utilizar a ferramenta d8 que faz parte do build-tools do Android Studio.

Esta ferramenta compila o bytecode Java para o bytecode DEX que pode ser executado pelo Android.

Compilando Java em DEX

Ao compilar o arquivo .class com o d8, temos como resultado o arquivo “classes.dex” que contém o bytecode interpretável pela máquina de Dalvik.

Para comprovarmos a eficácia, podemos transferir o arquivo .dex gerado para um dispositivo através do comando adb push e executá-lo dentro do Android com o comando dalvikvm -cp classes.dex h41stur, onde:

  • dalvikvm: é a máquina virtual Dalvik, responsável por interpretar os arquivos .dex;
  • -cp classes.dex: é o “class path” ou o caminho onde está armazenada a classe;
  • h41stur: é o nome da classe criada para ser interpretada.

A imagem abaixo mostra o resultado da execução.

Execução do DEX

TÉCNICAS DE ENGENHARIA REVERSA

A partir deste momento, podemos começar a “desmontar” um aplicativo de várias formas. Como teste, vamos utilizar o aplicativo EVABS (Extremaly Vulnerable Android Labs).

Unzip e Backsmali

Todo aplicativo nada mais é que um pacote compactado de arquivos e bibliotecas. Sendo assim, é possível descompactá-lo com o próprio unzip.

Descompactando desta forma, não se tem o aplicativo de forma legível para análise, mas é possível utilizar outras ferramentas para auxiliar em sua análise.

Descompactando o APK

Após descompactar o APK, podemos ver que alguns arquivos e diretórios são exibidos, conforme mostrado na imagem abaixo.

APK descompactado

Estes diretórios e arquivos contém todos os recursos do aplicativo, incluindo imagens, códigos, bibliotecas, incluindo o arquivo `classes.dex”, porém todos os arquivos estão em bytecode e ilegíveis para nós.

Para fazermos o reversing do arquivo classes.dex, podemos utilizar a ferramenta backsmali, bastando passar argumento de disassembly e o path do arquivo .dex e um output conforme mostrado abaixo.

Disassembly do .dex

Conforme podemos ver, o diretório “reversing” foi criado, pois foi o nome passado para o output.

Se caminharmos para o diretório raiz do aplicativo, podemos ver todos os scripts smali, conforme mostrado abaixo.

Arquivos smali

E ao abrirmos qualquer um destes arquivos, teremos o código smali legível para interpretação e edição.

Código smali

Muitas vezes, somente de observar o código smali, é possível determinar comportamentos importantes sobre a aplicação, credenciais hardcoded e diversas outras informações.

Como, por exemplo, no arquivo DBLeak.smali na linha 63, podemos ver a criação de uma tabela de usuários, e logo abaixo o insert de usuários e senhas, conforme mostrado abaixo.

Credenciais hardcoded

Para melhor entendimento sobre os operadores Dalvik, segue uma excelente referência.

Após a análise do aplicativo, caso alguma alteração tenha sido feita em um arquivo smali, é possível compilá-los novamente para empacotar em um APK novamente, para isso, basta apagar o arquivo classes.dex original, e utilizar a ferramenta smali para compilar o diretório gerado pelo backsmali.

Processo de recompilação dos arquivos smali

Após a compilação dos arquivos smali, é possível compactar os arquivos e diretórios novamente em formato de APK com a ferramenta zip conforme mostrado abaixo.

Compactando o aplicativo

E um novo aplicativo foi gerado, conforme mostrado abaixo.

Aplicativo gerado

É importante ressaltar, que o aplicativo recompactado ainda não está pronto para ser instalado, pois como vimos em posts anteriores, um aplicativo precisa estar assinado para que o Android o reconheça como válido, mais a frente iremos explorar a assinatura de um APK.

Apktool

Como já vimos a descompilação de um aplicativo de forma manual, podemos utilizar uma ferramenta automatizada que agiliza o processo.

O apktool é uma junção de várias ferramentas em um set que executa comandos de forma sequencial para descompactar e descompilar e/ou compilar e compactar um aplicativo.

Apktool

Conforme podemos ver, o apktool já conta até mesmo com o smali e backsmali em seu kit.

A imagem abaixo mostra o processo de descompilação de um aplicativo.

Decompilando o APK

E assim como da forma manual, todos os arquivos smali estão disponíveis para leitura e edição, conforme mostrado abaixo.

Arquivos smali

O processo de recompilação é tão simples quanto o anterior, bastando utilizar o parâmetro b (build) para o apktool.

Recompilando o APK

Assim como da forma manual, é importante ressaltar, que o aplicativo recompactado ainda não está pronto para ser instalado, pois como vimos em posts anteriores, um aplicativo precisa estar assinado para que o Android o reconheça como válido, mais a frente iremos explorar a assinatura de um APK.

Assinando um APK

Até o momento vimos uma forma de descompactar e descompilar um aplicativo para ser possível obter seu código smali e alterá-lo, caso possível, assim como recompilá-lo e compactá-lo para gerar um novo APK adulterado.

Porém, este processo remove a assinatura original do aplicativo, e como vimos em posts anteriores, o Android só reconhece um aplicativo como válido, caso este esteja assinado com um certificado.

Para fins de teste de um aplicativo, é possível assinar o APK adulterado com um certificado autoassinado. É importante salientar, que um aplicativo assinado desta forma, não é reconhecido pela loja oficial, portanto, não é possível subir um APK autoassinado na loja oficial

O processo para tal, envolve uma série de passos e ferramentas. Não existe uma única forma de assinar um aplicativo, portanto uma delas será explorada no decorrer do post.

KeyStore

O primeiro passo no processo de assinatura é a criação de uma KeyStore. O KeyStore, como o próprio nome sugere, é um arquivo que serve como um “banco de dados” que contém informações criptografadas e informações de como criptografar. Em resumo é uma área de armazenamento de chaves.

O processo de criação do KeyStore já envolve também a criação de um par de chaves. A própria suite ADB contém a ferramenta keytool que tem como função a administração de certificados. Abaixo o comando para criação de uma KeyStore:

1
keytool -genkey -v -keystore h41stur.keystore -alias h41stur -keyalg RSA -keysize 2048 -validity 10000

Onde:

  • -genkey: é o argumento para gerar um par de chaves;
  • -v: argumento para aumentar a verbosidade;
  • -keystore: é o argumento para indicar o nome da KeyStore a ser gerada;
  • -alias: é o argumento para dar um alias para o certificado;
  • -keyalg: é o argumento para indicar qual algoritmo de criptografia será usado para o certificado;
  • -keysize: é o argumento para passar o tamanho da chave em bits;
  • -validity: é o argumento para informar a validade em dias do certificado.

Após executar o comando, a ferramenta vai pedir uma senha para o certificado e mais algumas informações (que não tem tanta importância num certificado autoassinado para testes), e gerará o KeyStore com o nome indicado, conforme mostrado abaixo.

Gerando a KeyStore

É possível listar as chaves armazenadas na KeyStore com o argumento -list conforme mostrado abaixo.

Listando as chaves

Caso seja necessário, também é possível extrair os certificados armazenados na KeyStore, bastando indicá-la juntamente com o alias do certificado, com o comando abaixo.

1
keytool -export -rfc -keystore h41stur.keystore -alias h41stur -file h41stur.cer

Onde:

  • -export: é o argumento para exportar um certificado;
  • -rfc: é o argumento para indicar que a saída será no formato RFC;
  • -keystore: é o argumento para passar o caminho para a KeyStore;
  • -alias: é o argumento para passar o alias do certificado a ser extraído de dentro da KeyStore;
  • -file: é o argumento para informar o arquivo de saída do certificado exportado.

Ao executar o comando, a senha do certificado será solicitada e o arquivo será gerado, conforme mostrado abaixo.

Certificado extraído

Certificado extraído

JarSigner

Com a KeyStore e o par de chaves criados, é possível assinar o aplicativo adulterado com a ferramenta jarsigner existente na build-tools do Android Studio. O comando abaixo mostra o processo de assinatura.

1
jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore h41stur.keystore EVABSv4_rebuild.apk h41stur

Onde:

  • -sigalg: é o argumento para informar o algoritmo utilizado na assinatura;
  • -digestalg: é o argumento para informar o algoritmo utilizado na compilação do certificado;
  • -keystore: é o argumento para informar o caminho da KeyStore;

Estes argumentos devem ser seguidos do caminho do APK a ser assinado e do alias do certificado.

Ao executar o comando, a senha do certificado será solicitada e o aplicativo será assinado com o certificado, conforme mostrado abaixo.

Assinando o APK

É possível verificar a assinatura do APK com o argumento -verify conforme mostrado abaixo.

Verificando assinatura

Após este processo, o aplicativo ainda não está pronto, ainda é necessário fazer o alinhamento do arquivo compactado.

ZipAlign

O zipalign é uma ferramenta que pode ser encontrada na build-tools do Android Studio. De acordo com sua documentação, esta ferramenta certifica que todos os arquivos descompactados no arquivo sejam alinhados em relação ao seu início, permitindo que estes arquivos possam ser acessados via mmap, eliminando a necessidade de copiar esses dados na RAM e reduzindo o uso de memória do seu aplicativo.

O comando abaixo mostra o processo de alinhamento do arquivo.

1
zipalign -v 4 EVABSv4_rebuild.apk EVABSv4_alinhado.apk

Onde:

  • -v: é o argumento para aumentar a verbosidade;
  • 4: é o argumento para informar o alinhamento em bytes, no caso o 4 informa um alinhamento em 32 bits.

Após este processo, o aplicativo adulterado está pronto para ser instalado em um dispositivo físico ou emulado.

Jadx-gui

Nem sempre a engenharia reversa precisa seguir até o nível mais baixo de código e bibliotecas, em uma situação de code review, a conversão para o código-fonte Java se torna suficiente para entendimento do aplicativo.

O jadx-gui é um set de ferramentas de linha de comando e GUI que produzo códigos Java a partir de APKs e Android Dex.

Seu uso é simples, bastando chamar a ferramenta seguida do path do aplicativo a ser descompilado. A imagem abaixo mostra o aplicativo EVABS (Extremaly Vulnerable Android Labs) descompilado.

APK decompilado

A partir desta fase, é possível fazer buscas no aplicativo por termos e funções-chave.

É importante ressaltar, que na própria página da ferramenta, é explícito que sua eficácia não é de 100%, pois alguns pontos do aplicativo podem não ser descompilados.

Dex2jar

O dex2jar faz parte de uma suite de ferramentas que tem por objetivo trabalhar com extensões .dex os convertendo em classes Java.

Especificamente o dex2jar converte um aplicativo ou um código DEX em classes Java compactadas em um único arquivo .jar servindo com uma alternativa na descompilação de um aplicativo, uma vez que o resultado possa não ser 100% satisfatório entre uma ferramenta e outra.

Sua sintaxe é simples, bastando invocar a ferramenta seguida do path do aplicativo, após o processo, um arquivo com o sufixo -dex2jar.jar é gerado, conforme mostrado abaixo.

APK decompilado

Para leitura do arquivo gerado é preciso utilizar uma aplicação que leia as classes Java, nestes artigos, usaremos o jd-gui, uma ferramenta do Java Decompiler project que tem por objetivo desenvolver ferramentas para descompilar e analisar o Java byte-code.

Para efetuar a leitura, basta invocar o jd-gui seguido do path da classe gerada com o dex2jar. A imagem abaixo mostra como a leitura fica visível.

jd-gui

Enjarify

O enjarify assim como o dex2jar, é uma ferramenta para traduzir arquivos .dex e APKs em código Java, o scritp em Python serve como mais uma alternativa para descompilação de aplicativos.

Sua sintaxe também é simples, bastando invocá-lo seguido do path do arquivo .dex ou APK. A imagem abaixo mostra este processo.

APK decompilado

Ao final do processo, o enjarify também gera as classes Javas compactadas em um arquivo .jar, que pode ser aberto com o jd-gui.

Mobile Security Framework (MobSF)

O Mobile Security Framework (MobSF) é uma ferramenta “all-in-one” que faz todos os processos descritos neste post, desde a descompilação a code review. Além de permitir a análise tanto estática quanto dinâmica de um aplicativo.

Assim como as demais ferramentas, pode não trazer 100% de acerto, porém sua interface amigável e detalhada facilita a interpretação.

O demonstrativo abaixo, retirado do próprio GitHub MobSF mostra o processo de engenharia reversa.

MobSF

CONCLUSÃO

Neste artigo, foi possível obter um overview do processo de engenharia reversa e adulteração de um aplicativo Android, foram vistas várias técnicas que podem se complementar, uma vez que nenhuma delas pode ser elencada como a definitiva.
Nos próximos posts, entenderemos o processo de hooking de um aplicativo.

This post is licensed under CC BY 4.0 by the author.