在分析TCP數據包時,理解TCP協議的工作原理和報文格式是關鍵。TCP是一種面向連接的、提供可靠的、端到端的字節流傳輸服務。其頭部結構包括源端口、目標端口、序列號、確認應答號等字段。序列號是在建立連接時由計算機生成的隨機數作為初始值,每發送一次數據,就累加一次該數據字節數的大小,而確認應答號是指下一次期望收到的數據的序列號。
抓包和分析數據包是理解TCP/IP協議的重要手段。Wireshark是最知名的網絡通訊抓包分析工具,可以截取各種網絡封包并顯示詳細信息。通過抓包和分析數據包,我們可以深入理解TCP幀格式及“TCP三次握手”,進一步提高理論聯系實踐的能力。
例如,我們選擇一個TCP數據包進行分析,在數據包詳細信息面板中,我們可以看到TCP協議的詳細信息,包括TCP標志位、序號、確認號、窗口大小等信息。此外,我們還可以查看數據包的十六進制數據。
總的來說,TCP數據包的分析原理主要涉及對TCP協議的理解、使用相關工具進行數據包抓取和分析以及理解TCP頭部結構等方面。處理細節則包括如何從大量的數據包中篩選出需要的信息,如何理解和解析TCP協議的各種字段等。
理解TCP協議工作原理和報文
TCP(Transmission Control Protocol,傳輸控制協議)是一種面向連接的、可靠的、基于字節流的傳輸層通信協議。它在互聯網協議(IP)網絡上通過TCP/IP協議棧進行工作。
TCP的工作原理可以概括為以下幾個方面:
1.建立連接:在源主機和目標主機之間建立連接,以便進行數據傳輸。
2.編號和排序數據段:在連接建立后,源主機按順序發送數據段,每個數據段都有一個編號,以便目標主機重新排序。
3.確認和重傳:目標主機對接收到的數據段進行確認,如果數據段丟失或損壞,則發送請求重傳的信號。
4.流量控制:TCP使用窗口機制來控制數據的流量,以避免因接收方緩沖區滿而造成的數據丟失。
5.關閉連接:當數據傳輸完成時,連接將被關閉。
TCP的報文格式包括以下幾個部分:
1.源端口和目標端口:指示數據從哪個進程來,到哪個進程去。
2.序號:表示從TCP源端向TCP目標端發送的數據字節流中的第一個數據字節。
3.確認號:包含目標端所期望收到源端的下一個數據字節的序號。
4.TCP首部長度:表示該TCP頭部有多少個32位(以4個字節為單位)。
5.標志位:包括緊急標志(URG)、確認標志(ACK)、推送標志(PSH)、可重用標志(RST)、同步標志(SYN)和終止標志(FIN)等。
6.窗口大小:表示接收方可以接收的最大數據量。
7.校驗和:用于校驗數據的有效性。
8.緊急指針:當URG標志位為1時,表示緊急數據的位置。
9.數據部分:包含實際傳輸的數據。
TCP的報文格式根據不同的用途和選項可能會有所不同,但以上是基本組成部分。理解TCP報文格式對于理解TCP協議的工作原理和實現可靠傳輸至關重要。
TCP怎么實現可靠傳輸
TCP通過以下幾種機制來實現可靠傳輸:
-
序列號和確認機制:每個TCP包都有一個序列號,接收方通過發送端的確認信息來確認收到的數據,并告知發送方下一個期望接收的數據序號。
-
超時重傳:發送方在發送數據后啟動一個計時器,如果在規定時間內沒有收到接收方的確認信息,就會認為數據丟失,然后重新發送數據。
-
滑動窗口協議:接收方使用滑動窗口來控制數據的流量和接收速度。滑動窗口可以指定接收方現在能夠接受的最大數據量,發送方需要確保在窗口范圍內發送數據。
-
流量控制:TCP使用基于接收方通告的窗口大小來控制數據發送速率,確保發送方不會以過快的速度發送數據,使接收方無法處理。
-
擁塞控制:TCP通過調整發送方的發送速率來避免網絡擁塞。通過監測網絡的擁塞程度并及時降低發送速率,使得整個網絡能夠維持在一個合理的狀態。
以上是TCP實現可靠傳輸的主要機制,通過這些機制,TCP可以在不可靠的網絡環境下實現可靠的數據傳輸。
tcpflow - 分析網絡流量
tcpflow是一個功能強大的、基于命令行的免費開源工具,用于在Unix之類的系統(如Linux)上分析網絡流量。它可以捕獲通過TCP連接接收或傳輸的數據,并存儲在文件中供以后分析,采用的格式便于協議分析和調試。
tcpflow的工作原理是基于LBL Packet Capture Library,支持豐富的過濾條件,能夠捕獲網絡或存儲文件中的數據包,并按照正常順序重建數據流。每一條TCP流都會被存儲到獨立的文件中,方便以后分析。與tcpdump相比,tcpflow會重新構建真實的數據流,并且會分開存儲。
當你想要使用tcpflow
命令來捕獲和輸出TCP流量時,以下是一些相關的例子:
- 基本使用:
這個命令將讀取一個PCAP文件(例如Wireshark捕獲到的網絡數據包),并將TCP流量保存到相應的文件或屏幕上。tcpflow -r pcap_file.pcap
默認情況下,tcpflow將所有捕獲的數據存儲在表單中具有名稱的文件中(如果使用某些選項(如時間戳 ),這可能會有所不同)。現在讓我們做一個目錄列表,看看是否在任何文件中捕獲了tcp流。
還生成了一個XML報告,含有關于該程序的信息,比如它是如何編譯的、它在哪臺計算機上運行以及每條TCP連接的記錄。
-
指定輸出目錄:
tcpflow -o output_directory -r pcap_file.pcap
這個命令將指定TCP流量的輸出目錄。每個流將被保存為單獨的文件,文件名由IP地址和端口組成。
-
只顯示請求或響應:
tcpflow -r pcap_file.pcap 'src host X.X.X.X' tcpflow -r pcap_file.pcap 'dst host X.X.X.X'
這兩個命令將只捕獲某個特定源IP地址或目標IP地址的請求或響應流量。
-
過濾特定端口:
tcpflow -r pcap_file.pcap 'tcp port 80'
這個命令將只捕獲TCP端口為80的流量。
-
高亮顯示輸出:
tcpflow -C -r pcap_file.pcap
這個命令將以彩色高亮的方式顯示輸出結果,以便更容易閱讀和理解。
-
指定接口名稱
要從特定網絡接口捕獲數據包,請使用-i標志指定接口名稱。tcpflow -i eth0 port 80
有關更多信息和用法選項,請參見tcpflow手冊頁。
man tcpflow
Linux C/C++ 分析網絡流量(十六進制TCP數據包分析)
編寫主要目的是舊的tcpflow不提供十六進制控制臺輸出。Simson L.Garfinkel的新tcpflow需要一個libcairo-dev,這需要對我的服務器系統有很大的x11依賴性。我認為tcpflow是一個命令行工具,我真的不需要pdf報告。編譯新的tcpflow很困難,而且根本不能禁用libcairo。
...
void hexdump(const char *buffer, size_t size)
{
...for (;;) {const char *line_end = line_start + LINE_CHAR_COUNT > buffer_end? buffer_end: line_start + LINE_CHAR_COUNT;if (line_start == line_end) {break;}
...// hex part
...// blankcount += snprintf(output + count, sizeof(output), " ");// acsii partfor (const char *p = line_start; p < line_end; ++p) {count += snprintf(output + count, sizeof(output), "%c",isprint(*p) ? *p : '.');} printf("%s\n", output);line_start = line_end;}
}void process_ip_packet(const struct timeval *ts,const char *buffer, size_t size)
{if (size < sizeof(struct ip)) {die("invalid ip packet length");}...process_tcp_packet(ts,ntohl(ip_header->ip_src.s_addr),ntohl(ip_header->ip_dst.s_addr),buffer + ip_header_len,ip_len - ip_header_len);
}void process_tcp_packet(const struct timeval *ts,u_int32_t src_addr, u_int32_t dst_addr,const char *buffer, size_t size)
{
...if (size < sizeof(struct tcphdr)) {die("invalid tcp packet length");}...if (tcp_header_len >= size) {return;}if (g_option_color) {if (src_addr != last_src_addr || dst_addr != last_dst_addr ||src_port != last_src_port || dst_port != last_dst_port) {last_src_addr = src_addr;last_dst_addr = dst_addr;last_src_port = src_port;last_dst_port = dst_port;current_color = !current_color;}printf("%s", colors[current_color]);}if (g_option_display_header) {
...strftime(format_time, sizeof(format_time), "%Y-%m-%d %H:%M:%S", &tm);printf("%s.%ld %d.%d.%d.%d:%d => %d.%d.%d.%d:%d\n",format_time, ts->tv_usec / 1000,(src_addr & 0xff000000) >> 24,(src_addr & 0x00ff0000) >> 16,(src_addr & 0x0000ff00) >> 8,src_addr & 0x000000ff,src_port,(dst_addr & 0xff000000) >> 24,(dst_addr & 0x00ff0000) >> 16,(dst_addr & 0x0000ff00) >> 8,dst_addr & 0x000000ff,dst_port);}// print hex datahexdump(buffer + tcp_header_len, size - tcp_header_len);if (g_option_color) {printf("%s", "\033[0m");}printf("\n");
}...
void print_usage(const char *prog_name)
{fprintf(stderr, "usage: %s [-Ce] <pcap_file> \n", prog_name);fprintf(stderr, " -C do not display packet description\n");fprintf(stderr, " -e output in alternating colors\n");
}void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{if (h->len <= g_datalink_header_length) {die("invalid datalink packet length");}process_ip_packet(&h->ts, (const char *)bytes + g_datalink_header_length,h->len - g_datalink_header_length);
}int main(int argc, char *argv[])
{
...while ((opt = getopt(argc, argv, "Ce")) != -1) {switch (opt) {case 'C':g_option_display_header = false;break;case 'e':g_option_color = true;break;default:print_usage(argv[0]);exit(1);}}if (optind >= argc) {print_usage(argv[0]);exit(1);}const char *pcap_file_name = argv[optind];pcap_t *pd = pcap_open_offline(pcap_file_name, errbuf);if (NULL == pd) {die("%s", errbuf);}int dlt = pcap_datalink(pd);if (DLT_NULL == dlt) {g_datalink_header_length = 4;} else if (DLT_RAW == dlt) {g_datalink_header_length = 0;} else if (DLT_EN10MB == dlt || DLT_IEEE802 == dlt) {g_datalink_header_length = 14;} else if (DLT_PPP == dlt) {g_datalink_header_length = 4;} else if (DLT_LINUX_SLL == dlt) {g_datalink_header_length = 16;} else {fprintf(stderr, "unknown datalink type\n");return -1;}struct bpf_program filter;if (pcap_compile(pd, &filter, "tcp", 1, 0) != 0) {die("%s", pcap_geterr(pd));}if (pcap_setfilter(pd, &filter) != 0) {die("%s", pcap_geterr(pd));}if (pcap_loop(pd, -1, packet_handler, NULL) != 0) {die("%s", pcap_geterr(pd));}...
}
If you need the complete source code, please add the WeChat number (c17865354792)
運行結果:
Wireshark抓包展示效果:
總結
在進行十六進制TCP數據包分析時,我們首先需要捕獲網絡流量,獲取TCP數據包。這可以通過網絡抓包工具(如Wireshark)實現。然后,對捕獲到的數據包進行解析,提取出TCP頭部的各個字段和數據部分。
Welcome to follow WeChat official account【程序猿編碼】