标签:
抓包是一个简单易行的事,它可以帮你分析网络的行为。我记得早在2004年的时候,老师就讲过抓包有多么重要。这样,sock结构体的高4位形成了一个状态机:
实现这个状态机的流程图我就不画图了,没有时间...我觉得代码的注释还算清晰。把上述的状态机以及流程图编程实现,就是下面的这个Netfilter模块:
#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/list.h> #include <linux/ip.h> #include <net/tcp.h> MODULE_AUTHOR("marywangran"); MODULE_LICENSE("GPL"); #define CAP_HIT 31 #define CAP_WAIT 30 #define CAP_IGN 29 #define CAP_DEL 28 #define MAX_CACHE 8 unsigned char *url = "test"; struct wait_entry { struct list_head list; u16 cnt; __be32 saddr; __be32 daddr; u16 sport; u16 dport; unsigned long save_flags; struct sk_buff *skb[MAX_CACHE]; }; static DEFINE_SPINLOCK(caplist_lock); static LIST_HEAD(wait_list); static struct wait_entry * find_add_entry(struct sk_buff *skb) { struct list_head *lh, *n; struct wait_entry *wn; struct iphdr *iph = ip_hdr(skb); struct tcphdr *th = (void *)iph + iph->ihl*4; u32 cmp_saddr, cmp_daddr; u16 cmp_sport, cmp_dport; cmp_saddr = iph->saddr > iph->daddr ? iph->saddr:iph->daddr; cmp_daddr = iph->saddr > iph->daddr ? iph->daddr:iph->saddr; cmp_sport = th->source > th->dest ? th->source:th->dest; cmp_dport = th->source > th->dest ? th->dest:th->source; spin_lock(&caplist_lock); list_for_each_safe(lh, n, &wait_list) { wn = list_entry(lh, struct wait_entry, list); if (cmp_saddr == wn->saddr && cmp_daddr == wn->daddr && cmp_sport == wn->sport && cmp_dport == wn->dport) { if (wn->cnt < MAX_CACHE) { wn->skb[wn->cnt] = skb_clone(skb, GFP_ATOMIC);; wn->cnt += 1; spin_unlock(&caplist_lock); return wn; } else { int i = 0; for (i = 0; i < wn->cnt; i++) { if (wn->skb[i]) { kfree_skb(wn->skb[i]); } } list_del(lh); kfree(wn); spin_unlock(&caplist_lock); return NULL; } } } wn = (struct wait_entry *)kzalloc(sizeof(struct wait_entry), GFP_ATOMIC); wn->saddr = iph->saddr > iph->daddr ? iph->saddr:iph->daddr; wn->daddr = iph->saddr > iph->daddr ? iph->daddr:iph->saddr;; wn->sport = th->source > th->dest ? th->source:th->dest; wn->dport = th->source > th->dest ? th->dest:th->source; wn->skb[0] = skb_clone(skb, GFP_ATOMIC);; wn->cnt = 1; __set_bit(CAP_WAIT, &wn->save_flags); list_add(&wn->list, &wait_list); spin_unlock(&caplist_lock); return wn; } char *findstr(const char *s1, const char *s2, unsigned int len) { int l1, l2; l2 = strlen(s2); if (!l2) return (char *)s1; l1 = len; while (l1 >= l2) { l1--; if (!memcmp(s1, s2, l2)) return (char *)s1; s1++; } return NULL; } static int string_match(struct sk_buff *skb, char *str) { char *ret = NULL; ret = findstr(skb->data, str, 512); if (ret) { return 1; } return 0; } static void capture_skb(struct sk_buff *skb, const struct net_device *dev) { struct iphdr *iph = ip_hdr(skb); struct tcphdr *th = (void *)iph + iph->ihl*4; u16 sport = 0, dport = 0; u32 saddr = 0, daddr = 0; saddr = iph->saddr; daddr = iph->daddr; sport = th->source; dport = th->dest; // 简单打印而已 printk("###print %0x %0x %0x %0x S:%u A:%u len:%u\n", saddr, daddr, sport, dport, ntohl(th->seq), ntohl(th->ack_seq), skb->len); } static void check_pcap(struct sock *sk, struct sk_buff *skb, char *url, int hook, const struct net_device *dev) { struct wait_entry *entry = NULL; if (sk->sk_state == TCP_LISTEN) { // 这里注意半连接攻击!所以需要entry表项的超时机制。 entry = find_add_entry(skb); if (!entry) { goto out; } // 注意TCP_DEFER_ACCEPT选项,该选项允许在Listen状态下接收GET请求! if (url && string_match(skb, url)) { int i = 0; spin_lock(&caplist_lock); // 如果匹配到了字符串,那么就把之前缓存的最多8个数据包一并导出,如果要实现好一些,在缓存这些数据包时就要把时间戳带上,不然这里会有一个突发。 for (i = 0; i < entry->cnt; i++) { capture_skb(entry->skb[i], dev); } spin_unlock(&caplist_lock); // 由于此时的Listen状态socket并不对应五元组,因此entry作为一个五元组替代要保存flags信息,最终这个flags要映射到建立好的ESTABLISH socket中! // 匹配成功,这个entry对应的最终的socket flag要有HIT标志,表示这个socket上的数据包均需要抓取。 __set_bit(CAP_HIT, &entry->save_flags); // 匹配成功,等这个entry代表的元组创建了ESTABLISH socket之后,将flags转交给该socket的flags后,就要删除它,因为已经不需要了。 __set_bit(CAP_DEL, &entry->save_flags); // 匹配成功,不要继续等待GET了,清除WAIT标识 __clear_bit(CAP_WAIT, &entry->save_flags); } } else if (sk->sk_state == TCP_TIME_WAIT) { //TODO } else { int add = 0; if (!sock_flag(sk, CAP_IGN) && !sock_flag(sk, CAP_WAIT) && !sock_flag(sk, CAP_HIT)) { // 这里代表这是第一次从Listen状态进入ESTABLISH状态 entry = find_add_entry(skb); if (entry) { // 转交entry的flags到socket(注意只使用了高4位) sk->sk_flags |= (entry->save_flags & 0xf0000000); if (test_bit(CAP_DEL, &entry->save_flags)) { // 如果设置了DEL位,说明已经匹配成功,不需要这个entry了,直接删除 // 注意,此时的flags同时也有了HIT位 int i = 0; spin_lock(&caplist_lock); for (i = 0; i < entry->cnt; i++) { if (entry->skb[i]) { kfree_skb(entry->skb[i]); } } list_del(&entry->list); kfree(entry); entry = NULL; spin_unlock(&caplist_lock); } } else { // 如果根本就没有经过Listen,或者说在Listen阶段就被删除了entry,直接忽略,关于此socket,永不抓包 sock_set_flag(sk, CAP_IGN); goto out; } add = 1; } if (sock_flag(sk, CAP_HIT)) { // 携带HIT标志的,抓包。 capture_skb(skb, dev); } else if (sock_flag(sk, CAP_WAIT)){ // 携带WAIT标志的,继续等待数据包,期待在收发8个数据包内匹配到特定的URL if (add == 0) { entry = find_add_entry(skb); } if (!entry) { sock_set_flag(sk, CAP_IGN); sock_reset_flag(sk, CAP_WAIT); goto out; } if (url && string_match(skb, url)) { int i = 0; spin_lock(&caplist_lock); // 如果匹配到了字符串,那么就把之前缓存的最多8个数据包一并导出,如果要实现好一些,在缓存这些数据包时就要把时间戳带上,不然这里会有一个突发。 for (i = 0; i < entry->cnt; i++) { capture_skb(entry->skb[i], dev); } // 匹配成功,HIT位将进入socket的flags,不再需要继续等待匹配,无需缓存未决数据包了,删除entry for (i = 0; i < entry->cnt; i++) { if (entry->skb[i]) { kfree_skb(entry->skb[i]); } } list_del(&entry->list); kfree(entry); spin_unlock(&caplist_lock); sock_set_flag(sk, CAP_HIT); sock_reset_flag(sk, CAP_WAIT); } // ignore } } out: return; } static unsigned int ipv4_tcp_urlcap_in (unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sock *sk; struct iphdr *iph = ip_hdr(skb); struct tcphdr *th = (void *)iph + iph->ihl*4; if (iph->protocol != IPPROTO_TCP) { return NF_ACCEPT; } sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); if (!sk) { goto out; } skb->sk = sk; check_pcap(sk, skb, url, hooknum, in); out: return NF_ACCEPT; } static unsigned int ipv4_tcp_urlcap_out (unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sock *sk; struct iphdr *iph = ip_hdr(skb); if (iph->protocol != IPPROTO_TCP) { return NF_ACCEPT; } sk = skb->sk; if (!sk) { goto out; } check_pcap(sk, skb, url, hooknum, out); out: return NF_ACCEPT; } static struct nf_hook_ops ipv4_urlcap_ops[] __read_mostly = { { .hook = ipv4_tcp_urlcap_in, .owner = THIS_MODULE, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = -199, }, { .hook = ipv4_tcp_urlcap_out, .owner = THIS_MODULE, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = -199, }, }; static int __init url_cap_init(void) { int ret; ret = nf_register_hooks(ipv4_urlcap_ops, ARRAY_SIZE(ipv4_urlcap_ops)); if (ret) { goto out;; } return 0; out: return ret; } static void __exit url_cap_fini(void) { nf_unregister_hooks(ipv4_urlcap_ops, ARRAY_SIZE(ipv4_urlcap_ops)); } module_init(url_cap_init); module_exit(url_cap_fini);
标签:
原文地址:http://blog.csdn.net/dog250/article/details/52345004