tcpsnitch
ist ein Tracing-Tool zur Untersuchung der Interaktionen zwischen einer Anwendung und dem TCP/IP-Stack. tcpsnitch
führt den angegebenen Befehl aus, bis er beendet wird und alle libc-Funktionsaufrufe auf Internet-Sockets abfängt.
Um sanft zu beginnen, kann man den folgenden Befehl ausführen, um das curl
-Programm zu verfolgen:
$ tcpsnitch curl google.com
Für jeden geöffneten Internet-Socket erstellt tcpsnitch
eine geordnete Liste von Funktionsaufrufen (ein Funktionsaufruf wird im weiteren Verlauf dieses Dokuments als Ereignis bezeichnet). Für jedes Ereignis zeichnet tcpsnitch
die Argumente, den Rückgabewert und verschiedene Informationen wie den aktuellen Zeitstempel oder die Thread-ID auf. Konkret könnte ein connect()
-Ereignis in einem Socket-Trace so aussehen:
{
"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 "
}
}
}
Socket-Traces werden in Textdateien geschrieben, wobei jede Zeile ein JSON-Objekt ist, das ein einzelnes Ereignis darstellt. Der Kopf einer solchen Spur könnte so aussehen:
{ "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 " }}}
Da ein einzelner Befehl mehrere Prozesse verzweigen kann (und tcpsnitch
Verzweigungen folgt), werden alle zu einem bestimmten Prozess gehörenden Socket-Traces in einem Verzeichnis zusammengefasst, das nach dem verfolgten Prozess benannt ist. Innerhalb eines solchen Verzeichnisses werden Socket-Traces basierend auf der Reihenfolge benannt, in der sie vom Prozess geöffnet wurden.
Standardmäßig werden Traces in einem zufälligen Verzeichnis unter /tmp
gespeichert und automatisch auf www.tcpsnitch.org hochgeladen, einer Plattform zur Zentralisierung, Visualisierung und Analyse der Traces. Beachten Sie, dass alle hochgeladenen Spuren öffentlich sind und für jedermann zum Einsehen und Herunterladen verfügbar sind.
Wie im nächsten Codeausschnitt zu sehen ist, gibt Ihnen tcpsnitch
die URL an, unter der Ihr Trace verfügbar ist.
$ 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...
Beachten Sie, dass das Importieren des Trace (dh Extrahieren des Trace-Archivs und Einfügen aller Ereignisse in die Datenbank) mehrere Minuten dauert. Nach dem Import kann es mehrere Minuten dauern, bis die quantitative Analyse der Spur berechnet ist.
Schließlich ermöglicht tcpsnitch
auch das Extrahieren der TCP_INFO
Socket-Option in benutzerdefinierten Intervallen und das Aufzeichnen eines .pcap
Trace für jeden einzelnen Socket. Weitere Informationen finden Sie im Abschnitt „Verwendung“.
tcpsnitch
ermöglicht die Verfolgung von Anwendungen auf:
Da tcpsnitch
Aufrufe von libc-Funktionen mithilfe der Umgebungsvariablen LD_PRELOAD
abfängt, kann keine Ablaufverfolgung für Anwendungen durchgeführt werden, die statisch mit libc verknüpft sind.
Hinweis: Unter Linux ist Chrome (und jede Chromium-basierte App wie Electron, Opera usw.) bekanntermaßen NICHT kompatibel.
Für Benutzer, die Android-Anwendungen verfolgen möchten, scrollen Sie nach unten zum Abschnitt „Zusammenstellung für Android“.
Getestet auf Ubuntu 16 und 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
Getestet auf Fedora 25 und 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
Erstellen und installieren:
./configure
make
sudo make install
Verwendung: tcpsnitch [<options>] <cmd> [<cmd_args>]
wobei:
<options>
sind tcpsnitch
Optionen<cmd>
ist der zu verfolgende Befehl (obligatorisch)<cmd_args>
sind die Argumente von <cmd>
. Hier ist ein einfaches Beispiel mit curl
und allen Standardoptionen:
tcpsnitch curl google.com
Man kann tcpsnitch -h
eingeben, um weitere Informationen über die unterstützten Optionen zu erhalten. Die wichtigsten sind die folgenden:
-b
und -u
werden zum Extrahieren TCP_INFO
in benutzerdefinierten Intervallen verwendet. Weitere Informationen finden Sie im Abschnitt „ TCP_INFO
extrahieren“.-c
wird zum Erfassen von pcap
Spuren der Sockets verwendet. Weitere Informationen finden Sie im Abschnitt „Paketerfassung“.-a
und -k
werden zum Verfolgen von Android-Anwendungen verwendet. Weitere Informationen finden Sie im Abschnitt „Android-Nutzung“.-n
deaktiviert das automatische Hochladen von Traces.-d
legt das Verzeichnis fest, in das der Trace geschrieben wird (anstelle eines zufälligen Verzeichnisses in /tmp
).-f
legt den Ausführlichkeitsgrad der in der Datei gespeicherten Protokolle fest. Standardmäßig werden nur WARN- und ERROR-Meldungen in Protokolle geschrieben. Dies ist vor allem zum Melden eines Fehlers und zum Debuggen nützlich.-l
ähnelt -f
, legt jedoch die Protokollausführlichkeit auf STDOUT fest, wodurch standardmäßig nur FEHLERmeldungen angezeigt werden. Dies wird zu Debugzwecken verwendet.-t
steuert die Häufigkeit, mit der Ereignisse in die Datei übertragen werden. Standardmäßig werden Ereignisse alle 1000 Millisekunden in die Datei geschrieben.-v
ist im Moment ziemlich nutzlos, aber es soll tcpsnitch
im Stil von strace
in den ausführlichen Modus versetzen. Muss noch implementiert werden (derzeit werden nur Ereignisnamen angezeigt).TCP_INFO
extrahieren -b <bytes>
und -u <usec>
ermöglichen das Extrahieren des Werts der TCP_INFO
Socket-Option für jeden Socket in benutzerdefinierten Intervallen. Beachten Sie, dass die TCP_INFO
Werte wie jedes andere Ereignis im JSON-Trace des Socekts erscheinen.
-b <bytes>
wird TCP_INFO
alle am Socket gesendeten und empfangenen <bytes>
aufgezeichnet.-u <usec>
wird TCP_INFO
alle <usec>
Mikrosekunden aufgezeichnet.TCP_INFO
aufgezeichnet, wenn eine der beiden Bedingungen erfüllt ist. Standardmäßig ist diese Option deaktiviert. Beachten Sie außerdem, dass tcpsnitch
diese Bedingungen nur überprüft, wenn eine überschriebene Funktion aufgerufen wird.
Die Option -c
aktiviert die Erfassung eines .pcap
Trace für jeden Socket. Beachten Sie, dass Sie über die entsprechenden Berechtigungen verfügen müssen, um den Datenverkehr auf einer Schnittstelle erfassen zu können (weitere Informationen zu solchen Berechtigungen finden Sie unter man pcap
).
Diese Funktion ist derzeit nicht für Android verfügbar.
Die Nutzung unter Android ist ein zweistufiger Prozess, der der Nutzung unter Linux sehr ähnlich ist. Zuerst richten Sie tcpsnitch
ein und starten die zu verfolgende Anwendung mit den entsprechenden Optionen. Anschließend werden die Traces vom Gerät abgerufen und auf den Host-Computer kopiert.
Alle Optionen werden auf Android unterstützt, mit Ausnahme der Option -c
zum Erfassen von .pcap
Traces.
Auf dem Gerät müssen einmalig einige vorbereitende Einrichtungsschritte durchgeführt werden:
adb devices
aus und stellen Sie sicher, dass Ihr Telefon sichtbar ist (das device
sollte in der zweiten Spalte angezeigt werden). Wenn das Gerät über adb
erreichbar ist, ist die Verwendung fast die gleiche wie unter Linux:
tcpsnitch
mit der Option -a
aus, um anzugeben, dass Sie eine Anwendung auf dem verbundenen Android-Gerät verfolgen möchten. Beachten Sie, dass das Argument <cmd>
mit dem Namen eines Pakets übereinstimmen muss, das über ein einfaches grep
auf dem Gerät installiert wurde. Um beispielsweise die Firefox-Anwendung zu verfolgen, deren Paketname org.firefox.com
ist, kann man tcpsnitch -a firefox
ausgeben. tcpsnitch
informiert Sie über das gefundene passende Paket und startet sofort die Anwendung.tcpsnitch -k <package>
ein, um die Anwendung zu beenden und den Ablaufverfolgungsprozess zu beenden. Die Spuren werden vom Gerät abgerufen und auf Ihrer Festplatte in /tmp
gespeichert, bevor sie auf www.tcpsnitch.org hochgeladen werden. Wichtig: Sie müssen Ihr Android-Gerät neu starten, um die Nachverfolgung vollständig zu deaktivieren. Da tcpsnitch
Android-Eigenschaften zum Einrichten der LD_PRELOAD
-Bibliothek verwendet und diese Eigenschaften nicht deaktiviert werden können, muss das Gerät neu gestartet werden, um die Eigenschaften zu entfernen (vielleicht kennt jemand eine bessere Lösung?).
Hier ist ein vollständiges Beispiel für die Ablaufverfolgung von 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...
Beachten Sie, dass bei mehreren Übereinstimmungen für ein Paket das erste übereinstimmende Paket verwendet wird. Möglicherweise müssen Sie daher konkreter vorgehen, um Konflikte zu vermeiden. Sie können adb shell pm list packages
ausführen, um die Namen aller auf Ihrem Gerät installierten Pakete abzurufen.
Beachten Sie außerdem, dass ein einzelnes Gerät für adb
sichtbar sein muss.
Um Android-Anwendungen zu verfolgen, muss tcpsnitch
mit dem Android Native Development Kit (NDK) kompiliert werden. Die Kompilierung ist aufwändiger und die Einrichtung erfordert ein gerootetes Android-Gerät.
Im Wesentlichen umfasst es die folgenden Schritte:
libjansson
und libpcap
mit dem NDK und stellen Sie die kompilierten Bibliotheken und Header-Dateien der eigenständigen Toolchain zur Verfügung.tcpsnitch
mit der eigenständigen Toolchain und bereiten Sie das Android-Gerät vor.Der folgende Abschnitt enthält ein komplexes Beispiel, das Sie durch alle Schritte führt.
Ein paar Annahmen:
<NDK_PATH>
extrahiert.<TCPSNITCH_PATH>
.Definieren wir zunächst einige Variablen:
export TCPSNITCH=<TCPSNITCH_PATH>
export NDK=<NDK_PATH>
# Where the standalone toolchain WILL be created
export TOOLCHAIN=<TOOLCHAIN_PATH>
Beginnen wir nun mit der Generierung einer eigenständigen Toolchain für ein ARM-Gerät, auf dem Android API 23 (Version 6.0, Marshmallow) ausgeführt wird. Die Zuordnung zwischen Android-Versionen und API-Ebenen finden Sie auf der folgenden Seite.
$NDK/build/tools/make_standalone_toolchain.py --arch arm --api 23 --install-dir $TOOLCHAIN
Jetzt müssen wir sowohl libjansson
als auch libpcap
mit dem NDK kompilieren. Wenn dies erledigt ist, müssen wir ihre Header-Dateien und die kompilierten Bibliotheken im „Sysroot“ unserer eigenständigen Toolchain installieren.
Beginnen wir mit 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
Kommen wir nun zu 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
Wir sind jetzt bereit, tcpsnitch
zu kompilieren:
# 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
Sie können loslegen! Informationen zum Starten der Verfolgung von Anwendungen finden Sie im Abschnitt zur Android-Nutzung.
Eine interessante Funktion des dynamischen Linux-Linkers ( ld.so
) ist die Möglichkeit, vom Benutzer angegebene gemeinsam genutzte Bibliotheken vor den Bibliotheken zu verknüpfen, die in der Liste der Abhängigkeiten eines Programms angegeben sind. Diese Funktion kann mit der Umgebungsvariablen LD_PRELOAD
gesteuert werden, die eine (möglicherweise leere) Liste zusätzlicher benutzerdefinierter Bibliotheken enthält. Insbesondere kann diese LD_PRELOAD
Variable den dynamischen Linker dazu zwingen, eine vom Benutzer angegebene gemeinsam genutzte Bibliothek vor der libc
-Bibliothek zu verknüpfen. Daher hat jede in dieser benutzerdefinierten Bibliothek definierte Funktion Vorrang vor einer in libc
definierten Funktion mit derselben Signatur.
Dies bedeutet, dass dadurch Aufrufe von Systemaufruf-Wrapper-Funktionen abgefangen werden können. Wir müssen lediglich eine benutzerdefinierte gemeinsam genutzte Bibliothek hinzufügen, die diese Systemaufruf-Wrapper-Funktionen zu LD_PRELOAD
neu definiert. Eine solche Shim-Bibliothek fängt dann die libc
-Funktionsaufrufe transparent ab und führt einige Verarbeitungsvorgänge durch, bevor sie die ursprünglichen libc
Wrapper-Funktionen aufruft.
wrong ELF class
Klassenfehler? Nichts Schlimmes, diese können ignoriert werden. Die gemeinsam genutzte Bibliothek tcpsnitch
ist sowohl für die 32-Bit- als auch für die 64-Bit-Architektur kompiliert. Beim Verfolgen eines Befehls werden beide Bibliotheken in die Umgebungsvariable LD_PRELOAD
geladen, da es keine einfache Möglichkeit gibt, die Architektur der Befehlsbinärdatei zu ermitteln (häufig handelt es sich um ein Shell-Skript, das eine andere Binärdatei ausführt). Der dynamische Linker kümmert sich dann um das Laden der kompatiblen Bibliothek und ignoriert die zweite (gibt aber immer noch einen Fehler aus).
Kommen Sie und diskutieren Sie über tcpsnitch
unter https://gitter.im/Tcpsnitch.
Die E-Mail-Adresse des Autors lautet gregory.vanderschueren[at]gmail.com