APK → DEX → C → ELF x86-64
O WineDroid é uma camada experimental de compatibilidade que pesquisa como executar código de aplicativos Android no Linux sem iniciar um sistema Android completo, sem máquina virtual e sem contêiner Android.
Important
O WineDroid ainda não é um launcher Android de uso geral. O projeto já
produz e executa ELF nativo a partir de métodos DEX reais, mas grande parte
de java.*, android.*, JNI, Binder e interface gráfica ainda não foi
implementada.
- Objetivo
- Estado atual
- Marco comprovado com o SukiSU
- Como funciona
- Recursos implementados
- Requisitos
- Compilação
- Uso
- Testes
- Estrutura do repositório
- Limitações atuais
- Próximos marcos
- Segurança
- Licença
O objetivo do WineDroid é construir uma ponte nativa entre aplicativos Android e o desktop Linux.
Em vez de iniciar uma imagem completa do Android, o projeto:
- abre e analisa o APK;
- interpreta o manifesto Android Binary XML;
- lê as estruturas DEX e resolve métodos, campos, strings e tipos;
- seleciona métodos Dalvik compatíveis;
- converte esses métodos para código C;
- usa o Clang para gerar um executável ELF64 PIE;
- executa o resultado diretamente pelo kernel Linux;
- encaminha chamadas ainda não implementadas para stubs do runtime.
A meta de longo prazo é substituir progressivamente esses stubs por
implementações nativas de java.*, android.* e serviços do host.
O WineDroid já possui uma base funcional de compilação AOT e ligação recursiva.
| Área | Estado |
|---|---|
| Leitura de ZIP/APK | Implementada |
| Android Binary XML | Implementação parcial funcional |
| Índices e corpos DEX | Implementados |
| Inspeção de classes, métodos e bibliotecas nativas | Implementada |
| Dalvik → C | Implementação experimental funcional |
| C → ELF64 PIE x86-64 | Implementada com Clang |
| Execução direta do ELF no Linux | Implementada |
| Objetos por handles e campos compartilhados | Implementados parcialmente |
| Ligação de múltiplos métodos no mesmo processo | Implementada |
| Expansão recursiva de chamadas internas | Implementada |
| ABI genérica de argumentos Dalvik | Implementada |
packed-switch (0x2b) |
Implementado |
sparse-switch (0x2c) |
Ainda não implementado |
throw (0x27) |
Lowering implementado |
Tabelas try/catch e captura de exceções |
Ainda não implementadas |
| Framework Android real | Ainda não implementado |
Janela de Activity |
Ainda não implementada |
| JNI, Bionic e Binder | Ainda não implementados |
| Tradução ARM/ARM64 → x86-64 | Ainda não implementada |
O APK usado atualmente como alvo de integração é:
SukiSU v4.1.3 — build 40796
Na validação realizada após o commit de packed-switch, o WineDroid produziu
um ELF Linux nativo com o seguinte relatório:
Métodos raiz: 4
Métodos internos ligados: 166
Chamadas externas mantidas em stub: 176
Métodos internos rejeitados: 6
Chamadas limitadas pela profundidade: 334
Profundidade máxima usada: 3
Packed-switch reais no C: 1
Cases traduzidos nesse switch: 27
Formato final: ELF64 PIE x86-64
Os quatro métodos raiz são:
Lcom/sukisu/ultra/KernelSUApplication;-><init>()V
Lcom/sukisu/ultra/KernelSUApplication;->onCreate()V
Lcom/sukisu/ultra/ui/MainActivity;-><init>()V
Lcom/sukisu/ultra/ui/MainActivity;->onCreate(Landroid/os/Bundle;)V
Durante a execução, o ELF percorre código real do APK, inicializa objetos, segue chamadas internas e chega à camada de reflexão. O ponto atual termina com:
Ljava/lang/NoSuchMethodException;
[WineDroid] throw handle=25
status: 103
Esse status não indica falha na geração do ELF. Ele representa um caminho
Dalvik de throw alcançado porque operações como
Class.getDeclaredMethod, Method.invoke e partes de sun.misc.Unsafe
ainda dependem de stubs incompletos.
Note
As métricas acima são um retrato de uma configuração específica:
profundidade 3, teto de 192 métodos e o APK citado. Elas devem crescer
ou mudar conforme novos opcodes e APIs forem implementados.
flowchart LR
APK[APK] --> ZIP[Loader ZIP/APK]
ZIP --> AXML[Parser Android Binary XML]
ZIP --> DEX[Parser e índice DEX]
DEX --> RESOLVE[Resolução de métodos, campos e tipos]
RESOLVE --> GRAPH[Linker recursivo]
GRAPH --> LOWER[Lowering Dalvik para C]
GRAPH --> STUBS[Stubs java.* e android.*]
LOWER --> C[C intermediário]
C --> CLANG[Clang]
CLANG --> ELF[ELF64 PIE x86-64]
ELF --> LINUX[Kernel Linux]
STUBS --> ELF
A ABI antiga aceitava apenas this e um argumento. A ABI atual encaminha
todos os registradores de entrada:
wd_value method(uint32_t argc, const wd_value *args);O frame Dalvik recebe os argumentos no final do conjunto de registradores:
incoming_start = registers_size - ins_size
v[incoming_start + i] = args[i]
Isso permite ligar:
- métodos estáticos com argumentos;
- métodos de instância;
- construtores;
- chamadas com vários parâmetros;
- referências e valores primitivos;
- palavras de valores Dalvik wide.
O linker parte dos quatro métodos de ciclo de vida do SukiSU e percorre
referências invoke-*.
Quando o alvo interno é compatível, ele é compilado dentro do mesmo ELF. Quando ainda não é compatível, a chamada permanece em um stub externo.
Proteções atuais:
profundidade padrão: 4
teto padrão do grafo: 192 métodos
tamanho máximo: 1024 code units por método
Métodos incompatíveis são rejeitados individualmente sem impedir a expansão dos demais ramos do grafo.
O backend já baixa branches Dalvik para labels e goto em C.
O opcode packed-switch (0x2b) também é interpretado. O compilador:
- localiza o
packed-switch-payload; - lê
sizeefirst_key; - resolve cada offset relativo à instrução de switch;
- valida os destinos;
- gera um
switchC; - trata payloads DEX como dados, não como instruções executáveis.
A presença de throw (0x27) não bloqueia mais um método inteiro. O lowering
chama wd_throw apenas quando aquele caminho é realmente alcançado.
Ainda não existem:
- leitura completa das tabelas de exceção do
code_item; - busca de handlers compatíveis;
- propagação entre frames;
- semântica completa de
try/catch/finally.
Por enquanto, um throw não capturado encerra o ELF com status 103.
O comando winedroid inspect informa:
- tamanho e entradas do APK;
- formato do manifesto;
- package name e versão;
- SDK mínimo e alvo;
Applicatione launcherActivity;- activities e permissões;
- arquivos DEX;
- quantidade de classes, métodos, campos, protótipos e strings;
- bibliotecas nativas e ABI;
- presença de
resources.arsc; - entradas de assinatura v1;
- avisos encontrados durante o parsing.
O compilador suporta diferentes níveis de teste:
- programa Dalvik sintético;
- método isolado extraído de DEX;
- método isolado extraído de APK;
- método com objetos e chamadas externas;
- quatro métodos do ciclo de vida em um ELF;
- grafo recursivo de métodos internos.
O código C gerado contém atualmente estruturas para:
- frame de registradores Dalvik;
- valores e handles de objetos;
- armazenamento de campos;
- criação experimental de objetos;
- dispatch de métodos internos por
method_id; - stubs para métodos externos;
- logs de chamadas e alocações;
- caminho controlado para
throw.
Ambiente atualmente suportado:
Sistema: Linux
Arquitetura: x86-64
Rust: 1.88 ou superior
Edição Rust: 2024
Compilador C: Clang
Pacotes necessários:
- Git;
- Rust e Cargo;
- Clang;
- linker e libc de desenvolvimento;
- utilitários como
fileereadelfpara inspeção dos artefatos.
Exemplo no Arch Linux/Manjaro:
sudo pacman -S --needed git rust clang base-devel binutils fileExemplo no Debian/Ubuntu:
sudo apt install git cargo rustc clang build-essential binutils fileClone o repositório:
git clone https://github.com/rickbergs/winedroid.git
cd winedroidCompile todo o workspace:
cargo build --release --workspaceOs principais binários serão gerados em target/release/:
winedroid
winedroid-aot
winedroid-sukisu-link
winedroid-sukisu-recursive
cargo run -p winedroid-cli -- doctorcargo run -p winedroid-cli -- inspect ./app.apkRelatório em JSON:
cargo run -p winedroid-cli -- inspect ./app.apk --jsoncargo run -p winedroid-compiler --bin winedroid-aot -- \
scan-apk ./app.apk \
--limit 30cargo run -p winedroid-compiler --bin winedroid-aot -- \
demo \
--output /tmp/winedroid-demo.elf \
--emit-c /tmp/winedroid-demo.c \
--runcargo run -p winedroid-compiler --bin winedroid-aot -- \
compile-apk ./app.apk \
--method 'Lexample/App;->value()I' \
--output /tmp/winedroid-method.elf \
--emit-c /tmp/winedroid-method.c \
--runcargo run -p winedroid-compiler --bin winedroid-aot -- \
bootstrap-apk ./app.apk \
--method 'Lexample/App;->onCreate()V' \
--output /tmp/winedroid-bootstrap.elf \
--emit-c /tmp/winedroid-bootstrap.ccargo run -p winedroid-compiler --bin winedroid-sukisu-recursive -- \
~/Downloads/SukiSU_v4.1.3_40796-release.apk \
--output /tmp/winedroid-sukisu.elf \
--emit-c /tmp/winedroid-sukisu.c \
--max-depth 3 \
--max-methods 192Execute separadamente para observar o status atual:
/tmp/winedroid-sukisu.elf
printf 'status=%s\n' "$?"Com os stubs atuais de reflexão, o caminho conhecido pode terminar em
status=103.
Execute a validação completa:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo build --release --workspaceA suíte cobre, entre outros pontos:
- parsing de APK, AXML e DEX;
- resolução de referências;
- geração de ELF nativo;
- campos estáticos;
- objetos e campos compartilhados;
- ciclo de vida ligado;
- ABI genérica;
- chamadas internas recursivas;
packed-switch;- scanners que ignoram payloads DEX.
O código C é compilado com -Wall, -Wextra e -Werror.
Apenas -Wunused-label é dispensado porque o lowering preserva labels Dalvik
que podem ficar sem predecessores após a tradução.
winedroid/
├── crates/
│ ├── winedroid-core/ # APK, AXML, DEX e modelos
│ ├── winedroid-cli/ # inspect e doctor
│ └── winedroid-compiler/ # AOT, bootstrap e linkers
├── dev/ # scripts de desenvolvimento e validação
├── docs/ # documentação técnica
├── README.md
├── ROADMAP.md
├── Cargo.toml
└── LICENSE
Responsável por:
- abrir o APK;
- classificar entradas ZIP;
- interpretar o manifesto;
- indexar DEX;
- expor classes, métodos, campos e protótipos;
- inventariar bibliotecas nativas.
Interface de inspeção e diagnóstico:
winedroid inspect
winedroid doctor
Contém:
- backend AOT básico;
- backend de objetos;
- lowering Dalvik → C;
- compilação C → ELF;
- ciclo de vida ligado;
- linker recursivo;
- runtime C experimental;
- testes de execução nativa.
O WineDroid ainda não executa aplicativos Android completos de maneira utilizável.
Principais limitações:
- não existe janela de
Activity; - View, TextView, Compose e renderização não funcionam;
sparse-switch(0x2c) ainda bloqueia métodos;- existe uma borda de decoding envolvendo
0x00em um método real; try/catchDalvik ainda não funciona;- reflexão Java é apenas parcial/stub;
- dispatch virtual ainda usa o
method_idestático; - strings, arrays, coleções e classes Java são incompletos;
Context,PackageManager,SharedPreferencese serviços Android são stubs;- JNI e bibliotecas Android
.sonão são executadas; - Bionic, Binder, Looper e ciclo de mensagens não existem;
- OpenGL ES, áudio, sensores e notificações não existem;
- APKs ARM/ARM64 não têm tradução para o host x86-64;
- não existe sandbox própria por aplicativo.
Ordem técnica atual:
- implementar
sparse-switch(0x2c) e seu payload; - corrigir a borda de decoding/payload que aparece como opcode
0x00; - interpretar tabelas Dalvik de
try/catch; - implementar reflexão mínima:
Class.getDeclaredMethod;AccessibleObject.setAccessible;Method.invoke;- operações necessárias de
sun.misc.Unsafe;
- implementar dispatch virtual pelo tipo real do objeto;
- substituir stubs fundamentais de
java.lang,java.utilejava.io; - criar
Context,ApplicationeActivitymínimos; - abrir a primeira janela Wayland;
- adicionar JNI x86-64 e uma ponte inicial para Bionic;
- investigar tradução ou recompilação de código ARM64.
O planejamento detalhado está em ROADMAP.md.
Caution
APK deve ser tratado como entrada não confiável.
Recomendações:
- nunca execute o WineDroid como root;
- use apenas APKs que você tenha direito de testar;
- prefira uma conta de usuário sem privilégios;
- não exponha arquivos pessoais ao runtime experimental;
- revise o C intermediário antes de executar APKs desconhecidos;
- considere namespaces, seccomp ou outro sandbox externo.
O compilador e o runtime ainda não passaram por auditoria de segurança.
Contribuições são bem-vindas, especialmente em:
- parsing DEX;
- cobertura de opcodes;
- semântica de exceções;
- runtime Java mínimo;
- APIs Android;
- Wayland;
- JNI e carregamento ELF Android;
- fuzzing e testes de regressão.
Ao relatar um problema, inclua:
- distribuição e kernel;
- versão do Rust e do Clang;
- comando executado;
- saída completa;
- descritor do método;
- opcode e
pc, quando disponíveis; - APK de teste apenas quando sua redistribuição for permitida.
Distribuído sob a licença Apache-2.0.
Criado e mantido por Richard Bergamaschi.
WineDroid não é afiliado ao Android, Google, WineHQ ou ao projeto Wine. Android é uma marca de seus respectivos proprietários.