Sitelet https://github.com/script-or-script/FlutterTap/commit/ec055abe9ccd1c87dfc9755a1dac33de202f76ba
Skip to content

Commit ec055ab

Browse files
harden crash guard and ELF reads; finish docs, README and licensing pass
Clears the review findings that were deliberately deferred from the previous commit, plus the remaining stale documentation. Crash guard (mem_scan.cpp): - Install the handler with SA_ONSTACK, matching what ART installs its own SIGSEGV handler with. ART uses guard-page faults for implicit stack-overflow checks, so on a real stack overflow the handler has to run on the alternate stack rather than the exhausted one. Ignored by the kernel where no sigaltstack exists, so it costs nothing elsewhere. - invokeOld() had two paths that returned without doing anything: SA_SIGINFO set with a null sa_sigaction, and SIG_IGN. Returning from a SIGSEGV handler without resolving the fault re-executes the faulting instruction, so those hung the app at 100% CPU instead of crashing it. All paths now either call a real handler or restore the previous disposition and let the instruction re-fault. Restoring beats the old signal()+raise(): raise() generates a fresh signal, so the tombstone pointed at our handler frame instead of the real faulting instruction -- an unrelated app crash could look like it came from FlutterTap. elf_utils.cpp: readMem() went through a bare memcpy while everything else that reads a heuristically-derived address uses safe_read. The addresses come from e_phoff/e_phnum of a library we did not write, so a corrupt ELF could point outside the mapping -- and the installed SIGSEGV guard cannot catch it, because it only diverts faults while safe_read has armed it. Both call sites now fail cleanly instead of taking the host app down. hooks.cpp: log lines carry a "hooks:" prefix, matching elf:/resolver:/ mem_scan:/companion: everywhere else. It was the only file without one. scripts/build_module_zip.sh: convert paths with cygpath before handing them to the NDK tools. They are native Windows binaries and cannot read MSYS paths, and MSYS2_ARG_CONV_EXCL in the caller's environment silently disables the automatic conversion -- which made llvm-strip fail with "No such file or directory" on a valid path, aborting the build. Caught only because the checksum of the installed module did not match the local build. Docs: - README rewritten: what the project is, why Flutter needs a different approach from a normal CA-install, how the resolution works, compatibility table with both validated environments, install guide, manual config.json usage, diagnostics, build instructions and licensing. Replaces the placeholder that still claimed the project was awaiting device validation. - relatorio.html: section 4 said the Alibaba-specific byte pattern was ported while 6.5 said the mnemonic scan made it unnecessary -- 6.5 was right; the language count said 3 where the app ships 7; the project tree omitted action.sh; the Dobby pin was quoted as e9fe7fb when that is the upstream parent of the actual pin 025e9fc; section 8 and 10 covered only the OnePlus 5 and never mentioned the Pixel 8a / Android 17 / SukiSu Ultra run, which is the stronger datapoint; section 5 now points at section 12 for the three items that had to be reimplemented; section 13 listed finished work as pending. - PDF regenerated with --headless=new --no-pdf-header-footer. The old flag was silently ignored, so every page carried a file:///C:/Users/... footer, which is a local-path leak in a document meant to be shared. Verified: 25 pages, no page contains a local path. Re-validated on the Pixel 8a (Android 17, SukiSu Ultra + Zygisk Next 1.4.3 with the ZN Linker active) after these changes -- these touch the crash-guard path, so a build-clean check was not enough. Installed build verified by checksum before rebooting. 120 app processes, SystemUI alive, no new tombstone. Both demo apps hooked; httpbin.org captured at 01:57:11Z and ostorlab.co (HTTP/2 200, decrypted) at 01:59:31Z, matching the taps and the hook log lines. The dobby submodule pin remains the one open blocker for publishing.
1 parent 30f7b67 commit ec055ab

7 files changed

Lines changed: 259 additions & 69 deletions

File tree

‎README.md‎

Lines changed: 137 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,153 @@
11
# FlutterTap
22

3-
Módulo Zygisk (Magisk / KernelSU / SukiSu Ultra / APatch) que redireciona o tráfego de rede de
4-
apps Flutter selecionados para um proxy configurável e contorna a verificação de certificado TLS,
5-
para fins de análise de tráfego. Inclui um app gerenciador (Android) para escolher os apps-alvo e
6-
configurar IP/porta do proxy, sem precisar editar nada manualmente.
3+
Módulo **Zygisk** que redireciona o tráfego de rede de apps **Flutter** selecionados para um proxy
4+
configurável e contorna a verificação de certificado TLS (SSL pinning) do BoringSSL — sem instalar
5+
certificado no aparelho, sem recompilar o app e sem depender de uma sessão do Frida conectada por USB.
76

8-
Validado em dispositivo real capturando tráfego de ponta a ponta, em Android 10 (Magisk +
9-
NeoZygisk) e Android 17 (SukiSu Ultra + Zygisk Next, inclusive com o **Zygisk Next Linker**
10-
ativo). Capturas de tela em [`docs/screenshots/`](docs/screenshots/).
7+
Acompanha um **app gerenciador** (Jetpack Compose) para escolher os apps-alvo e o IP/porta do proxy
8+
direto no celular.
119

