关于抓包和协议栈的处理
抓包,是一个静态的过程,把包抓上来,然后靠你的眼睛去看,剩下来的事情就靠你的眼神以及你对协议栈的了解了,找我老婆公司的一个人过来看一个数据包,估计没戏,但是找我公司我座位周围随便一个人过来看,都能说点什么...正如你坐上飞机,你就把生命交给飞行员一样,如果你不懂协议,抓到的包对你一点用都没有,你并不能依靠其它什么,此时的飞行员就是你自己!我不明白为何所有人都喜欢抓包,即使看不懂,也依然抓取好几G的包...当然,我并不责怪那些必须带点现场数据回来的人。我在想,为何不把包导入协议栈,让协议栈处理呢?此时,你可以依赖的东西就更多了,你可以用snort,你可以用iptables的LOG,你可以用深度解析...工具会帮你动态分析。虽然也有很多工具可以帮你分析你抓到的数据包,但是这种行为却不是动态的。抓包和现场分析的行为区别在程序员的角度正和看代码和调试代码之间的区别一样!
抓包机制
抓包既然只是为了给你提供一个静态的数据,那就是抓到就好了。这种事情在Linux中是通过PACKET套接字实现的,在Linux的链路层,数据包传给了所有的PACKET套接字,然后这些数据包(或许经过了BPF过滤)被直接扔给了用户态的诸如tcpdump之类的程序。记住,tcpdump程序抓到的数据包是直接链路层的包,没有经过协议栈的任何处理,比如没有经过NAT,没有经过路由...实际上,如果数据包是恰好路过本地,并且本机的该网卡又启动了promisc混杂模式,那么不是发到本网卡的数据包是不可能被导入到本机协议栈处理的,你能做的仅仅是将其静态抓取,顶多保存成一个万恶的pcap文件。导入协议栈
把过路的数据包导入协议栈,这是有实际用途的。比如如果本机是一个镜像机,所有的数据包都发往本机,我觉得仅仅是把数据包抓取再静态分析肯定不好,让它们直接经过协议栈处理,岂不更好?然而,导入本地协议栈有一个前提,那就是数据帧的目标MAC地址是接收网卡的MAC地址(仅仅以以太网举例)。为此,我需要做的仅仅是注册一个PACKET类型的数据包处理器,处理函数中将数据包的pkt_type改为PACKET_HOST即可,熟悉Linux内核协议栈代码的应该知道,在网络层的ip_rcv中会首先丢弃数据包pkt_type不是PACKET_HOST的所有数据包。代码很简单:
#include <linux/module.h>
#include <linux/netpoll.h>
#include <linux/moduleparam.h>
MODULE_PARM_DESC(capdev, " capdev=ethX"
"指定在哪个设备上启动导入本地协议栈的抓包机制");
MODULE_PARM_DESC(mark, " mark=value"
"为导入本地协议栈的数据包打上的mark");
static __u32 mark;
module_param(mark, int, 0);
static char devname[256];
struct net_device *capdev;
module_param_string(capdev, devname, 256, 0);
int
cappkg_func (struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt,
struct net_device *orig_dev)
{
if (dev != capdev /*不在capdev链表中,目前只有一个*/) {
goto release_skb;
}
if (/*skb->pkt_type == PACKET_OUTGOING ||*/
//本机发出的包,受制于fib_validate_source!
//因此需要将源地址SNAT成一个非本机地址!
skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
skb->mark = mark;
return netif_receive_skb(skb);
}
release_skb:
kfree_skb(skb);
return NET_RX_SUCCESS;
}
static struct
packet_type cappkg_proto = {
.type = __constant_htons(ETH_P_ALL),
.func = cappkg_func,
};
int
init_module (void)
{
int ret = 0;
capdev = dev_get_by_name(&init_net, devname);
if (!capdev) {
ret = -1;
goto err;
}
dev_add_pack(&cappkg_proto);
err:
return ret;
}
void
cleanup_module(void)
{
dev_remove_pack(&cappkg_proto);
}
MODULE_DESCRIPTION("将仅仅是经过本地的数据包导入到本地协议栈处理");
MODULE_AUTHOR("Wangran <marywangran@126.com>");
MODULE_LICENSE("GPL");
代码的用途
1.镜像端口
我不是曾经一直纠结于如何让Linux实现镜像端口吗?此代码可以实现,不同于诸位前辈或者几年前的我自己(我是我自己的前辈!)的实现,我并不是直接硬编码将数据包导入到ethX,而是通过策略路由将其导入,数据包被我的模块处理函数复制了一份,然后打上了一个mark,接下来我就可以基于此mark做策略路由了...当然,我也可以将其做REDIRECT(即DNAT),将其导入本地第四层。注意,不是通过抓包的方式,而是通过协议栈处理的方式。2.恶作剧
想搞恶作剧吗?那就安装这个模块尽情蹂躏TCP协议本身以及那些懂TCP但不全懂却有十足钻研精神的程序员吧!我晓得,如果我想搞清楚到底发生了什么,我一定可以!但是我不会那样去浪费有限的生命,一方面是我恨TCP,另一方面是我觉得仅仅出现问题就够了,并且由于我对TCP变态设计的一贯不认可,它在我的模块下一定会出问题,这也是我的目的,另外最重要的是,我的以上两个理由让我避开了诸如“你懂!你给讲一下!”那样的讽刺或者调侃似的追问,我真的没有太多的时间!TCP协议所谓的有连接本身没有错,但是为何要记住对方?难道不能用类似syn cookie之类的机制么?且以一个简单至极的序列号来标示数据,你有认证机制吗?为何不使用数据或者元数据本身?!事实上,我觉得TCP太过复杂了,而且可调的参数不多,且太乱,这些参数之间的关系又太过复杂...不如仅仅保留UDP,然后在UDP以上实现任何你想要的逻辑,比如实现TCP的逻辑,或者比它更好的...不要拿性能说话,性能根本就不是问题,正如Java程序员面对C程序员的诘问时说的那样,难道Java因为性能问题在成功的路上陷入困境了?一般而言,你花了一个月时间通过软件方式榨取了那么一点点性能提升,过不了几个月,新的硬件就会让你的成果黯然失色!
废话不多说了,我仅仅给出一个TCP连接在我的这个模块下被劫持的例子,剩下的可以利用的自己来想吧。只需要把这个模块安装在一台机器上,然后把这个机器挂接在一个Switch或者HUB上,当然HUB更好,在该机器的连接交换机的网卡上做一个SNAT:
iptables -t nat -A POSTROUTING -j MASQUERADE
加载模块!接下来你就会发现,其它好多挂在同一个交换机上的很多人,TCP连接都不通了,说白了这是Linux的连接跟踪导致的,下面我就简单分析一下为什么。
首先把Linux嗅探机的下列参数设置一下:
sysctl -w net.netfilter.nf_conntrack_tcp_loose=1
ifconfig eth0 promisc
如果你的交换机是一个HUB,那就特别好玩了,如果不是,那就偷偷下班后把它换成一个HUB...别总想着什么登录进交换机进行一大堆设置,然后在各种场合卖弄那些术语或者咬文嚼字,这都没用,程序员最大的弊端在于看不起社会工程学,觉得这些没有技术含量,当然并不包含高手...