tcpsnitch
是一個追蹤工具,旨在調查應用程式和 TCP/IP 堆疊之間的交互作用。 tcpsnitch
執行指定的命令,直到退出並攔截 Internet 套接字上的所有 libc 函數呼叫。
為了溫和地啟動,可以執行以下命令來追蹤curl
程式:
$ tcpsnitch curl google.com
對於每個開啟的網路套接字, tcpsnitch
都會建立一個函數呼叫的有序列表(函數呼叫在本文檔的其餘部分中稱為事件)。對於每個事件, tcpsnitch
都會記錄參數、傳回值和各種信息,例如當前時間戳記或執行緒 ID。具體來說, connect()
事件在套接字追蹤中可能如下所示:
{
"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 "
}
}
}
套接字追蹤寫入文字文件,其中每一行都是表示單一事件的 JSON 物件。這樣的跟蹤的頭部可能是這樣的:
{ "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 " }}}
由於單一命令可能派生多個進程( tcpsnitch
跟隨派生),因此屬於給定進程的所有套接字追蹤都放在一個目錄中,並以追蹤的進程命名。在這樣的目錄中,套接字追蹤是根據進程打開它們的順序來命名的。
預設情況下,追蹤記錄保存在/tmp
下的隨機目錄中,並自動上傳到 www.tcpsnitch.org,這是一個旨在集中、視覺化和分析追蹤記錄的平台。請注意,所有上傳的追蹤都是公開的,任何人都可以查閱和下載。
正如下一個程式碼片段所示, tcpsnitch
為您提供了可用追蹤的 URL。
$ 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...
請注意,導入追蹤需要幾分鐘(即提取追蹤存檔並將所有事件插入資料庫)。導入後,可能還需要幾分鐘來計算跡線的定量分析。
最後, tcpsnitch
還允許按使用者定義的時間間隔提取TCP_INFO
套接字選項,並記錄每個單獨套接字的.pcap
追蹤。請參閱使用部分以獲取更多資訊。
tcpsnitch
允許追蹤以下應用程式:
由於tcpsnitch
透過使用LD_PRELOAD
環境變數攔截對 libc 函數的呼叫來運作,因此無法對與 libc 靜態連結的應用程式執行追蹤。
注意:在 Linux 上,Chrome(以及任何基於 Chromium 的應用程序,如 Electron、Opera 等)已知不相容。
對於想要追蹤 Android 應用程式的用戶,請向下捲動至「Android 編譯」部分。
在 Ubuntu 16 和 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
在 Fedora 25 和 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
建置與安裝:
./configure
make
sudo make install
用法: tcpsnitch [<options>] <cmd> [<cmd_args>]
其中:
<options>
是tcpsnitch
選項<cmd>
是要追蹤的命令(強制)<cmd_args>
是<cmd>
的參數。這是一個使用curl
和所有預設選項的簡單範例:
tcpsnitch curl google.com
您可以發出tcpsnitch -h
來獲取有關受支援選項的更多資訊。最重要的是以下幾點:
-b
和-u
用於按使用者定義的時間間隔提取TCP_INFO
。有關詳細信息,請參閱“提取TCP_INFO
”部分。-c
用於捕捉套接字的pcap
痕跡。有關詳細信息,請參閱“資料包捕獲”部分。-a
和-k
用於追蹤 Android 應用程式。有關詳細信息,請參閱“Android 使用”部分。-n
停用追蹤的自動上傳。-d
設定將寫入追蹤的目錄(而不是/tmp
中的隨機目錄)。-f
設定儲存到檔案的日誌的詳細程度。預設情況下,僅將 WARN 和 ERROR 訊息寫入日誌。這主要用於報告錯誤和調試。-l
與-f
類似,但設定 STDOUT 上的日誌詳細程度,預設僅顯示錯誤訊息。這用於調試目的。-t
控制將事件轉儲到檔案的頻率。預設情況下,事件每 1000 毫秒寫入檔案一次。-v
目前沒什麼用,但它應該以strace
的風格將tcpsnitch
置於詳細模式。仍有待實施(目前僅顯示事件名稱)。TCP_INFO
-b <bytes>
和-u <usec>
允許按使用者定義的時間間隔提取每個套接字的TCP_INFO
套接字選項的值。請注意, TCP_INFO
值與 socekt 的 JSON 追蹤中的任何其他事件一樣顯示。
-b <bytes>
,在套接字上傳送和接收的每個<bytes>
都會記錄TCP_INFO
。-u <usec>
,每<usec>
微秒記錄一次TCP_INFO
。TCP_INFO
。預設此選項為關閉。另請注意, tcpsnitch
僅在呼叫重寫函數時檢查這些條件。
-c
選項啟動對每個套接字的.pcap
追蹤的捕捉。請注意,您需要具有適當的權限才能捕獲介面上的流量(有關此類權限的更多信息,請參閱man pcap
)。
此功能目前不適用於 Android。
Android 上的使用過程分為兩步,與 Linux 上的使用非常相似。首先, tcpsnitch
設定並啟動要使用適當選項進行追蹤的應用程序,然後從裝置中提取追蹤並將其複製到主機。
Android 支援所有選項,但用於捕獲.pcap
追蹤的-c
選項除外。
必須在設備上完成一些初步設定步驟:
adb devices
並確保您的手機可見(您應該在第二列中看到device
)。當透過adb
存取設備時,用法幾乎與 Linux 上相同:
-a
常規tcpsnitch
命令以指示您要追蹤所連接的 Android 裝置上的應用程式。請注意, <cmd>
參數必須與透過簡單grep
安裝在裝置上的套件的名稱相符。例如,要追蹤套件名為org.firefox.com
的 Firefox 應用程序,可以發出tcpsnitch -a firefox
。 tcpsnitch
將通知您找到匹配的軟體包並立即啟動應用程式。tcpsnitch -k <package>
來終止應用程式並終止追蹤進程。追蹤將從裝置中提取並保存在磁碟上的/tmp
中,然後上傳到 www.tcpsnitch.org。重要提示:您必須重新啟動 Android 裝置才能完全停用追蹤。由於tcpsnitch
使用 Android 屬性來設定LD_PRELOAD
庫,而這些屬性無法取消設置,因此必須重新啟動裝置才能刪除這些屬性(也許有人知道更好的解決方案?)。
以下是追蹤 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...
請注意,如果一個包有多個匹配項,則將使用第一個匹配的包。因此,您可能需要更加具體以避免衝突。您可以執行adb shell pm list packages
來取得設備上安裝的所有軟體包的名稱。
另請注意,單一裝置必須對adb
可見。
為了追蹤 Android 應用程序, tcpsnitch
必須使用 Android 本機開發工具包 (NDK) 進行編譯。編譯過程比較複雜,安裝需要有 root 權限的 Android 裝置。
基本上,它涉及以下步驟:
libjansson
和libpcap
,並使編譯後的函式庫和頭檔可用於獨立工具鏈。tcpsnitch
並準備 Android 裝置。以下部分提供了一個複雜的範例,將引導您完成所有步驟。
一些假設:
<NDK_PATH>
。<TCPSNITCH_PATH>
。首先,我們定義幾個變數:
export TCPSNITCH=<TCPSNITCH_PATH>
export NDK=<NDK_PATH>
# Where the standalone toolchain WILL be created
export TOOLCHAIN=<TOOLCHAIN_PATH>
現在,我們首先為運行 Android API 23(版本 6.0,Marshmallow)的 ARM 裝置產生獨立工具鏈。 Android 版本和 API 等級之間的對應關係如下頁所示。
$NDK/build/tools/make_standalone_toolchain.py --arch arm --api 23 --install-dir $TOOLCHAIN
我們現在必須使用 NDK 編譯libjansson
和libpcap
。完成後,我們必須在獨立工具鏈的「sysroot」中安裝它們的頭檔和編譯後的函式庫。
讓我們從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
現在,讓我們來解決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
我們現在準備編譯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
你準備好了!有關如何啟動追蹤應用程式的信息,請參閱 Android 使用部分。
Linux 動態連結器 ( ld.so
) 的一個有趣功能是能夠在程式依賴項清單中指定的程式庫之前連結使用者指定的共用程式庫。此功能可透過LD_PRELOAD
環境變數進行控制,該變數包含附加使用者指定庫的清單(可能為空)。特別是,此LD_PRELOAD
變數可能會強制動態連結器在libc
庫之前連結使用者指定的共享庫。因此,在此使用者指定的庫中定義的任何函數都優先於libc
中定義的具有相同簽名的函數。
這裡的含義是它允許攔截對系統呼叫包裝函數的呼叫。我們只需新增一個自訂共用程式庫,將這些系統呼叫包裝函數重新定義為LD_PRELOAD
。這樣的 shim 函式庫會透明地攔截libc
函數調用,並在調用原始libc
包裝函數之前執行一些處理。
wrong ELF class
錯誤是什麼?沒什麼不好的,這些可以忽略。 tcpsnitch
共享庫針對 32 位元和 64 位元體系結構進行編譯。追蹤命令時,兩個庫都會載入到LD_PRELOAD
環境變數中,因為沒有簡單的方法可以了解命令二進位檔案的體系結構(通常是執行另一個二進位檔案的 shell 腳本)。然後,動態連結器負責載入相容庫並忽略第二個庫(但仍然拋出錯誤)。
歡迎來 https://gitter.im/Tcpsnitch 討論tcpsnitch
。
作者的電子郵件是 gregory.vanderschueren[at]gmail.com