L×Box: Desvendando o Tráfego por Aplicativo para Diagnóstico Detalhado
A nova funcionalidade Per-app traffic profiler do L×Box, um cliente Android open-source baseado em sing-box, permite aos usuários diagnosticar problemas de roteamento e privacidade de forma intuitiva. A ferramenta visualiza o tráfego de rede de cada aplicativo individualmente, facilitando a identificação de falhas em regras de split-routing e a auditoria de conexões.
MundiX News·13 de maio de 2026·10 min de leitura·👁 31 views
Recentemente, depurei um problema comum: "Tinkoff Investments não abre via VPN". O sintoma era que o aplicativo iniciava, as imagens carregavam, mas a sessão de autorização não era estabelecida, e o login ficava travado. Após cerca de 30 minutos de análise de snapshots, logs e correlação manual de conn_ids, a causa ficou clara. Parte dos domínios, como *.t-bank-app.ru, era corretamente roteada diretamente pelo meu conjunto de regras ru-domains. No entanto, outra parte resolvia via CNAME para *.trbcdn.net (com TLD .net!), que não estava coberto pelas regras ru-domains. Consequentemente, o sing-box enviava esse tráfego através do bypass da VPN para a Polônia. Isso resultava em um split-routing: algumas requisições saíam do meu IP doméstico e outras de um IP polonês. O backend do banco, que associa a sessão ao IP de origem e a um fingerprint, detectava um cliente inconsistente e recusava o estabelecimento do estado de autenticação. O sintoma era "login travado", e a raiz do problema era o split-routing em nível de domínio dentro de um único aplicativo.
Diante dessa experiência, ficou evidente que tal diagnóstico não deveria consumir tanto tempo. Assim, no L×Box (meu cliente Android baseado em sing-box e open source), foi introduzida a funcionalidade Per-app traffic profiler. O processo é simples: selecione o aplicativo, inicie a gravação, execute o aplicativo por cerca de 30 segundos e, em seguida, analise a aba Domains. É possível visualizar claramente quais domínios (*.t-bank-app.ru, por exemplo) estão saindo via direct-out e quais estão sendo roteados via vpn-1. A falha nas regras de roteamento torna-se imediatamente aparente, sem a necessidade de packet capture, root ou ferramentas externas.
Por que isso é importante?
A segurança individual e corporativa é cada vez mais demandada. Os usuários desejam saber para onde seus aplicativos estão enviando dados, e esta ferramenta oferece um ponto de diagnóstico simples. Ela atende a três classes principais de tarefas que surgem regularmente para qualquer usuário de VPN com split-routing:
Debug de Roteamento: O clássico "X não funciona via VPN". A causa mais comum é um domínio que parece ser local, mas seu CNAME o direciona para uma CDN com um TLD estrangeiro, fazendo com que o rule_set falhe. Sem visibilidade da cadeia de CNAMEs, a investigação se torna um verdadeiro detetive.
Auditoria de Privacidade: "Para onde este aplicativo está se conectando?" Uma lista de domínios, ordenada por volume de tráfego, permite identificar conexões desconhecidas. Ao notar algo suspeito, o usuário pode investigar o tráfego de saída e, se necessário, bloqueá-lo.
Debug de Desempenho: "Por que este serviço está lento?" Analisar em qual outbound o tráfego está sendo roteado, se há tentativas de DNS repetidas ou se a conexão está presa a um IP lento, ajuda a diagnosticar gargalos de desempenho.
No mercado de clientes VPN para Android, ninguém oferece visualização em tempo real da cadeia de roteamento por aplicativo. Ferramentas como PCAPDroid realizam packet capture, mas não enxergam o roteamento. NetGuard bloqueia tráfego, mas não diagnostica. AdGuard visualiza DNS, mas não o outbound. O Wireshark é uma solução desktop. A vantagem do L×Box reside no sing-box como motor de VPN subjacente, que registra resoluções DNS estruturadas com cadeias de CNAME, o nome do pacote associado ao conn_id e a cadeia de outbound. Essa informação é extraída e apresentada de forma organizada.
Como funciona?
Um serviço Singleton TrafficProfiler (Dart, ChangeNotifier) gerencia uma sessão ativa e um buffer circular (ring-buffer) para 5 sessões concluídas. Todos os dados são mantidos em memória; ao encerrar o aplicativo, as sessões são apagadas. A persistência não é um objetivo, pois a funcionalidade é puramente diagnóstica e não analítica, e o custo de esquemas e migrações excederia o valor. Existem duas fontes de dados independentes:
Fonte A: Stream de Logs: O sing-box envia logs através de EventChannel nativo (lxbox/coreLog) para o AppLog (meu buffer circular por fonte). O TrafficProfiler escuta esses logs, analisando três tipos de linhas: INFO[N] [<conn_id> <Nms>] router: found package name: <pkg>, INFO[N] [<conn_id> <Nms>] dns: exchanged|cached <type> <name>. <ttl> IN <type> <data>, e INFO[N] [<conn_id> <Nms>] dns: exchange failed for <host>: <reason>. Um acumulador por conn_id (_DnsAccumulator) constrói a cadeia de CNAMEs e emite eventos finais de dnsResolve com o domínio original solicitado, a lista de saltos de CNAME e o IP final.
Fonte B: Polling da API Clash: A API /connections do sing-box é consultada a cada 2 segundos (apenas enquanto uma sessão está ativa para economizar bateria). A diferença em relação ao snapshot anterior revela eventos de tcpOpen/tcpClose com informações de bytes, cadeias, host, IP e porta. A filtragem é feita com base em metadata.process ou metadata.processPath, após remover o sufixo de UID (que inclui o PID, como em "ru.tinkoff.investing (10364)").
Agregações por domínio (Aggregates byDomain) e por IP (byIp) são calculadas sob demanda ao ler os dados (nas visualizações de sub-aba). Um classificador de problemas de conexão (Connection-issue classifier) marca eventos com um ícone de alerta (⚠) com base em duas condições independentes de localidade: DNS timeout (sinal direto do sing-box) e TCP RST early (heurística para bloqueio/RST/unreachable, detectada por conexões fechadas em menos de 1 segundo com 0 bytes enviados/recebidos).
Os limites de memória são de 50.000 eventos por sessão ou uma janela deslizante de 3 horas, o que ocorrer primeiro. O buffer circular _completed é esvaziado em modo FIFO ao adicionar um novo item. Mapas de conn_id são limpos com um TTL de 30 segundos quando o mapa excede 256 entradas.
Interface do Usuário:
A interface possui 4 sub-abas:
Live: Um stream de eventos em ordem cronológica inversa, com marcadores de tipo coloridos (DNS/TCP/UDP) e exibição inline da cadeia de CNAME e da cadeia de outbound abaixo de cada evento.
Domains: Agregado por domínio, ordenado por bytes, com filtro de busca por domínio, IP ou CNAME target. Expandir uma linha mostra a cadeia de CNAME, todos os IPs, o outbound e os problemas detectados (⚠).
IPs: Similar à aba Domains, mas agregado por IP. Útil para conexões sem host (sem sniffing de SNI) e IPs suspeitos de feeds de ameaças.
Connections: Uma linha do tempo por conexão com expansão inline, mostrando CNAME, todos os IPs, problemas (⚠) e um botão [View in Domains →] com transição automática e preenchimento de busca.
A gravação continua mesmo ao sair da tela (devido ao Singleton). Um chip vermelho com um raio (⚡) aparece na barra da tela inicial com uma versão curta do nome do pacote, indicando que a gravação está ativa. Tocar nele retorna à aba Per-app. A gravação só é interrompida manualmente, por force-stop do aplicativo ou ao iniciar uma nova sessão.
Desafios Enfrentados:
Grande parte do tempo de desenvolvimento foi consumida por aspectos não previstos no design inicial. Destaco alguns pontos:
Sufixo UID em metadata.process: O sing-box retorna "ru.tinkoff.investing (10364)" em vez de apenas "ru.tinkoff.investing". Sem remover o PID entre parênteses, nenhuma conexão é atribuída corretamente à sessão alvo. Além disso, metadata.process frequentemente é null, com o pacote atual em metadata.processPath.
Overflow do Ring-buffer e Scan de Diferença de Comprimento: O método inicial de drenagem de logs comparava entries.length com o snapshot anterior. O AppLog tem um limite de 500 entradas para a fonte principal. Quando o tráfego intenso preenche o buffer, novas entradas substituem as antigas, a contagem estabiliza em 500 e a diferença de comprimento se torna zero permanentemente. Eventos DNS pararam de chegar após 1-2 minutos de gravação. A correção foi usar a diferença de timestamp (armazenar o timestamp da última entrada processada, em vez de seu índice).
Captura de dns: cached: É necessário capturar dns: cached tanto quanto dns: exchanged. O sing-box registra ambas as formas; para aplicativos com uso intenso, a maioria das resoluções são cache hits. A máscara regex inicial capturava apenas exchanged, resultando em um nível DNS fragmentado (conexões TCP existiam, mas eventos DNS não).
Atribuição de Domínio ao Domínio Original, não ao Target CNAME: O sing-box registra a cadeia de CNAME sequencialmente: CNAME a → b, seguido por A b → ip. Se um evento fosse emitido com o nome da entrada A (o host CDN), o domínio b apareceria na aba Domains, e o domínio a originalmente solicitado desapareceria. A correção foi fazer com que o acumulador por conn_id armazene o primeiro nome, e os targets CNAME sejam acumulados separadamente como uma cadeia.
Alvo de Clique em Connections: A expansão inline de uma linha de conexão tornava todo o tile clicável. Ao tocar no botão "View in Domains" ou tentar copiar um CNAME, a linha se fechava. A solução foi envolver o InkWell apenas na parte do cabeçalho, com a seção expandida vivendo separadamente.
API de Debug:
Todos os controles da UI estão acessíveis via API HTTP localhost (autenticada por token Bearer, vinculada a 127.0.0.1, com verificação de host anti-rebind). Os endpoints incluem /profiler/start, /profiler/stop, /profiler/active, /profiler/session/<id>?include=domains,ips,events, /profiler/sessions, além de um stream SSE /profiler/stream para o feed de eventos em tempo real. Uma referência completa está disponível na documentação.
A verdadeira utilidade dessa API não é para o desenvolvedor, mas para um agente de IA. O cenário de uso atual é: conectar o telefone via cabo, configurar adb forward, abrir um agente de codificação no projeto L×Box, descrever o problema (ex: "Tinkoff Investments login travado, investigue") e fornecer o token da Debug API. O agente então inicia a gravação, solicita a reprodução do problema, consulta os dados da sessão e identifica o split-routing causado pelo CNAME para *.trbcdn.net. Ele pode até sugerir uma correção e gerar um Pull Request. Todo o processo de diagnóstico, que antes levava horas, agora se resume ao tempo de tomar um chá. Isso é possível porque o diagnóstico é estruturado em JSON tipado com campos como domain, ip, cname_chain, outbound_chain e issues, permitindo que o agente processe os dados de forma programática.
O que não faz (ainda):
URLs e Headers em Nível HTTP: Atualmente, apenas domain:port é visível, não GET /api/v3/auth/login ou User-Agent. Isso se deve à limitação arquitetural do sing-box operar no nível TUN/SOCKS, onde o tráfego TLS já está criptografado.
Latência/RTT por Domínio: A coleta atual se limita a bytes e contagem de conexões. Adicionar tempos de handshake TCP e RTT DNS ajudaria a identificar nós CDN lentos.
Criação Inline de Regras de Roteamento: Botões como [Block this domain] ou [Add to ru-direct] na aba Domains facilitariam o feedback loop, mas a implementação via API por agentes já cobre essa funcionalidade.
TLS Fingerprinting (JA3/JA4): O hash do TLS ClientHello é útil para depurar problemas de DPI, mas o sing-box ainda não expõe essa informação via API libbox.
Onde funciona:
O L×Box v1.7.0 está disponível para Android 8.0+ (minSdk 26), arquiteturas arm64-v8a / x86_64. Requer sing-box com find_process: true (habilitado por padrão no template). O projeto é open source no GitHub, com builds via CI/CD e sem telemetria. O guia do usuário para rastreamento por aplicativo está disponível na documentação.
O sing-box já possui a inteligência sobre o tráfego e o roteamento. O L×Box atua como a interface que finalmente torna essa informação acessível aos usuários, eliminando a necessidade de correlação manual de conn_ids entre snapshots de logs.
🛡️⚡
Pare de pesquisar. Comece a hackear.
O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.
Sem cartão para começar · Planos a partir de R$49/mês
Recentemente, depurei um problema comum: "Tinkoff Investments não abre via VPN". O sintoma era que o aplicativo iniciava, as imagens carregavam, mas a sessão de autorização não era estabelecida, e o login ficava travado. Após cerca de 30 minutos de análise de snapshots, logs e correlação manual de conn_ids, a causa ficou clara. Parte dos domínios, como *.t-bank-app.ru, era corretamente roteada diretamente pelo meu conjunto de regras ru-domains. No entanto, outra parte resolvia via CNAME para *.trbcdn.net (com TLD .net!), que não estava coberto pelas regras ru-domains. Consequentemente, o sing-box enviava esse tráfego através do bypass da VPN para a Polônia. Isso resultava em um split-routing: algumas requisições saíam do meu IP doméstico e outras de um IP polonês. O backend do banco, que associa a sessão ao IP de origem e a um fingerprint, detectava um cliente inconsistente e recusava o estabelecimento do estado de autenticação. O sintoma era "login travado", e a raiz do problema era o split-routing em nível de domínio dentro de um único aplicativo.
Diante dessa experiência, ficou evidente que tal diagnóstico não deveria consumir tanto tempo. Assim, no L×Box (meu cliente Android baseado em sing-box e open source), foi introduzida a funcionalidade Per-app traffic profiler. O processo é simples: selecione o aplicativo, inicie a gravação, execute o aplicativo por cerca de 30 segundos e, em seguida, analise a aba Domains. É possível visualizar claramente quais domínios (*.t-bank-app.ru, por exemplo) estão saindo via direct-out e quais estão sendo roteados via vpn-1. A falha nas regras de roteamento torna-se imediatamente aparente, sem a necessidade de packet capture, root ou ferramentas externas.
Por que isso é importante?
A segurança individual e corporativa é cada vez mais demandada. Os usuários desejam saber para onde seus aplicativos estão enviando dados, e esta ferramenta oferece um ponto de diagnóstico simples. Ela atende a três classes principais de tarefas que surgem regularmente para qualquer usuário de VPN com split-routing:
Debug de Roteamento: O clássico "X não funciona via VPN". A causa mais comum é um domínio que parece ser local, mas seu CNAME o direciona para uma CDN com um TLD estrangeiro, fazendo com que o rule_set falhe. Sem visibilidade da cadeia de CNAMEs, a investigação se torna um verdadeiro detetive.
Auditoria de Privacidade: "Para onde este aplicativo está se conectando?" Uma lista de domínios, ordenada por volume de tráfego, permite identificar conexões desconhecidas. Ao notar algo suspeito, o usuário pode investigar o tráfego de saída e, se necessário, bloqueá-lo.
Debug de Desempenho: "Por que este serviço está lento?" Analisar em qual outbound o tráfego está sendo roteado, se há tentativas de DNS repetidas ou se a conexão está presa a um IP lento, ajuda a diagnosticar gargalos de desempenho.
No mercado de clientes VPN para Android, ninguém oferece visualização em tempo real da cadeia de roteamento por aplicativo. Ferramentas como PCAPDroid realizam packet capture, mas não enxergam o roteamento. NetGuard bloqueia tráfego, mas não diagnostica. AdGuard visualiza DNS, mas não o outbound. O Wireshark é uma solução desktop. A vantagem do L×Box reside no sing-box como motor de VPN subjacente, que registra resoluções DNS estruturadas com cadeias de CNAME, o nome do pacote associado ao conn_id e a cadeia de outbound. Essa informação é extraída e apresentada de forma organizada.
Como funciona?
Um serviço Singleton TrafficProfiler (Dart, ChangeNotifier) gerencia uma sessão ativa e um buffer circular (ring-buffer) para 5 sessões concluídas. Todos os dados são mantidos em memória; ao encerrar o aplicativo, as sessões são apagadas. A persistência não é um objetivo, pois a funcionalidade é puramente diagnóstica e não analítica, e o custo de esquemas e migrações excederia o valor. Existem duas fontes de dados independentes:
Fonte A: Stream de Logs: O sing-box envia logs através de EventChannel nativo (lxbox/coreLog) para o AppLog (meu buffer circular por fonte). O TrafficProfiler escuta esses logs, analisando três tipos de linhas: INFO[N] [<conn_id> <Nms>] router: found package name: <pkg>, INFO[N] [<conn_id> <Nms>] dns: exchanged|cached <type> <name>. <ttl> IN <type> <data>, e INFO[N] [<conn_id> <Nms>] dns: exchange failed for <host>: <reason>. Um acumulador por conn_id (_DnsAccumulator) constrói a cadeia de CNAMEs e emite eventos finais de dnsResolve com o domínio original solicitado, a lista de saltos de CNAME e o IP final.
Fonte B: Polling da API Clash: A API /connections do sing-box é consultada a cada 2 segundos (apenas enquanto uma sessão está ativa para economizar bateria). A diferença em relação ao snapshot anterior revela eventos de tcpOpen/tcpClose com informações de bytes, cadeias, host, IP e porta. A filtragem é feita com base em metadata.process ou metadata.processPath, após remover o sufixo de UID (que inclui o PID, como em "ru.tinkoff.investing (10364)").
Agregações por domínio (Aggregates byDomain) e por IP (byIp) são calculadas sob demanda ao ler os dados (nas visualizações de sub-aba). Um classificador de problemas de conexão (Connection-issue classifier) marca eventos com um ícone de alerta (⚠) com base em duas condições independentes de localidade: DNS timeout (sinal direto do sing-box) e TCP RST early (heurística para bloqueio/RST/unreachable, detectada por conexões fechadas em menos de 1 segundo com 0 bytes enviados/recebidos).
Os limites de memória são de 50.000 eventos por sessão ou uma janela deslizante de 3 horas, o que ocorrer primeiro. O buffer circular _completed é esvaziado em modo FIFO ao adicionar um novo item. Mapas de conn_id são limpos com um TTL de 30 segundos quando o mapa excede 256 entradas.
Interface do Usuário:
A interface possui 4 sub-abas:
Live: Um stream de eventos em ordem cronológica inversa, com marcadores de tipo coloridos (DNS/TCP/UDP) e exibição inline da cadeia de CNAME e da cadeia de outbound abaixo de cada evento.
Domains: Agregado por domínio, ordenado por bytes, com filtro de busca por domínio, IP ou CNAME target. Expandir uma linha mostra a cadeia de CNAME, todos os IPs, o outbound e os problemas detectados (⚠).
IPs: Similar à aba Domains, mas agregado por IP. Útil para conexões sem host (sem sniffing de SNI) e IPs suspeitos de feeds de ameaças.
Connections: Uma linha do tempo por conexão com expansão inline, mostrando CNAME, todos os IPs, problemas (⚠) e um botão [View in Domains →] com transição automática e preenchimento de busca.
A gravação continua mesmo ao sair da tela (devido ao Singleton). Um chip vermelho com um raio (⚡) aparece na barra da tela inicial com uma versão curta do nome do pacote, indicando que a gravação está ativa. Tocar nele retorna à aba Per-app. A gravação só é interrompida manualmente, por force-stop do aplicativo ou ao iniciar uma nova sessão.
Desafios Enfrentados:
Grande parte do tempo de desenvolvimento foi consumida por aspectos não previstos no design inicial. Destaco alguns pontos:
Sufixo UID em metadata.process: O sing-box retorna "ru.tinkoff.investing (10364)" em vez de apenas "ru.tinkoff.investing". Sem remover o PID entre parênteses, nenhuma conexão é atribuída corretamente à sessão alvo. Além disso, metadata.process frequentemente é null, com o pacote atual em metadata.processPath.
Overflow do Ring-buffer e Scan de Diferença de Comprimento: O método inicial de drenagem de logs comparava entries.length com o snapshot anterior. O AppLog tem um limite de 500 entradas para a fonte principal. Quando o tráfego intenso preenche o buffer, novas entradas substituem as antigas, a contagem estabiliza em 500 e a diferença de comprimento se torna zero permanentemente. Eventos DNS pararam de chegar após 1-2 minutos de gravação. A correção foi usar a diferença de timestamp (armazenar o timestamp da última entrada processada, em vez de seu índice).
Captura de dns: cached: É necessário capturar dns: cached tanto quanto dns: exchanged. O sing-box registra ambas as formas; para aplicativos com uso intenso, a maioria das resoluções são cache hits. A máscara regex inicial capturava apenas exchanged, resultando em um nível DNS fragmentado (conexões TCP existiam, mas eventos DNS não).
Atribuição de Domínio ao Domínio Original, não ao Target CNAME: O sing-box registra a cadeia de CNAME sequencialmente: CNAME a → b, seguido por A b → ip. Se um evento fosse emitido com o nome da entrada A (o host CDN), o domínio b apareceria na aba Domains, e o domínio a originalmente solicitado desapareceria. A correção foi fazer com que o acumulador por conn_id armazene o primeiro nome, e os targets CNAME sejam acumulados separadamente como uma cadeia.
Alvo de Clique em Connections: A expansão inline de uma linha de conexão tornava todo o tile clicável. Ao tocar no botão "View in Domains" ou tentar copiar um CNAME, a linha se fechava. A solução foi envolver o InkWell apenas na parte do cabeçalho, com a seção expandida vivendo separadamente.
API de Debug:
Todos os controles da UI estão acessíveis via API HTTP localhost (autenticada por token Bearer, vinculada a 127.0.0.1, com verificação de host anti-rebind). Os endpoints incluem /profiler/start, /profiler/stop, /profiler/active, /profiler/session/<id>?include=domains,ips,events, /profiler/sessions, além de um stream SSE /profiler/stream para o feed de eventos em tempo real. Uma referência completa está disponível na documentação.
A verdadeira utilidade dessa API não é para o desenvolvedor, mas para um agente de IA. O cenário de uso atual é: conectar o telefone via cabo, configurar adb forward, abrir um agente de codificação no projeto L×Box, descrever o problema (ex: "Tinkoff Investments login travado, investigue") e fornecer o token da Debug API. O agente então inicia a gravação, solicita a reprodução do problema, consulta os dados da sessão e identifica o split-routing causado pelo CNAME para *.trbcdn.net. Ele pode até sugerir uma correção e gerar um Pull Request. Todo o processo de diagnóstico, que antes levava horas, agora se resume ao tempo de tomar um chá. Isso é possível porque o diagnóstico é estruturado em JSON tipado com campos como domain, ip, cname_chain, outbound_chain e issues, permitindo que o agente processe os dados de forma programática.
O que não faz (ainda):
URLs e Headers em Nível HTTP: Atualmente, apenas domain:port é visível, não GET /api/v3/auth/login ou User-Agent. Isso se deve à limitação arquitetural do sing-box operar no nível TUN/SOCKS, onde o tráfego TLS já está criptografado.
Latência/RTT por Domínio: A coleta atual se limita a bytes e contagem de conexões. Adicionar tempos de handshake TCP e RTT DNS ajudaria a identificar nós CDN lentos.
Criação Inline de Regras de Roteamento: Botões como [Block this domain] ou [Add to ru-direct] na aba Domains facilitariam o feedback loop, mas a implementação via API por agentes já cobre essa funcionalidade.
TLS Fingerprinting (JA3/JA4): O hash do TLS ClientHello é útil para depurar problemas de DPI, mas o sing-box ainda não expõe essa informação via API libbox.
Onde funciona:
O L×Box v1.7.0 está disponível para Android 8.0+ (minSdk 26), arquiteturas arm64-v8a / x86_64. Requer sing-box com find_process: true (habilitado por padrão no template). O projeto é open source no GitHub, com builds via CI/CD e sem telemetria. O guia do usuário para rastreamento por aplicativo está disponível na documentação.
O sing-box já possui a inteligência sobre o tráfego e o roteamento. O L×Box atua como a interface que finalmente torna essa informação acessível aos usuários, eliminando a necessidade de correlação manual de conn_ids entre snapshots de logs.
📤 Compartilhar & Baixar
🧰 Ferramentas recomendadas
Divulgação: alguns links são patrocinados. Podemos receber comissão se você comprar — sem custo extra para você. Só indicamos o que faz sentido para a comunidade.