tcpsnitch
es una herramienta de seguimiento diseñada para investigar las interacciones entre una aplicación y la pila TCP/IP. tcpsnitch
ejecuta el comando especificado hasta que sale e intercepta todas las llamadas a funciones libc en los sockets de Internet.
Para comenzar suavemente, se puede ejecutar el siguiente comando para rastrear el programa curl
:
$ tcpsnitch curl google.com
Para cada socket de Internet abierto, tcpsnitch
crea una lista ordenada de llamadas a funciones (una invocación de función se denomina evento en el resto de este documento). Para cada evento, tcpsnitch
registra los argumentos, el valor de retorno y diversa información, como la marca de tiempo actual o la identificación del hilo. Específicamente, un evento connect()
podría verse así en un seguimiento de socket:
{
"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 "
}
}
}
Los seguimientos de socket se escriben en archivos de texto donde cada línea es un objeto JSON que representa un único evento. Al jefe de tal rastro le podría gustar esto:
{ "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 un solo comando puede bifurcar múltiples procesos (y tcpsnitch
sigue a las bifurcaciones), todos los seguimientos de sockets que pertenecen a un proceso determinado se reúnen en un directorio, que lleva el nombre del proceso rastreado. Dentro de dicho directorio, los seguimientos de sockets reciben nombres según el orden en que fueron abiertos por el proceso.
De forma predeterminada, los seguimientos se guardan en un directorio aleatorio en /tmp
y se cargan automáticamente en www.tcpsnitch.org, una plataforma diseñada para centralizar, visualizar y analizar los seguimientos. Tenga en cuenta que todos los seguimientos cargados son públicos y están disponibles para que cualquiera pueda consultarlos y descargarlos.
Como se ve en el siguiente fragmento de código, tcpsnitch
le proporciona la URL en la que está disponible su seguimiento.
$ 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...
Tenga en cuenta que se necesitan varios minutos para importar el seguimiento (es decir, extraer el archivo de seguimiento e insertar todos los eventos en la base de datos). Una vez importado, es posible que se necesiten varios minutos más para calcular el análisis cuantitativo del rastro.
Finalmente, tcpsnitch
también permite extraer la opción de socket TCP_INFO
a intervalos definidos por el usuario y registrar un seguimiento .pcap
para cada socket individual. Consulte la sección de uso para obtener más información.
tcpsnitch
permite rastrear aplicaciones en:
Como tcpsnitch
funciona interceptando llamadas a funciones de libc utilizando la variable de entorno LD_PRELOAD
, no se puede realizar el seguimiento de aplicaciones que están vinculadas estáticamente con libc.
Nota: En Linux, se sabe que Chrome (y cualquier aplicación basada en Chromium como Electron, Opera, etc.) NO es compatible.
Para los usuarios que quieran rastrear aplicaciones de Android, desplácese hacia abajo hasta la sección "Compilación para Android".
Probado en Ubuntu 16 y 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
Probado en Fedora 25 y 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>]
donde:
<options>
son opciones tcpsnitch
<cmd>
es el comando para rastrear (obligatorio)<cmd_args>
son los argumentos de <cmd>
. Aquí hay un ejemplo simple con curl
y todas las opciones predeterminadas:
tcpsnitch curl google.com
Se puede ejecutar tcpsnitch -h
para obtener más información sobre las opciones admitidas. Los más importantes son los siguientes:
-b
y -u
se utilizan para extraer TCP_INFO
en intervalos definidos por el usuario. Consulte la sección "Extracción de TCP_INFO
" para obtener más información.-c
se utiliza para capturar rastros pcap
de los sockets. Consulte la sección "Captura de paquetes" para obtener más información.-a
y -k
se utilizan para rastrear aplicaciones de Android. Consulte la sección "Uso de Android" para obtener más información.-n
desactivar la carga automática de trazas.-d
establece el directorio en el que se escribirá el seguimiento (en lugar de un directorio aleatorio en /tmp
).-f
establece el nivel de detalle de los registros guardados en el archivo. De forma predeterminada, sólo se escriben en los registros los mensajes de ADVERTENCIA y ERROR. Esto será útil principalmente para informar un error y depurar.-l
es similar a -f
pero establece la detalle del registro en STDOUT, que de forma predeterminada solo muestra mensajes de ERROR. Esto se utiliza con fines de depuración.-t
controla la frecuencia con la que los eventos se vuelcan al archivo. De forma predeterminada, los eventos se escriben en el archivo cada 1000 milisegundos.-v
es bastante inútil en este momento, pero se supone que debe poner tcpsnitch
en modo detallado al estilo de strace
. Aún por implementar (por el momento solo muestra nombres de eventos).TCP_INFO
-b <bytes>
y -u <usec>
permiten extraer el valor de la opción de socket TCP_INFO
para cada socket en intervalos definidos por el usuario. Tenga en cuenta que los valores TCP_INFO
aparecen como cualquier otro evento en el seguimiento JSON del socekt.
-b <bytes>
, TCP_INFO
se registra cada <bytes>
enviado+recibido en el socket.-u <usec>
, TCP_INFO
se registra cada <usec>
microsegundos.TCP_INFO
se registra cuando se cumple cualquiera de las dos condiciones. De forma predeterminada, esta opción está desactivada. También tenga en cuenta que tcpsnitch
solo verifica estas condiciones cuando se llama a una función anulada.
La opción -c
activa la captura de un rastro .pcap
para cada socket. Tenga en cuenta que debe tener los permisos adecuados para poder capturar el tráfico en una interfaz (consulte man pcap
para obtener más información sobre dichos permisos).
Esta función no está disponible para Android por el momento.
El uso en Android es un proceso de dos pasos, muy similar al uso en Linux. Primero, tcpsnitch
configura e inicia la aplicación a rastrear con las opciones apropiadas, luego los rastros se extraen del dispositivo y se copian en la máquina host.
Todas las opciones son compatibles con Android, excepto la opción -c
para capturar rastros .pcap
.
Se deben realizar algunos pasos de configuración preliminares una vez en el dispositivo:
adb devices
y asegúrese de que su teléfono esté visible (debería ver device
en la segunda columna). Cuando se puede acceder al dispositivo a través de adb
, el uso es casi el mismo que en Linux:
tcpsnitch
normal con la opción -a
para indicar que desea rastrear una aplicación en el dispositivo Android conectado. Tenga en cuenta que el argumento <cmd>
debe coincidir con el nombre de un paquete instalado en el dispositivo mediante un simple grep
. Por ejemplo, para rastrear la aplicación Firefox cuyo nombre de paquete es org.firefox.com
, se puede emitir tcpsnitch -a firefox
. tcpsnitch
le informará del paquete coincidente encontrado e inmediatamente iniciará la aplicación.tcpsnitch -k <package>
para finalizar la aplicación y finalizar el proceso de seguimiento. Los rastros se extraerán del dispositivo y se guardarán en su disco en /tmp
antes de cargarlos en www.tcpsnitch.org. Importante: debes reiniciar tu dispositivo Android para desactivar completamente el rastreo. Como tcpsnitch
usa propiedades de Android para configurar la biblioteca LD_PRELOAD
, y estas propiedades no se pueden desarmar, se debe reiniciar el dispositivo para eliminar las propiedades (¿tal vez alguien conozca una solución mejor?).
Aquí hay un ejemplo completo para rastrear 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...
Tenga en cuenta que en caso de que haya varias coincidencias para un paquete, se utilizará el primer paquete coincidente. Por lo tanto, es posible que deba ser más específico para evitar conflictos. Puede ejecutar adb shell pm list packages
para obtener el nombre de todos los paquetes instalados en su dispositivo.
También tenga en cuenta que un solo dispositivo debe ser visible para adb
.
Para rastrear aplicaciones de Android, tcpsnitch
debe compilarse con el kit de desarrollo nativo de Android (NDK). La compilación es más complicada y la configuración requiere un dispositivo Android rooteado.
Básicamente, implica los siguientes pasos:
libjansson
y libpcap
con el NDK y haga que las bibliotecas compiladas y los archivos de encabezado estén disponibles para la cadena de herramientas independiente.tcpsnitch
con la cadena de herramientas independiente y prepare el dispositivo Android.La siguiente sección ofrece un ejemplo complejo que le guía por todos los pasos.
Algunas suposiciones:
<NDK_PATH>
.<TCPSNITCH_PATH>
.Primero, definamos algunas variables:
export TCPSNITCH=<TCPSNITCH_PATH>
export NDK=<NDK_PATH>
# Where the standalone toolchain WILL be created
export TOOLCHAIN=<TOOLCHAIN_PATH>
Ahora, comencemos generando una cadena de herramientas independiente para un dispositivo ARM que ejecuta Android API 23 (versión 6.0, Marshmallow). El mapeo entre las versiones de Android y los niveles de API en la página siguiente.
$NDK/build/tools/make_standalone_toolchain.py --arch arm --api 23 --install-dir $TOOLCHAIN
Ahora debemos compilar libjansson
y libpcap
con el NDK. Una vez hecho esto, debemos instalar sus archivos de encabezado y las bibliotecas compiladas en el "sysroot" de nuestra cadena de herramientas independiente.
Comencemos con 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
Ahora, abordemos 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
Ahora estamos listos 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
¡Estás listo para partir! Consulte la sección de uso de Android para saber cómo iniciar el seguimiento de aplicaciones.
Una característica interesante del vinculador dinámico de Linux ( ld.so
) es la capacidad de vincular bibliotecas compartidas especificadas por el usuario antes que las bibliotecas especificadas en la lista de dependencias de un programa. Esta característica se puede controlar con la variable de entorno LD_PRELOAD
que contiene una lista (posiblemente vacía) de bibliotecas adicionales especificadas por el usuario. En particular, esta variable LD_PRELOAD
puede forzar al vinculador dinámico a vincular una biblioteca compartida especificada por el usuario antes que la biblioteca libc
. Como resultado, cualquier función definida en esta biblioteca especificada por el usuario tiene prioridad sobre una función con la misma firma definida en libc
.
La implicación aquí es que permite interceptar llamadas a funciones contenedoras de llamadas del sistema. Simplemente tenemos que agregar una biblioteca compartida personalizada que redefina estas funciones contenedoras de llamadas del sistema a LD_PRELOAD
. Una biblioteca de shim de este tipo intercepta de forma transparente las llamadas a la función libc
y realiza algún procesamiento antes de llamar a las funciones contenedoras libc
originales.
wrong ELF class
? Nada malo, estos pueden ignorarse. La biblioteca compartida tcpsnitch
está compilada para arquitecturas de 32 y 64 bits. Al rastrear un comando, ambas bibliotecas se cargan en la variable de entorno LD_PRELOAD
ya que no hay una manera fácil de conocer la arquitectura del binario del comando (a menudo es un script de shell que ejecuta otro binario). Luego, el vinculador dinámico se encarga de cargar la biblioteca compatible e ignora la segunda (pero aún arroja un error).
Venga a discutir sobre tcpsnitch
en https://gitter.im/Tcpsnitch.
El correo electrónico del autor es gregory.vanderschueren[arroba]gmail.com