12-
O README completo (tutorial de instalação passo a passo, capturas comentadas) ainda será escrito.
10+
<p align="center">
11+
<img src="docs/screenshots/pixel8a-android17/07_manager_full_view.png" width="260" alt="App gerenciador do FlutterTap">
12+
<img src="docs/screenshots/pixel8a-android17/06_apps_selected.png" width="260" alt="Seleção de apps-alvo">
13+
<img src="docs/screenshots/pixel8a-android17/04_module_enabled_action.png" width="260" alt="Módulo ativo na lista do gerenciador de root">
14+
</p>
1315

14-
## Estrutura
16+
## Por que existe
1517

16-
- `module/` — módulo nativo (C++/Zygisk)
17-
- `manager-app/` — app gerenciador (Kotlin/Jetpack Compose)
18-
- `scripts/build_module_zip.sh` — empacota o `.zip` flashável
19-
- `docs/` — documentação técnica e relatório de desenvolvimento
18+
Apps feitos em Flutter não usam a pilha de TLS do Android: o engine embute o **BoringSSL** e mantém a
19+
própria cadeia de confiança. Na prática, isso significa que instalar o certificado CA do Burp no sistema
20+
**não intercepta nada** — o app ignora o repositório de certificados do Android por completo. E como o
21+
Flutter também não respeita o proxy configurado no Wi-Fi, o tráfego simplesmente sai pela rede sem passar
22+
pelo seu interceptador.
2023

