一般遇到莫名其妙的网络问题时,第一反应就是抓包分析,然而抓取的数据包真的可信吗?
抓包会不会帮倒忙,让事情更加微妙从而火上浇油呢?我让你抓,抓个毛线啊!
完全正确,只要做一点手脚即可,这次我用systemtap。
我希望的效果是,只要你抓包,显示的都是127.0.0.1到本机的包,哈哈,无论怎样都是这种数据包,从而迷惑程序员的判断。
stap的脚本代码如下:
#! /usr/bin/stap -g
%{
#include
#include
#include
%}
function reset_skb(skb:long, type:long)
%{
struct sk_buff *skb2 = (struct sk_buff *)STAP_ARG_skb;
struct ethhdr *eth;
struct iphdr *hdr;
eth = (struct ethhdr *)skb_mac_header(skb2);
if (skb2->pkt_type == PACKET_OUTGOING)
eth = (struct ethhdr *)(skb2->data);
if (eth->h_proto != 8) return;
hdr = (struct iphdr *)(((char *)eth) + sizeof(struct ethhdr));
if (STAP_ARG_type == 0) {
// rehat系列可用,否则就借用mark之类的IP层以下不用的字段(bridge除外)。
skb2->rh_reserved1 = hdr->saddr;
skb2->rh_reserved2 = hdr->daddr;
hdr->saddr = 0x100007f;
hdr->daddr = 0x100007f;
} else {
hdr->saddr = skb2->rh_reserved1;
hdr->daddr = skb2->rh_reserved2;
}
%}
probe kernel.function("tpacket_rcv")
{
reset_skb($skb, 0);
}
// probe return是必须的,下面会讲
probe kernel.function("tpacket_rcv").return
{
reset_skb($skb, 1);
}
其实,我本来是可以用Rootkit的方式来完成这件事的,并且隐藏掉Rootkit存在的事实,这些东西我之前都写过,也没有必要在赘述了。简单起见,我用stap。
我的意思是,使用stap的语法会更加高效且安全,在编译期就避开了各种规避panic的debug工作,毕竟人生苦短啊。
然而,我上面的脚本依然使用了本地化的内联C代码,只怪我术业不精吧。
Java调用C的Jni,C调用汇编的内联汇编,stap调用C的本例,bpf调用内核API,都属于同一回事。
下面举一个纯粹点的例子,完全用stap的语法来改点东西:
#! /usr/bin/stap
probe kernel.function("tpacket_rcv")
{
len = @cast($skb, "sk_buff")->len;
if (len > 500) {
@cast($skb, "sk_buff")->len = 10;
}
}
很简单,我把数据包从协议头就截断了,显然抓取的ping -s 1000大包都无法识别
# 直连机器 ping 100.100.100.1 本机抓包如下
[root@localhost ~]# tcpdump -i enp0s9 -n -v
tcpdump: listening on enp0s9, link-type EN10MB (Ethernet), capture size 262144 bytes
15:32:18.379180 IP [|ip]
15:32:19.403423 IP [|ip]
15:32:20.427958 IP [|ip]
15:32:21.451891 IP [|ip]
^C
虽然抓包被截断了,可是为什么ping也ping不通了呢?
因为Linux内核抓包处理函数并没有copy一份新的skb,而仅仅是increase了skb的引用计数而已,因此在tpacket_rcv中修改了数据包,那就修改了数据包本身,所以说,如果仅仅希望影响抓包效果,那么在抓包函数return的时候,一定要把包改回来,这也就是为什么要probe return的原因。
如果你想丢包,那就把数据包改坏吧:
哦,现在是下班时间,我想给经理beat electric discourse.
浙江温州皮鞋湿,下雨进水不会胖。