打印 TCP 数据包数据
Print TCP Packet Data
在 TCP 通信中,当数据包从以太网传输到网络 (IP) 层时,我想打印该数据包中存在的数据?
我正在研究 linux。
我得到了一些信息,它可以在 linux 内核代码的帮助下完成,即在 linux NAT 防火墙代码中。但是我在哪里可以获得内核源代码?这些编码在哪里完成?
您想使用 tcpdump
工具检查线路上的 TCP 数据包。
您没有说明您要查看的数据类型。
This 会在端口 53 上为 DNS
转储流量
tcpdump -vvv -s 0 -l -n port 53
This page 有很好的概述。
在内核级别不确定。
您可以使用 libpcap
实用程序捕获数据包并对其进行解析。例如:
您可以像这样使用 tcpdump:
tcpdump -vvv -s 0 -l -n port 80 -i NameOfYourDevice
或更好:
tcpdump -i NameOfYourDevice -a -x -n port 80
如何从 TCP 数据包打印数据
下面是一个完全满足您需要的示例:挂钩接收到的 TCP 数据包并打印其有效负载。如果你想从接收到的数据包中打印一些其他信息(比如二进制数据),你只需要稍微修改一下这个评论下的部分:
/* ----- Print all needed information from received TCP packet ------ */
如果你需要追踪发送的包而不是接收的包,你可以替换这一行:
nfho.hooknum = NF_INET_PRE_ROUTING;
这个:
nfho.hooknum = NF_INET_POST_ROUTING;
保存下一个文件并发出make
命令来构建内核模块。然后执行 sudo insmod print_tcp.ko
加载它。之后,您将能够使用 dmesg
命令查看嗅探信息。如果你想卸载你的模块,运行 sudo rmmod print_tcp
命令。
print_tcp.c:
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#define PTCP_WATCH_PORT 80 /* HTTP port */
static struct nf_hook_ops nfho;
static unsigned int ptcp_hook_func(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph; /* IPv4 header */
struct tcphdr *tcph; /* TCP header */
u16 sport, dport; /* Source and destination ports */
u32 saddr, daddr; /* Source and destination addresses */
unsigned char *user_data; /* TCP data begin pointer */
unsigned char *tail; /* TCP data end pointer */
unsigned char *it; /* TCP data iterator */
/* Network packet is empty, seems like some problem occurred. Skip it */
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb); /* get IP header */
/* Skip if it's not TCP packet */
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
tcph = tcp_hdr(skb); /* get TCP header */
/* Convert network endianness to host endiannes */
saddr = ntohl(iph->saddr);
daddr = ntohl(iph->daddr);
sport = ntohs(tcph->source);
dport = ntohs(tcph->dest);
/* Watch only port of interest */
if (sport != PTCP_WATCH_PORT)
return NF_ACCEPT;
/* Calculate pointers for begin and end of TCP packet data */
user_data = (unsigned char *)((unsigned char *)tcph + (tcph->doff * 4));
tail = skb_tail_pointer(skb);
/* ----- Print all needed information from received TCP packet ------ */
/* Show only HTTP packets */
if (user_data[0] != 'H' || user_data[1] != 'T' || user_data[2] != 'T' ||
user_data[3] != 'P') {
return NF_ACCEPT;
}
/* Print packet route */
pr_debug("print_tcp: %pI4h:%d -> %pI4h:%d\n", &saddr, sport,
&daddr, dport);
/* Print TCP packet data (payload) */
pr_debug("print_tcp: data:\n");
for (it = user_data; it != tail; ++it) {
char c = *(char *)it;
if (c == '[=12=]')
break;
printk("%c", c);
}
printk("\n\n");
return NF_ACCEPT;
}
static int __init ptcp_init(void)
{
int res;
nfho.hook = (nf_hookfn *)ptcp_hook_func; /* hook function */
nfho.hooknum = NF_INET_PRE_ROUTING; /* received packets */
nfho.pf = PF_INET; /* IPv4 */
nfho.priority = NF_IP_PRI_FIRST; /* max hook priority */
res = nf_register_hook(&nfho);
if (res < 0) {
pr_err("print_tcp: error in nf_register_hook()\n");
return res;
}
pr_debug("print_tcp: loaded\n");
return 0;
}
static void __exit ptcp_exit(void)
{
nf_unregister_hook(&nfho);
pr_debug("print_tcp: unloaded\n");
}
module_init(ptcp_init);
module_exit(ptcp_exit);
MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Module for printing TCP packet data");
MODULE_LICENSE("GPL");
生成文件:
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
module:
$(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 clean
.PHONY: module clean
else
MODULE = print_tcp.o
CFLAGS_$(MODULE) := -DDEBUG
obj-m := $(MODULE)
endif
说明
我会推荐你阅读这本书:[4]。特别是您对接下来的章节感兴趣:
- 第 11 章:第 4 层协议
- TCP(传输控制协议)
- 使用 TCP 从网络层 (L3) 接收数据包
- 使用 TCP 发送数据包
- 第 9 章:Netfilter
- Netfilter 挂钩
如何获取Linux内核源代码
您可以使用您喜欢的一种方式获取内核源代码:
来自 kernel.org (more specifically from kernel/git/torvalds/linux.git), using Git 的香草内核。例如。如果您需要k3.13,可以通过以下方式完成:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
$ cd linux/
$ git checkout v3.13
来自您的发行版的内核源代码。例如。在 Debian 中,您只需安装 linux-source
软件包(源代码将安装到 /usr/src
)。对于 Ubuntu,请参阅 these instructions。
详情:
[1] How to get TCP header from sk_buff
[2] Network flow control in Linux kernel
[3] Writing Loadable Kernel Modules using netfilter hooks
[4]"Linux Kernel Networking: Implementation and Theory" by Rami Rosen
[5] How to access data/payload from tcphdr
更新
where the hook captures packets for this example? In other words, is it upon TCP stack so that I don't need to take care of packet losing, reordering, etc.?
Netfilter 挂钩在 ip_rcv()
函数 (here) 中调用,因此您基本上是在 IPv4 层(OSI 中的网络层)工作。所以我相信数据包丢失处理、数据包重新排序等 尚未 在该 netfilter 挂钩中处理。
查看下一个链接以了解见解:
如果你想在传输层 (TCP) 上挂钩数据包 -- netfilter 不足以完成此任务,因为它只在网络层 (IPv4) 中工作。
我不知道你所说的 "transferred from ethernet to network layer" 是什么意思。你的意思是当内核停止处理第 2 层 headers 并移动到第 3 层 headers 时?
我将让您随意使用这些信息。如果要过滤和拦截第 3 层数据包(IP 数据包),Linux 上有两个主要选项。首先,您可以编写一个 netfilter 挂钩(为此您需要内核编程知识和一些 C 技能)。这基本上是一个内核模块,因此您必须自己编写第 4 层过滤器。 Linux 的库中有 struct tcphdr
和 struct ip
,只有 google,您可以找到包含定义。
我不建议在您希望它运行良好的环境中使用的其他选项是 queue 使用 iptables 或 nftables 将数据包发送到用户空间。这更容易编程,因为您可以直接从 cli 使用 IPtables 和 nftables 挂钩,而无需担心学习内核模块编程。一个 iptables 钩子的例子是 iptables -A PREROUTING -p tcp --dport 8000 -j NFQUEUE --queue-num 0
。这会将在 PREROUTING 中捕获的任何发往端口 8000 的 tcp 数据包路由到 netfilter queue 编号 0(这基本上只是一个用户空间套接字)。您将需要为您的发行版安装 libnetfilter_queue
或等效的数据包,然后您可以捕获和修改此过滤器捕获的各种语言的数据包。我个人知道并用 C、Python、Rust 和 Golang 编写了这些脚本(尽管 golang 在速度方面有点 bleh 而 Python 是 Python,但是 scapy有一些很酷的数据包操作的东西)。给你的提示:如果你以这种方式修改第 4 层数据包,校验和会很麻烦。在我将它设置为零后,我无法让 netfilter 自动计算校验和,我建议为 IP 和 TCP 在线找到一个有效的校验和计算函数,因为你已经在写一些不应该在生产中使用的东西。
如果你想拦截第 2 层帧(以太网帧),我知道自内核 2.x 以来,ebtables 就存在于 Linux 中。但是,这没有我所知道的简单 NFQUEUE 类型的解决方案,因此您不得不编写自己的代码。我相信它有用户空间 APIs 用于创建过滤器和修改数据包,所以你可能在那里很幸运。如果用户空间 API 不工作,写一个内核模块很有趣 :)。
感谢@Sam Protsenko 的回答。但对于内核版本 >= 4.13,函数 nf_register_hook(&nfho)
和 nf_unregister_hook(&nfho)
已替换为 nf_register_net_hook(&init__net, &nfho)
和 nf_unregister_net_hook(&init__net, &nfho)
.
如果您想尝试代码,请检查您的内核版本并根据您的情况修改代码。
此外,对于初学者,您可能想要apt install sparse
,它是Makefile中使用的内核代码错误检查程序。
在 TCP 通信中,当数据包从以太网传输到网络 (IP) 层时,我想打印该数据包中存在的数据?
我正在研究 linux。
我得到了一些信息,它可以在 linux 内核代码的帮助下完成,即在 linux NAT 防火墙代码中。但是我在哪里可以获得内核源代码?这些编码在哪里完成?
您想使用 tcpdump
工具检查线路上的 TCP 数据包。
您没有说明您要查看的数据类型。
This 会在端口 53 上为 DNS
转储流量tcpdump -vvv -s 0 -l -n port 53
This page 有很好的概述。
在内核级别不确定。
您可以使用 libpcap
实用程序捕获数据包并对其进行解析。例如:
您可以像这样使用 tcpdump:
tcpdump -vvv -s 0 -l -n port 80 -i NameOfYourDevice
或更好:
tcpdump -i NameOfYourDevice -a -x -n port 80
如何从 TCP 数据包打印数据
下面是一个完全满足您需要的示例:挂钩接收到的 TCP 数据包并打印其有效负载。如果你想从接收到的数据包中打印一些其他信息(比如二进制数据),你只需要稍微修改一下这个评论下的部分:
/* ----- Print all needed information from received TCP packet ------ */
如果你需要追踪发送的包而不是接收的包,你可以替换这一行:
nfho.hooknum = NF_INET_PRE_ROUTING;
这个:
nfho.hooknum = NF_INET_POST_ROUTING;
保存下一个文件并发出make
命令来构建内核模块。然后执行 sudo insmod print_tcp.ko
加载它。之后,您将能够使用 dmesg
命令查看嗅探信息。如果你想卸载你的模块,运行 sudo rmmod print_tcp
命令。
print_tcp.c:
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#define PTCP_WATCH_PORT 80 /* HTTP port */
static struct nf_hook_ops nfho;
static unsigned int ptcp_hook_func(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph; /* IPv4 header */
struct tcphdr *tcph; /* TCP header */
u16 sport, dport; /* Source and destination ports */
u32 saddr, daddr; /* Source and destination addresses */
unsigned char *user_data; /* TCP data begin pointer */
unsigned char *tail; /* TCP data end pointer */
unsigned char *it; /* TCP data iterator */
/* Network packet is empty, seems like some problem occurred. Skip it */
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb); /* get IP header */
/* Skip if it's not TCP packet */
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
tcph = tcp_hdr(skb); /* get TCP header */
/* Convert network endianness to host endiannes */
saddr = ntohl(iph->saddr);
daddr = ntohl(iph->daddr);
sport = ntohs(tcph->source);
dport = ntohs(tcph->dest);
/* Watch only port of interest */
if (sport != PTCP_WATCH_PORT)
return NF_ACCEPT;
/* Calculate pointers for begin and end of TCP packet data */
user_data = (unsigned char *)((unsigned char *)tcph + (tcph->doff * 4));
tail = skb_tail_pointer(skb);
/* ----- Print all needed information from received TCP packet ------ */
/* Show only HTTP packets */
if (user_data[0] != 'H' || user_data[1] != 'T' || user_data[2] != 'T' ||
user_data[3] != 'P') {
return NF_ACCEPT;
}
/* Print packet route */
pr_debug("print_tcp: %pI4h:%d -> %pI4h:%d\n", &saddr, sport,
&daddr, dport);
/* Print TCP packet data (payload) */
pr_debug("print_tcp: data:\n");
for (it = user_data; it != tail; ++it) {
char c = *(char *)it;
if (c == '[=12=]')
break;
printk("%c", c);
}
printk("\n\n");
return NF_ACCEPT;
}
static int __init ptcp_init(void)
{
int res;
nfho.hook = (nf_hookfn *)ptcp_hook_func; /* hook function */
nfho.hooknum = NF_INET_PRE_ROUTING; /* received packets */
nfho.pf = PF_INET; /* IPv4 */
nfho.priority = NF_IP_PRI_FIRST; /* max hook priority */
res = nf_register_hook(&nfho);
if (res < 0) {
pr_err("print_tcp: error in nf_register_hook()\n");
return res;
}
pr_debug("print_tcp: loaded\n");
return 0;
}
static void __exit ptcp_exit(void)
{
nf_unregister_hook(&nfho);
pr_debug("print_tcp: unloaded\n");
}
module_init(ptcp_init);
module_exit(ptcp_exit);
MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Module for printing TCP packet data");
MODULE_LICENSE("GPL");
生成文件:
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
module:
$(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 clean
.PHONY: module clean
else
MODULE = print_tcp.o
CFLAGS_$(MODULE) := -DDEBUG
obj-m := $(MODULE)
endif
说明
我会推荐你阅读这本书:[4]。特别是您对接下来的章节感兴趣:
- 第 11 章:第 4 层协议
- TCP(传输控制协议)
- 使用 TCP 从网络层 (L3) 接收数据包
- 使用 TCP 发送数据包
- TCP(传输控制协议)
- 第 9 章:Netfilter
- Netfilter 挂钩
如何获取Linux内核源代码
您可以使用您喜欢的一种方式获取内核源代码:
来自 kernel.org (more specifically from kernel/git/torvalds/linux.git), using Git 的香草内核。例如。如果您需要k3.13,可以通过以下方式完成:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git $ cd linux/ $ git checkout v3.13
来自您的发行版的内核源代码。例如。在 Debian 中,您只需安装
linux-source
软件包(源代码将安装到/usr/src
)。对于 Ubuntu,请参阅 these instructions。
详情:
[1] How to get TCP header from sk_buff
[2] Network flow control in Linux kernel
[3] Writing Loadable Kernel Modules using netfilter hooks
[4]"Linux Kernel Networking: Implementation and Theory" by Rami Rosen
[5] How to access data/payload from tcphdr
更新
where the hook captures packets for this example? In other words, is it upon TCP stack so that I don't need to take care of packet losing, reordering, etc.?
Netfilter 挂钩在 ip_rcv()
函数 (here) 中调用,因此您基本上是在 IPv4 层(OSI 中的网络层)工作。所以我相信数据包丢失处理、数据包重新排序等 尚未 在该 netfilter 挂钩中处理。
查看下一个链接以了解见解:
如果你想在传输层 (TCP) 上挂钩数据包 -- netfilter 不足以完成此任务,因为它只在网络层 (IPv4) 中工作。
我不知道你所说的 "transferred from ethernet to network layer" 是什么意思。你的意思是当内核停止处理第 2 层 headers 并移动到第 3 层 headers 时?
我将让您随意使用这些信息。如果要过滤和拦截第 3 层数据包(IP 数据包),Linux 上有两个主要选项。首先,您可以编写一个 netfilter 挂钩(为此您需要内核编程知识和一些 C 技能)。这基本上是一个内核模块,因此您必须自己编写第 4 层过滤器。 Linux 的库中有 struct tcphdr
和 struct ip
,只有 google,您可以找到包含定义。
我不建议在您希望它运行良好的环境中使用的其他选项是 queue 使用 iptables 或 nftables 将数据包发送到用户空间。这更容易编程,因为您可以直接从 cli 使用 IPtables 和 nftables 挂钩,而无需担心学习内核模块编程。一个 iptables 钩子的例子是 iptables -A PREROUTING -p tcp --dport 8000 -j NFQUEUE --queue-num 0
。这会将在 PREROUTING 中捕获的任何发往端口 8000 的 tcp 数据包路由到 netfilter queue 编号 0(这基本上只是一个用户空间套接字)。您将需要为您的发行版安装 libnetfilter_queue
或等效的数据包,然后您可以捕获和修改此过滤器捕获的各种语言的数据包。我个人知道并用 C、Python、Rust 和 Golang 编写了这些脚本(尽管 golang 在速度方面有点 bleh 而 Python 是 Python,但是 scapy有一些很酷的数据包操作的东西)。给你的提示:如果你以这种方式修改第 4 层数据包,校验和会很麻烦。在我将它设置为零后,我无法让 netfilter 自动计算校验和,我建议为 IP 和 TCP 在线找到一个有效的校验和计算函数,因为你已经在写一些不应该在生产中使用的东西。
如果你想拦截第 2 层帧(以太网帧),我知道自内核 2.x 以来,ebtables 就存在于 Linux 中。但是,这没有我所知道的简单 NFQUEUE 类型的解决方案,因此您不得不编写自己的代码。我相信它有用户空间 APIs 用于创建过滤器和修改数据包,所以你可能在那里很幸运。如果用户空间 API 不工作,写一个内核模块很有趣 :)。
感谢@Sam Protsenko 的回答。但对于内核版本 >= 4.13,函数 nf_register_hook(&nfho)
和 nf_unregister_hook(&nfho)
已替换为 nf_register_net_hook(&init__net, &nfho)
和 nf_unregister_net_hook(&init__net, &nfho)
.
如果您想尝试代码,请检查您的内核版本并根据您的情况修改代码。
此外,对于初学者,您可能想要apt install sparse
,它是Makefile中使用的内核代码错误检查程序。