21-
Veja [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) e [`docs/BUILD.md`](docs/BUILD.md) para detalhes.
24+
A técnica para contornar isso é conhecida, mas normalmente exige rodar um script Frida com o
25+
`frida-server` ativo e o cabo conectado, a cada sessão. O FlutterTap transforma essa mesma abordagem em um
26+
módulo persistente: instala uma vez, escolhe os apps, e funciona sozinho a cada boot.
27+
28+
## Como funciona
29+
30+
Nenhuma das funções envolvidas é exportada, então elas precisam ser localizadas em tempo de execução, a
31+
cada processo:
32+
33+
1. O módulo é carregado em cada processo de app via Zygisk e verifica se aquele pacote está na lista de
34+
alvos — se não estiver, se descarrega imediatamente (zero footprint em apps não selecionados).
35+
2. Numa thread de monitoramento, espera o `libflutter.so` ser carregado e faz o parsing dos segmentos ELF.
36+
3. Procura as strings `"ssl_client"` e `"Socket_CreateConnect"` na memória e, a partir delas, resolve os
37+
endereços reais de `verify_cert_chain` e `GetSockAddr` **desmontando o código com Capstone** — a mesma
38+
biblioteca que o Frida usa por baixo.
39+
4. Instala três hooks com Dobby: captura o `sockaddr` de destino, reescreve IP/porta para o proxy, e força
40+
o resultado "certificado válido".
41+
42+
Detalhes de implementação, incluindo as decisões que divergem da abordagem original e por quê, estão em
43+
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
44+
45+
## Compatibilidade
46+
47+
| Item | Suporte |
48+
|---|---|
49+
| Android | 10 (API 29) a 17 (API 37) |
50+
| Arquiteturas | `arm64-v8a`, `x86_64` |
51+
| Root | Magisk, KernelSU, SukiSu Ultra, APatch |
52+
| Zygisk | Zygisk nativo do Magisk, Zygisk Next (**inclusive com o Zygisk Next Linker ativo**) ou NeoZygisk |
53+
54+
Validado em hardware em dois ambientes deliberadamente distintos:
55+
56+
- **OnePlus 5** — Android 10, Kitsune Magisk v27.2 + NeoZygisk 2.3
57+
- **Pixel 8a** — Android 17, SukiSu Ultra + Zygisk Next 1.4.3, com o **Zygisk Next Linker** ligado
58+
59+
> **Zygisk é obrigatório.** Root sozinho não basta: todo o mecanismo vive nos callbacks do Zygisk. No
60+
> Magisk, ative a opção "Zygisk" nas configurações. No KernelSU/SukiSu Ultra, instale o Zygisk Next ou o
61+
> NeoZygisk. O APatch já traz uma implementação compatível.
62+
>
63+
> A compatibilidade com o **Zygisk Next Linker** exigiu correções específicas — se você mantém um módulo
64+
> Zygisk e ele quebra com esse recurso, a seção 12 do relatório documenta as três armadilhas encontradas
65+
> (e uma pista falsa que custou tempo), de forma reaproveitável.
66+
67+
## Instalação
68+
69+
1. **Instale o módulo**: baixe o `FlutterTap-<versão>.zip` e instale pelo app do seu gerenciador de root
70+
(Magisk / KernelSU / SukiSu Ultra / APatch). Reinicie o aparelho.
71+
2. **Instale o app gerenciador** (`FlutterTap.apk`) e conceda root na primeira abertura.
72+
3. **Configure**: informe o IP e a porta do seu proxy (o IP da máquina rodando o Burp, na mesma rede
73+
Wi-Fi) e marque os apps que quer interceptar.
74+
4. **Force a parada do app-alvo e abra de novo.** O hook só entra em processos novos — reabrir sem forçar
75+
a parada não basta.
76+
77+
> **Root é obrigatório também para o app gerenciador**, não só para salvar: a configuração fica em
78+
> `/data/adb/`, então sem root ele não consegue nem ler o que já está gravado.
79+
80+
No Burp, use um listener em **modo invisível** (*invisible proxying*), já que o app envia requisições
81+
comuns, não requisições de proxy.
82+
83+
Capturas passo a passo da instalação, em um Pixel 8a com Android 17, estão em
84+
[`docs/screenshots/pixel8a-android17/`](docs/screenshots/pixel8a-android17/).
85+
86+
### Configuração sem o app gerenciador
87+
88+
O app é apenas uma interface para um arquivo JSON. Para automatizar (provisionamento, CI, aparelho sem
89+
tela), escreva direto em `/data/adb/fluttertap/config.json`:
90+
91+
```json
92+
{
93+
"enabled": true,
94+
"proxy_ip": "192.168.1.10",
95+
"proxy_port": 8080,
96+
"target_packages": ["com.exemplo.app"]
97+
}
98+
```
99+
100+
O arquivo é relido do disco a cada processo criado, então basta `am force-stop` no app-alvo e reabrir —
101+
sem reiniciar o aparelho. A correspondência é feita pelo nome do processo, e listar `com.exemplo.app`
102+
também captura subprocessos nomeados (`com.exemplo.app:remote`). A seção 11.3 do relatório detalha o
103+
esquema, as receitas de edição via `adb` e os modos de falha silenciosa.
104+
105+
## Diagnóstico
106+
107+
```sh
108+
adb logcat -s FlutterTap:V
109+
```
110+
111+
O esperado, para um app-alvo, é a sequência: `selected for hooking` → `libflutter.so loaded at ...` →
112+
`verify_cert_chain=0x... GetSockAddr=0x...` → `hooks: installed for ... -> proxy IP:PORTA`, e depois
113+
`hooks: overwrite sockaddr` / `hooks: verify_cert_chain bypass` conforme o app faz requisições.
114+
115+
Se **nada** aparecer, quase sempre é uma destas três: o nome do pacote não corresponde ao processo, o
116+
`config.json` está inválido (o módulo cai nos padrões e não intercepta nada), ou o app já estava rodando
117+
quando a configuração mudou.
118+
119+
## Compilando
120+
121+
Requer Android SDK/NDK e **JDK 17 ou 21** (o Gradle 8.11.1 não lê JDK 22+; aponte o `JAVA_HOME` para o JBR
122+
do Android Studio se o seu `java` padrão for mais novo).
123+
124+
```sh
125+
git clone --recurse-submodules <repo>
126+
./scripts/build_module_zip.sh # gera dist/FlutterTap-<versão>.zip
127+
./gradlew :manager-app:assembleDebug
128+
```
129+
130+
Instruções completas em [`docs/BUILD.md`](docs/BUILD.md).
131+
132+
## Documentação
133+
134+
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — como cada peça funciona e por que foi feita assim
135+
- [`docs/BUILD.md`](docs/BUILD.md) — compilação e empacotamento
136+
- [`docs/FlutterTap-Relatorio-Desenvolvimento.pdf`](docs/FlutterTap-Relatorio-Desenvolvimento.pdf) —
137+
relatório completo de desenvolvimento, com a validação em dispositivo real e o guia de compatibilidade
138+
com o Zygisk Next Linker
22139

23140
## Aviso
24141

25-
Uso destinado à análise de tráfego autorizada (pentest mobile, engenharia reversa, pesquisa de
26-
segurança) em aplicativos que você tem permissão para testar.
142+
Uso destinado à **análise de tráfego autorizada** — pentest mobile, engenharia reversa e pesquisa de
143+
segurança em aplicativos que você tem permissão para testar. Interceptar tráfego de aplicativos de
144+
terceiros sem autorização é ilegal na maior parte das jurisdições.
27145

28146
## Licença
29147

30-
MIT — veja [`LICENSE`](LICENSE). O módulo nativo linka estaticamente Dobby (Apache-2.0) e
31-
Capstone (BSD-3); os componentes de terceiros e suas licenças estão listados em
32-
[`THIRD_PARTY.md`](THIRD_PARTY.md), e os textos completos acompanham o `.zip` distribuído.
148+
MIT — veja [`LICENSE`](LICENSE). O módulo nativo linka estaticamente Dobby (Apache-2.0) e Capstone
149+
(BSD-3); os componentes de terceiros estão listados em [`THIRD_PARTY.md`](THIRD_PARTY.md), e os textos
150+
completos das licenças acompanham o `.zip` distribuído.
33151

34152
---
35153
Desenvolvido por Eduardo Lopes
-16 KB
Binary file not shown.

0 commit comments

Comments
 (0)