# Análise estática no Android – parte 1: abrindo o APK

> Como descompactar um APK em formatos diferentes, o que cada um revela e o que muda quando o app é feito em React Native.

- Autor: Marcelo Silva
- Publicado em: 2026-10-09
- Tema: Segurança
- Série: Segurança Mobile, parte 4 de 11 (https://marcelxsilva.dev/series/seguranca-mobile/)
- URL: https://marcelxsilva.dev/artigos/abrindo-um-apk/

Um APK é, na prática, um arquivo ZIP com uma extensão diferente e algumas regras a mais. Isso significa que boa parte do trabalho de análise estática, olhar o app sem executá-lo, começa simplesmente descompactando esse arquivo e escolhendo a ferramenta certa para cada pergunta.

Este artigo cobre a primeira fase prática da série: como abrir um APK em formatos diferentes, por que cada um serve para um tipo de busca, e o que muda quando o app é feito em React Native.

## Três formatos, três perguntas

Um APK sozinho não é muito legível: o `AndroidManifest.xml` vem em binário, o código Kotlin/Java vem em bytecode DEX, e os recursos (`strings.xml`, layouts) também vêm compilados. Por isso o primeiro passo de qualquer análise é converter o pacote em pelo menos três formas diferentes, cada uma útil para um tipo de busca:

| Formato | Como obter | Para que serve |
|---|---|---|
| Arquivos crus | `unzip` | Ver o `assets/`, bibliotecas nativas (`.so`), bundle JavaScript |
| Java decompilado | [jadx](https://github.com/skylot/jadx) | Ler código Kotlin/Java quase original, `BuildConfig`, lógica de negócio |
| Manifest e recursos em XML | [apktool](https://apktool.org/) | `AndroidManifest.xml` e `strings.xml` legíveis, smali |

O `jadx` converte bytecode DEX de volta para algo muito próximo do código-fonte original, porque esse bytecode preserva nomes de classes, métodos e variáveis (ao contrário de um binário nativo compilado, que perde quase tudo isso). É esse detalhe que torna a engenharia reversa de um APK relativamente acessível, e por isso ferramentas de ofuscação como o [R8](https://developer.android.com/build/shrink-code) existem: elas embaralham esses nomes antes de publicar o app.

O `apktool` já olha para outro lado do pacote: ele desmonta o DEX em smali (uma representação textual do bytecode, mais próxima de assembly do que de Java) e, principalmente, decodifica o manifesto e os recursos compilados de volta para XML legível. É ali que ficam as permissões, os componentes exportados e a configuração de rede do app, assunto do próximo artigo.

Na prática, os três formatos saem de três comandos, rodados a partir do APK (o `jadx` e o `apktool` se instalam com um gerenciador de pacotes comum, como o Homebrew no macOS: `brew install jadx apktool`):

```bash
mkdir -p pentest && cd pentest

unzip -q -o example.apk -d apk-out
jadx -q -d jadx-out example.apk
apktool d -f -q example.apk -o apktool-out
```

| Pasta | O que tem | Para que serve |
|---|---|---|
| `apk-out/` | Arquivos crus (dex, assets, libs nativas) | Bundle JS, arquivos soltos em `assets/` |
| `jadx-out/` | Java decompilado | `BuildConfig`, código nativo do app |
| `apktool-out/` | Manifest e `res/` em XML legível | Manifest, `strings.xml`, configuração de rede |

O jadx costuma ser o mais lento dos três (minutos, não segundos, em apps grandes); nada impede seguir explorando as outras duas pastas enquanto ele termina.

## Se o pacote for um AAB

Hoje a Google Play exige o envio no formato [Android App Bundle (.aab)](https://developer.android.com/guide/app-bundle), não mais o APK direto. O AAB não é instalável sozinho: ele contém os recursos separados por configuração (idioma, densidade de tela, arquitetura de CPU) e é a própria Play Store quem gera, na hora da instalação, o APK otimizado para cada aparelho.

Para analisar um AAB como analisaria um APK, a ferramenta oficial do Google, o [`bundletool`](https://developer.android.com/tools/bundletool), gera um APK universal, que contém todas as configurações juntas:

```bash
java -jar bundletool.jar build-apks --bundle=example.aab --output=example.apks --mode=universal --overwrite
unzip -p example.apks universal.apk > universal.apk
```

A partir desse `universal.apk`, bastam as mesmas técnicas de descompactação, jadx e apktool descritas acima.

## O caso React Native: o bundle JavaScript

Apps feitos com [React Native](https://reactnative.dev/) acrescentam uma camada ao que o jadx e o apktool mostram. A lógica de produto não fica (só) em Kotlin: ela fica num bundle JavaScript, um único arquivo que concentra todo o código JS da aplicação, normalmente em `assets/index.android.bundle` dentro do APK.

Esse bundle existe em duas formas possíveis, dependendo de como o projeto foi configurado:

- **Texto puro (minificado)**: ainda é JavaScript, só que compactado numa linha. Dá para ler direto ou formatar com uma ferramenta como o [Prettier](https://prettier.io/).
- **Bytecode Hermes**: desde que o [Hermes](https://hermesengine.dev/), o motor JavaScript criado pela Meta para React Native, se tornou o padrão do framework (a partir da versão 0.70), o bundle passou a ser compilado para um bytecode próprio, não mais texto. Ferramentas como o [hermes-dec](https://github.com/P1sec/hermes-dec) fazem o caminho inverso: extraem a tabela de strings embutida no bytecode e, com limitações, produzem um pseudo-JavaScript aproximado do código original.

Dá para confirmar qual dos dois formatos um bundle usa com o utilitário `file`, do Unix, e localizar o próprio bundle dentro do pacote descompactado:

```bash
find apk-out -name "*.bundle" -o -name "*.jsbundle" -o -name "*.hbc"
file apk-out/assets/index.android.bundle
find apk-out -iname "libhermes*"
```

| Resposta do `file` | O que fazer |
|---|---|
| `Hermes JavaScript bytecode, version <n>` | Extrair as strings do bytecode (próximo passo) |
| `ASCII text` / `UTF-8 text` | É JS minificado. Dá para ler direto ou formatar com o Prettier |

A presença de uma biblioteca `libhermes.so` no APK também é um indício forte de que o app usa Hermes, mesmo antes de olhar o bundle.

Com um bundle em bytecode Hermes, o primeiro passo é extrair a tabela de strings embutida, a fonte mais confiável de texto legível dentro do binário (`uv tool install hermes-dec` instala o pacote):

```bash
hbc-file-parser apk-out/assets/index.android.bundle > hbc-strings.txt
strings -n 5 apk-out/assets/index.android.bundle > bundle.txt
```

Para ir além da tabela de strings e tentar reconstruir a lógica em si, o próprio `hermes-dec` traz um desassemblador e um decompilador (pseudo-JS, que não compila: ele não reconstrói laços nem `if`):

```bash
hbc-disassembler apk-out/assets/index.android.bundle saida.hasm
hbc-decompiler apk-out/assets/index.android.bundle saida.js
```

O `strings` funciona em qualquer um dos dois formatos (bytecode ou texto) e é mais rápido, mas corta trechos de texto muito longos; o `hbc-strings.txt`, gerado pelo `hermes-dec`, é a leitura completa.

Dentro desse texto, o que se procura é sempre parecido: endereços de servidor que não deveriam estar na versão pública (ambientes de desenvolvimento ou homologação), nomes de variáveis de segredo, tokens no formato [JWT](https://jwt.io/), chaves de provedores de nuvem e qualquer lógica de validação que um atacante possa decifrar lendo o próprio código:

```bash
grep -oE 'https?://[A-Za-z0-9._-]+' bundle.txt | sort -u
grep -oE '[^ |]*(dev|develop|hml|homolog|staging|test)[^ |]*' hbc-strings.txt | sort -u
grep -oE '[^ |]*(secret|SECRET|password|token|TOKEN)[^ |]*' hbc-strings.txt | sort -u
grep -oE 'eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*' hbc-strings.txt | sort -u
grep -oE "AIza[0-9A-Za-z_-]{35}" hbc-strings.txt
grep -oE "AKIA[0-9A-Z]{16}" hbc-strings.txt
```

Na ordem: todos os hosts chamados pelo app; endereços de ambientes que não são de produção; nomes de variável que sugerem segredo; tokens JWT; e dois padrões conhecidos de chave (API do Google e credencial da AWS).

Encontrar uma dessas strings é só metade do trabalho: o passo seguinte, quando se tem acesso ao repositório do app, é localizar a origem exata no código-fonte com uma busca pelo trecho encontrado (`git grep -n -F '<trecho>' -- src`), para entender se aquilo é intencional (um valor de exemplo) ou um vazamento real.

## Limites das ferramentas

Nem toda ferramenta de decompilação acompanha a evolução do Hermes. O [`hbctool`](https://github.com/bongtrop/hbctool), por exemplo, só sabe ler as versões 59 e 62 do bytecode, o que o deixa defasado em apps mais novos. Vale sempre conferir, antes de montar um fluxo de análise em cima de uma ferramenta de terceiros, se ela cobre a versão do Hermes usada pelo app analisado.

No próximo artigo da série: o que dá para aprender olhando só para as variáveis de ambiente que acabam compiladas dentro do binário, e onde cada plataforma esconde esse dado.
