tcpsnitch
é uma ferramenta de rastreamento projetada para investigar as interações entre um aplicativo e a pilha TCP/IP. tcpsnitch
executa o comando especificado até sair e interceptar todas as chamadas de função libc em soquetes de internet.
Para começar suavemente, pode-se executar o seguinte comando para rastrear o programa curl
:
$ tcpsnitch curl google.com
Para cada soquete de Internet aberto, tcpsnitch
constrói uma lista ordenada de chamadas de função (uma invocação de função é chamada de evento no restante deste documento). Para cada evento, tcpsnitch
registra os argumentos, o valor de retorno e diversas informações, como o carimbo de data/hora atual ou o ID do thread. Especificamente, um evento connect()
pode ter esta aparência em um rastreamento de soquete:
{
"type" : " connect " ,
"timestamp_usec" : 1491043720731853 ,
"return_value" : 0 ,
"success" : true ,
"thread_id" : 17313 ,
"details" : {
"addr" : {
"sa_family" : " AF_INET " ,
"ip" : " 127.0.1.1 " ,
"port" : " 53 "
}
}
}
Os rastreamentos de soquete são gravados em arquivos de texto onde cada linha é um objeto JSON que representa um único evento. O chefe de tal rastreamento poderia assim:
{ "type" : " socket " , "timestamp_usec" : 1491043720731840 , "return_value" : 6 , "success" : true , "thread_id" : 17313 , "details" : { "sock_info" : { "domain" : " AF_INET " , "type" : " SOCK_DGRAM " , "protocol" : 0 , "SOCK_CLOEXEC" : false , "SOCK_NONBLOCK" : true }}}
{ "type" : " ioctl " , "timestamp_usec" : 1491043720765019 , "return_value" : 0 , "success" : true , "thread_id" : 17313 , "details" : { "request" : " FIONREAD " }}
{ "type" : " recvfrom " , "timestamp_usec" : 1491043720765027 , "return_value" : 44 , "success" : true , "thread_id" : 17313 , "details" : { "bytes" : 2048 , "flags" : { "MSG_CMSG_CLOEXEC" : false , "MSG_DONTWAIT" : false , "MSG_ERRQUEUE" : false , "MSG_OOB" : false , "MSG_PEEK" : false , "MSG_TRUNC" : false , "MSG_WAITALL" : false }, "addr" : { "sa_family" : " AF_INET " , "ip" : " 127.0.1.1 " , "port" : " 53 " }}}
{ "type" : " ioctl " , "timestamp_usec" : 1491043720770075 , "return_value" : 0 , "success" : true , "thread_id" : 17313 , "details" : { "request" : " FIONREAD " }}
{ "type" : " recvfrom " , "timestamp_usec" : 1491043720770094 , "return_value" : 56 , "success" : true , "thread_id" : 17313 , "details" : { "bytes" : 65536 , "flags" : { "MSG_CMSG_CLOEXEC" : false , "MSG_DONTWAIT" : false , "MSG_ERRQUEUE" : false , "MSG_OOB" : false , "MSG_PEEK" : false , "MSG_TRUNC" : false , "MSG_WAITALL" : false }, "addr" : { "sa_family" : " AF_INET " , "ip" : " 127.0.1.1 " , "port" : " 53 " }}}
Como um único comando pode bifurcar vários processos (e tcpsnitch
segue os forks), todos os rastreamentos de soquete pertencentes a um determinado processo são reunidos em um diretório, nomeado após o processo rastreado. Dentro desse diretório, os rastreamentos de soquete são nomeados com base na ordem em que foram abertos pelo processo.
Por padrão, os rastreamentos são salvos em um diretório aleatório em /tmp
e carregados automaticamente em www.tcpsnitch.org, uma plataforma projetada para centralizar, visualizar e analisar os rastreamentos. Observe que todos os rastreamentos carregados são públicos e estão disponíveis para qualquer pessoa consultar e baixar.
Conforme visível no próximo trecho de código, tcpsnitch
fornece a URL na qual seu rastreamento está disponível.
$ tcpsnitch curl google.com
Trace saved in /tmp/tmp.4ERKizKyU3.
Uploading trace....
Trace successfully uploaded at https://tcpsnitch.org/app_traces/20.
Trace archive will be imported shortly. Refresh this page in a few minutes...
Observe que são necessários vários minutos para importar o rastreamento (ou seja, extrair o arquivo de rastreamento e inserir todos os eventos no banco de dados). Uma vez importado, podem ser necessários mais alguns minutos para calcular a análise quantitativa do traço.
Finalmente, tcpsnitch
também permite extrair a opção de soquete TCP_INFO
em intervalos definidos pelo usuário e registrar um rastreamento .pcap
para cada soquete individual. Consulte a seção de uso para obter mais informações.
tcpsnitch
permite rastrear aplicativos em:
Como tcpsnitch
funciona interceptando chamadas para funções libc usando a variável de ambiente LD_PRELOAD
, o rastreamento não pode ser executado para aplicativos que estão vinculados estaticamente à libc.
Nota: No Linux, o Chrome (e qualquer aplicativo baseado em Chromium, como Electron, Opera, etc...) NÃO é compatível.
Para usuários que desejam rastrear aplicativos Android, role para baixo até a seção "Compilação para Android".
Testado no Ubuntu 16 e 14, Debian 8, Elementary 0.4, Mint 18
sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install make gcc gcc-multilib libc6-dev libc6-dev:i386 libjansson-dev libjansson-dev:i386 libpcap0.8 libpcap0.8:i386 libpcap0.8-dev
Testado no Fedora 25 e CentOS 7
sudo yum install make gcc glibc-devel glibc-devel.i686 libgcc libgcc.i686 libpcap-devel.x86_64 libpcap-devel.i686 jansson jansson.i686 && curl -O http://www.digip.org/jansson/releases/jansson-2.10.tar.bz2 && bunzip2 -c jansson-2.10.tar.bz2 | tar xf - && rm -f jansson-2.10.tar.bz2 && cd jansson-2.10 && ./configure && make && sudo make install && cd .. && rm -rf jansson-2.10
Construir e instalar:
./configure
make
sudo make install
Uso: tcpsnitch [<options>] <cmd> [<cmd_args>]
onde:
<options>
são opções tcpsnitch
<cmd>
é o comando para rastrear (obrigatório)<cmd_args>
são os argumentos de <cmd>
. Aqui está um exemplo simples com curl
e todas as opções padrão:
tcpsnitch curl google.com
Pode-se emitir tcpsnitch -h
para obter mais informações sobre as opções suportadas. Os mais importantes são os seguintes:
-b
e -u
são usados para extrair TCP_INFO
em intervalos definidos pelo usuário. Consulte a seção "Extraindo TCP_INFO
" para obter mais informações.-c
é usado para capturar rastros pcap
dos soquetes. Consulte a seção "Captura de pacotes" para obter mais informações.-a
e -k
são usados para rastrear aplicativos Android. Consulte a seção "Uso do Android" para obter mais informações.-n
desativa o upload automático de rastreamentos.-d
define o diretório no qual o rastreamento será gravado (em vez de um diretório aleatório em /tmp
).-f
define o nível de detalhamento dos logs salvos no arquivo. Por padrão, apenas mensagens WARN e ERROR são gravadas nos logs. Isso é útil principalmente para relatar um bug e depurar.-l
é semelhante a -f
, mas define o detalhamento do log em STDOUT, que por padrão mostra apenas mensagens de ERRO. Isso é usado para fins de depuração.-t
controla a frequência com que os eventos são despejados no arquivo. Por padrão, os eventos são gravados no arquivo a cada 1.000 milissegundos.-v
é bastante inútil no momento, mas deve colocar tcpsnitch
no modo detalhado no estilo strace
. Ainda a ser implementado (no momento exibe apenas nomes de eventos).TCP_INFO
-b <bytes>
e -u <usec>
permitem extrair o valor da opção de soquete TCP_INFO
para cada soquete em intervalos definidos pelo usuário. Observe que os valores TCP_INFO
aparecem como qualquer outro evento no rastreamento JSON do socekt.
-b <bytes>
, TCP_INFO
é registrado a cada <bytes>
enviados+recebidos no soquete.-u <usec>
, TCP_INFO
é registrado a cada <usec>
microssegundos.TCP_INFO
é registrado quando uma das duas condições é correspondida. Por padrão, esta opção está desativada. Observe também que tcpsnitch
só verifica essas condições quando uma função substituída é chamada.
A opção -c
ativa a captura de um rastreamento .pcap
para cada soquete. Observe que você precisa ter as permissões apropriadas para poder capturar o tráfego em uma interface (consulte man pcap
para obter mais informações sobre tais permissões).
Este recurso não está disponível para Android no momento.
O uso no Android é um processo de duas etapas, muito semelhante ao uso no Linux. Primeiro, configure tcpsnitch
e inicie o aplicativo a ser rastreado com as opções apropriadas e, em seguida, os rastreamentos são extraídos do dispositivo e copiados para a máquina host.
Todas as opções são suportadas no Android, exceto a opção -c
para capturar rastreamentos .pcap
.
Algumas etapas de configuração preliminar devem ser realizadas uma vez no dispositivo:
adb devices
e certifique-se de que seu telefone esteja visível (você deve ver device
na segunda coluna). Quando o dispositivo é acessível via adb
, o uso é quase o mesmo do Linux:
tcpsnitch
normal com a opção -a
para indicar que deseja rastrear um aplicativo no dispositivo Android conectado. Observe que o argumento <cmd>
deve corresponder ao nome de um pacote instalado no dispositivo por meio de um simples grep
. Por exemplo, para rastrear o aplicativo Firefox cujo nome de pacote é org.firefox.com
, pode-se emitir tcpsnitch -a firefox
. tcpsnitch
irá informá-lo sobre o pacote correspondente encontrado e iniciar imediatamente o aplicativo.tcpsnitch -k <package>
para encerrar o aplicativo e encerrar o processo de rastreamento. Os rastreamentos serão extraídos do dispositivo e salvos em seu disco em /tmp
antes de serem carregados em www.tcpsnitch.org. Importante: você deve reiniciar seu dispositivo Android para desativar completamente o rastreamento. Como tcpsnitch
usa propriedades do Android para configurar a biblioteca LD_PRELOAD
, e essas propriedades não podem ser desativadas, é necessário reiniciar o dispositivo para remover as propriedades (talvez alguém conheça uma solução melhor?).
Aqui está um exemplo completo para rastrear o Firefox:
$ tcpsnitch -a firefox
Found Android package: ' org.mozilla.firefox ' .
Uploading tcpsnitch library to /data/libtcpsnitch.so.0.1-arm.
Start package ' org.mozilla.firefox ' .
Execute ' ./tcpsnitch -k firefox ' to terminate the capture.
# INTERACTING WITH APPLICATION
$ tcpsnitch -k firefox
Found Android package: ' org.mozilla.firefox ' .
Pulling trace from Android device....
Trace saved in /tmp/tmp.MidCH9rm3x.
Uploading trace....
Trace successfully uploaded at https://tcpsnitch.org/app_traces/21.
Trace archive will be imported shortly. Refresh this page in a few minutes...
Observe que no caso de múltiplas correspondências para um pacote, o primeiro pacote correspondente será usado. Portanto, talvez você precise ser mais específico para evitar conflitos. Você pode executar adb shell pm list packages
para obter o nome de todos os pacotes instalados no seu dispositivo.
Observe também que um único dispositivo deve estar visível para adb
.
Para rastrear aplicativos Android, tcpsnitch
deve ser compilado com o Android Native Development Kit (NDK). A compilação é mais complicada e a configuração requer um dispositivo Android com root.
Basicamente, envolve as seguintes etapas:
libjansson
e libpcap
com o NDK e disponibilize as bibliotecas compiladas e os arquivos de cabeçalho para o conjunto de ferramentas independente.tcpsnitch
com o conjunto de ferramentas independente e prepare o dispositivo Android.A seção a seguir fornece um exemplo complexo que orienta você em todas as etapas.
Algumas suposições:
<NDK_PATH>
.<TCPSNITCH_PATH>
.Primeiro, vamos definir algumas variáveis:
export TCPSNITCH=<TCPSNITCH_PATH>
export NDK=<NDK_PATH>
# Where the standalone toolchain WILL be created
export TOOLCHAIN=<TOOLCHAIN_PATH>
Agora, vamos começar gerando um conjunto de ferramentas independente para um dispositivo ARM executando Android API 23 (versão 6.0, Marshmallow). O mapeamento entre versões do Android e níveis de API na página seguinte.
$NDK/build/tools/make_standalone_toolchain.py --arch arm --api 23 --install-dir $TOOLCHAIN
Agora devemos compilar libjansson
e libpcap
com o NDK. Quando isso for feito, devemos instalar seus arquivos de cabeçalho e as bibliotecas compiladas no "sysroot" em nosso conjunto de ferramentas independente.
Vamos começar com libjansson
:
git clone https://github.com/akheron/jansson && cd jansson
# Configuration file which we don't use, we may leave it empty
touch src/jansson_private_config.h
sed -i 's/BUILD_SHARED_LIBRARY/BUILD_STATIC_LIBRARY/g' Android.mk
$NDK/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
cp obj/local/armeabi/libjansson.a $TOOLCHAIN/sysroot/usr/lib/
cp src/jansson.h android/jansson_config.h $TOOLCHAIN/sysroot/usr/include/
cd .. && rm -rf jansson
Agora, vamos abordar libpcap
:
git clone https://github.com/the-tcpdump-group/libpcap && cd libpcap
export CC=$TOOLCHAIN/bin/arm-linux-androideabi-gcc
./configure --host=arm-linux --with-pcap=linux --prefix=/usr
# You will need to install some missing dependencies (e.g. `flex` & `bison`)
sudo apt-get install flex bison
# Reissue ./configure untill all dependencies are met
./configure --host=arm-linux --with-pcap=linux --prefix=/usr
# Compile && install in toolchain
make && sudo make install DESTDIR=$TOOLCHAIN/sysroot
cd .. && rm -rf libpcap
Agora estamos prontos para compilar tcpsnitch
:
# First, let's fix the buggy `tcp.h` header from the NDK
sed -i 's/include <linux/tcp.h>/include <sys/cdefs.h>n#include <linux/tcp.h>/g' $TOOLCHAIN/sysroot/usr/include/netinet/tcp.h
# Configure the compiler
export CC_ANDROID=$TOOLCHAIN/bin/arm-linux-androideabi-gcc
# Build & install tcpsnitch
make android && sudo make install
Você está pronto para ir! Consulte a seção de uso do Android para saber como iniciar o rastreamento de aplicativos.
Um recurso interessante do vinculador dinâmico do Linux ( ld.so
) é a capacidade de vincular bibliotecas compartilhadas especificadas pelo usuário antes das bibliotecas especificadas na lista de dependências de um programa. Este recurso pode ser controlado com a variável de ambiente LD_PRELOAD
que contém uma lista (possivelmente vazia) de bibliotecas adicionais especificadas pelo usuário. Em particular, esta variável LD_PRELOAD
pode forçar o vinculador dinâmico a vincular uma biblioteca compartilhada especificada pelo usuário antes da biblioteca libc
. Como resultado, qualquer função definida nesta biblioteca especificada pelo usuário tem precedência sobre uma função com a mesma assinatura definida em libc
.
A implicação aqui é que ele permite interceptar chamadas para funções wrapper de chamadas do sistema. Precisamos apenas adicionar uma biblioteca compartilhada personalizada que redefina essas funções de wrappers de chamada do sistema para LD_PRELOAD
. Essa biblioteca shim intercepta de forma transparente as chamadas de função libc
e executa algum processamento antes de chamar as funções wrapper libc
originais.
wrong ELF class
? Nada de ruim, isso pode ser ignorado. A biblioteca compartilhada tcpsnitch
é compilada para arquiteturas de 32 e 64 bits. Ao rastrear um comando, ambas as bibliotecas são carregadas na variável de ambiente LD_PRELOAD
, pois não há uma maneira fácil de conhecer a arquitetura do binário de comando (geralmente é um script de shell executando outro binário). O vinculador dinâmico então se encarrega de carregar a biblioteca compatível e ignorar a segunda (mas ainda gera um erro).
Venha discutir sobre tcpsnitch
em https://gitter.im/Tcpsnitch.
O e-mail do autor é gregory.vanderschueren[at]gmail.com