教你修改以及重构skb -基于2.6.18

    技术2025-04-28  17

    测试环境:    CentOS5.3 2.6.18 工具:    sendip和wireshark    sendip可以发送各种数据包,确实方便.wireshark图形化的显示对于分析整个数据包还是相当不错的...     一:内核 态基于Netfilter构造数据包 主要有两种方式: 1. alloc_skb申请一个skb结构体,然后根据实际的应用填充不同的成员,或者基于当前数据包的skb,    调用skb_copy() pskb_copy() skb_copy_expand()等新申请一个nskb,并且拷贝skb的内容。 2. 直接在先前接收到的数据包skb上作修改,主要有源IP、目IP,如果是TCP/UDP协议的话,还有源端口目的端口号。   就是根据你自己的需求去调整数据包的相关成员即可。然后重新计算各个部分的校验和。    不管你第一种方式还是第二种方式,你需要知道你也必须知道的就是对于l2 l3 l4层的数据你都必须去构造,我之前就是 由于没有构造L2而郁闷了一天...    让我们先从一个小程序 开始,把5个hook都挂上mac这个函数,主要就是看看l2,对于l3 l4以及应用层我以前的几个帖子里面 已经有很多了,这里就不说了

       printk("------begin %s--------/n", hooks[hooknum]);    print_ipproto(iph->protocol);    printk("len is %d, data len is %d/n", nskb->len, nskb->data_len);    if(nskb->mac_len > 0)            {             eth = (struct ethhdr*)(nskb->mac.raw);             print_mac(eth);                           }       printk("------end  %s--------/n", hooks[hooknum]); 复制代码

     

    #include <linux /module.h> #include <linux/kernel .h> #include <linux/init.h> #include <linux/netfilter .h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/netdevice.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <net/tcp .h> #include <net/udp.h> #include <net/icmp.h> #include <linux/netfilter_ipv4.h> #define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" #define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5] MODULE_LICENSE("GPL"); MODULE_AUTHOR("kenthy@163.com"); const char* hooks[] ={ "NF_IP_PRE_ROUTING",                              "NF_IP_LOCAL_IN",                              "NF_IP_FORWARD",                              "NF_IP_LOCAL_OUT",                              "NF_IP_POST_ROUTING"}; void print_ipproto(int proto) { switch(proto) {         case IPPROTO_ICMP:                 printk("%s/n", "IPPROTO_ICMP");           break;         case IPPROTO_TCP:                 printk("%s/n", "IPPROTO_TCP");           break;         case IPPROTO_UDP:                 printk("%s/n", "IPPROTO_UDP");           break;         default:                 printk("%s/n", "other IPPROTO");         } } void print_mac(struct ethhdr* eth) { if(eth==NULL)         return;         if(eth->h_source!=NULL)         printk("SOURCE:" MAC_FMT "/n", MAC_ARG(eth->h_source)); if(eth->h_dest!=NULL)              printk("DEST:" MAC_FMT "/n", MAC_ARG(eth->h_dest)); } unsigned int mac(unsigned int hooknum,                  struct sk_buff** skb,                  const struct net_device *in,                  const struct net_device *out,                  int (*okfn)(struct sk_buff*)) {    struct sk_buff* nskb;    struct iphdr *iph = NULL;    struct ethhdr* eth;      nskb = *skb;   if(nskb==NULL)   {     printk("%s/n", "*skb is NULL");     return NF_ACCEPT;    }      iph = ip_hdr(nskb);   if(iph == NULL)   {     printk("%s/n", "*iph is NULL");     return NF_ACCEPT;    }           printk("------begin %s--------/n", hooks[hooknum]);    print_ipproto(iph->protocol);    printk("len is %d, data len is %d/n", nskb->len, nskb->data_len);    if(nskb->mac_len > 0)            {             eth = (struct ethhdr*)(nskb->mac.raw);             print_mac(eth);                           }    else     printk("%s", "mac is NULL");                           printk("------end  %s--------/n", hooks[hooknum]);        return NF_ACCEPT; }                              static struct nf_hook_ops mac_ops[] = {         {                 .hook                = mac,                 .owner                = THIS_MODULE,                 .pf                = PF_INET,                 .hooknum        = NF_IP_PRE_ROUTING,                 .priority = NF_IP_PRI_FIRST,         },         {                 .hook                = mac,                 .owner                = THIS_MODULE,                 .pf                = PF_INET,                 .hooknum        = NF_IP_LOCAL_IN,                 .priority = NF_IP_PRI_FIRST,         },         {                 .hook                = mac,                 .owner                = THIS_MODULE,                 .pf                = PF_INET,                 .hooknum        = NF_IP_FORWARD,                 .priority = NF_IP_PRI_FIRST,         },         {                 .hook                = mac,                 .owner                = THIS_MODULE,                 .pf                = PF_INET,                 .hooknum        = NF_IP_LOCAL_OUT,                 .priority = NF_IP_PRI_FIRST,         },         {                 .hook                = mac,                 .owner                = THIS_MODULE,                 .pf                = PF_INET,                 .hooknum        = NF_IP_PRE_ROUTING,                 .priority = NF_IP_POST_ROUTING,         }, }; static int __init init(void) {     int ret;     ret = nf_register_hooks(mac_ops, ARRAY_SIZE(mac_ops));     if (ret < 0) {         printk("http detect:can't register mac_ops detect hook!/n");         return ret;     }     printk("insmod mac_ops detect module/n");     return 0; } static void __exit fini(void) {     nf_unregister_hooks(mac_ops, ARRAY_SIZE(mac_ops));     printk("remove mac_ops detect module./n"); } module_init(init); module_exit(fini); 复制代码

      insmod mac.ko加载mac模块 后,随便发个ping包   Jan 10 09:44:13 nfs-client kernel: ------begin NF_IP_LOCAL_OUT-------- Jan 10 09:44:13 nfs-client kernel: IPPROTO_ICMP Jan 10 09:44:13 nfs-client kernel: len is 84, data len is 0 Jan 10 09:44:13 nfs-client kernel: mac is NULL------end  NF_IP_LOCAL_OUT-------- Jan 10 09:44:13 nfs-client kernel: ------begin NF_IP_PRE_ROUTING-------- Jan 10 09:44:13 nfs-client kernel: IPPROTO_ICMP Jan 10 09:44:13 nfs-client kernel: len is 84, data len is 0 Jan 10 09:44:13 nfs-client kernel: SOURCE:00:50:56:fa:70:2a Jan 10 09:44:13 nfs-client kernel: DEST:00:0c:29:4f:de:ac Jan 10 09:44:13 nfs-client kernel: ------end  NF_IP_PRE_ROUTING-------- Jan 10 09:44:13 nfs-client kernel: ------begin NF_IP_LOCAL_IN-------- Jan 10 09:44:13 nfs-client kernel: IPPROTO_ICMP Jan 10 09:44:13 nfs-client kernel: len is 84, data len is 0 Jan 10 09:44:13 nfs-client kernel: SOURCE:00:50:56:fa:70:2a Jan 10 09:44:13 nfs-client kernel: DEST:00:0c:29:4f:de:ac Jan 10 09:44:13 nfs-client kernel: ------end  NF_IP_LOCAL_IN--------    可以看到对于挂载在out上的数据包mac已经被剥掉 当接收一个包时,处理n层协议头的函数从n-1层收到一个缓冲区,它的skb->data指向n层协议的头。处理n层协议的函数把本层的指针(例 如,L3对应的是skb->nh指针)初始化为skb->data,因为这个指针的值会在处理下一层协议时改变(skb->data将 被初始化成缓冲区里的其他地址)。在处理n层协议的函数结束时,在把包传递给n+1层的处理函数前,它会把skb->data指针指向n层协议头的 末尾,这正好是n+1层协议的协议头。 发送包的过程与此相反,但是由于要为每一层添加新的协议头,这个过程要比接收包的过程复杂。

    下载 (13.31 KB) 2010-01-10 14:40

          好的,到现在你已经知道要重新搞一个数据包需要自己来DIY l2 l3 l4当然还有l7如果你想....        来先看看skb的几个重要指针吧 unsigned char *head unsigned char *end unsigned char *data unsigned char *tail 它们表示缓冲区和数据部分的边界。在每一层申请缓冲区时,它会分配比协议头或协议数据大的空间。head和end指向缓冲区的头部和尾部,而data和 tail指向实际数据的头部和尾部。每一层会在head和data之间填充协议头,或者在tail和end之间添加新的协议数据   

    下载 (7.6 KB) 2010-01-10 14:40

    那么具体操作这些指针呢? (a)skb_put, (b)skb_push, (c)skb_pull, and (d)skb_reserve

    下载 (38.67 KB) 2010-01-10 14:40

      再加上dev_queue_xmit这个函数,你已经可以完成整件事了,对你现在有点蒙感觉无从下手,我也是!!! 我是历经几十次的死机才成功的,写这个文章也是希望后来人少走点弯路...   那就从修改开始吧....

    #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/netfilter.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/netdevice.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <linux/inet.h> #include <net/tcp.h> #include <net/udp.h> #include <net/icmp.h> #include <linux/netfilter_ipv4.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("kenthy@163.com"); #define    ETH    "eth0" unsigned char   SMAC[ETH_ALEN] = {0x00,0x0C,0x29,0x4F,0xDE,0xAC}; unsigned char   DMAC[ETH_ALEN] = {0x00,0x50,0x56,0xFA,0x70,0x2A}; static struct nf_hook_ops modify_ops; static unsigned int modify(unsigned int hooknum, struct sk_buff ** skb,                                   const struct net_device * in, const struct net_device * out,                                   int (*okfn)(struct sk_buff *)) {     struct sk_buff* nskb;     struct iphdr* nip_hdr;     unsigned int   nip_hdr_off;     struct tcphdr* ntcp_hdr;     unsigned int ntcp_hdr_off;     struct ethhdr* neth_hdr;     int ret = 0;        nskb = skb_copy(*skb, GFP_ATOMIC);    if(nskb == NULL)      {              printk("%s/n", "skb_copy return NULL");              return NF_ACCEPT;      }   if( nskb->nh.iph->protocol != IPPROTO_TCP)     {             kfree_skb(nskb);       return NF_ACCEPT;      }     nip_hdr = nskb->nh.iph;     nip_hdr_off = nip_hdr->ihl << 2;         ntcp_hdr = (struct tcphdr *)((void *)nip_hdr + nip_hdr_off);     ntcp_hdr_off = ntcp_hdr->doff << 2;     if(!ntcp_hdr->syn)             {                     kfree_skb(nskb);                     return NF_ACCEPT;             }         //evil!     nip_hdr->daddr = in_aton("192.168.1.101");     nip_hdr->check = 0;     nip_hdr->check = ip_fast_csum((unsigned char *)nip_hdr, nip_hdr->ihl);              nskb->csum = 0;     nskb->csum = csum_partial((unsigned char *)(ntcp_hdr + ntcp_hdr_off),                                       ntohs(nip_hdr->tot_len) - nip_hdr_off - ntcp_hdr_off, 0);     ntcp_hdr->check = 0;     ntcp_hdr->check = csum_tcpu dp_magic(nip_hdr->saddr, nip_hdr->daddr,                                         ntohs(nip_hdr->tot_len) - nip_hdr_off, nip_hdr->protocol,                                         csum_partial((unsigned char *)ntcp_hdr, ntcp_hdr_off, nskb->csum));     nskb->ip_summed = CHECKSUM_NONE;     nskb->pkt_type  = PACKET_OTHERHOST;         nskb->dev = dev_get_by_name(ETH);     if(nskb->dev==NULL)     {             printk("%s/n", "dev_get_by_name return NULL");             kfree_skb(nskb);             return NF_ACCEPT;             }                 nskb->mac.raw = skb_push (nskb, ETH_HLEN);      { //eth headeri       neth_hdr = (struct ethhdr *)nskb->mac.raw;       memcpy (neth_hdr->h_dest, DMAC, ETH_ALEN);       memcpy (neth_hdr->h_source, SMAC, ETH_ALEN);       neth_hdr->h_proto = __constant_htons (ETH_P_IP);     }         dev_hold(nskb->dev);     printk("%s/n", "dev_hold ok");         ret = dev_queue_xmit(nskb);     printk("ret:%d/n", ret);     return NF_STOLEN; } static int __init init(void) {   int  ret = 0;   modify_ops.hook = modify;   modify_ops.hooknum = NF_IP_LOCAL_OUT;   modify_ops.pf = PF_INET;   modify_ops.priority = NF_IP_PRI_FIRST;   ret = nf_register_hook(&modify_ops);   if (ret < 0)    {      printk("%s/n", "can't modify skb hook!");      return ret;    }     printk("%s/n", "insmod modify skb module");     return 0; } static void __exit fini(void) {     nf_unregister_hook(&modify_ops);     printk("%s/n", "remove modify skb module."); } module_init(init); module_exit(fini); 复制代码

    测试结果: sendip -p ipv4 -is 192.168.238.180 -p tcp -ts 598982 -td 80 192.168.1.1 //                                  sip                                 sport       dport  dip sendip比较好用吧,你可以指定syn ack之类的呢

    下载 (57.13 KB) 2010-01-10 14:40

    上面的三个数据包是没有加载skb_modify模块的...上面的具体函数你们可以sourceinsight跟踪看看,我也不可能一一讲解 因为我们是挂在NF_IP_LOCAL_OUT上所以我们需要重新搞mac header 那么DIY SKB呢?

    #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/netfilter.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <net/tcp.h> #include <net/udp.h> #include <net/route.h> #include <net/icmp.h> #include <linux/netfilter_ipv4.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("kenthy@163.com"); #define    ETH     "eth0" #define    SIP     "192.168.238.180" #define    DIP     "192.168.1.101" #define    SPORT   39804 #define    DPORT   80 unsigned char   SMAC[ETH_ALEN] = {0x00,0x0C,0x29,0x4F,0xDE,0xAC}; unsigned char   DMAC[ETH_ALEN] = {0x00,0x50,0x56,0xFA,0x70,0x2A}; int cp_dev_xmit_tcp (char * eth, u_char * smac, u_char * dmac,              u_char * pkt, int pkt_len,              u_long sip, u_long dip,              u_short sport, u_short dport, u_long seq, u_long ack_seq, u_char psh, u_char fin) {   struct sk_buff * skb = NULL;   struct net_device * dev = NULL;   struct ethhdr * ethdr = NULL;   struct iphdr * iph = NULL;   struct tcphdr * tcph = NULL;   u_char * pdata = NULL;   int nret = 1;   if (NULL == smac || NULL == dmac) goto out;   dev = dev_get_by_name(eth);   if (NULL == dev)    goto out;   skb = alloc_skb (pkt_len + sizeof (struct iphdr) + sizeof (struct tcphdr) + LL_RESERVED_SPACE (dev), GFP_ATOMIC);      if (NULL == skb)     goto out;      skb_reserve (skb, LL_RESERVED_SPACE (dev));   skb->dev = dev;   skb->pkt_type = PACKET_OTHERHOST;   skb->protocol = __constant_htons(ETH_P_IP);   skb->ip_summed = CHECKSUM_NONE;   skb->priority = 0;      skb->nh.iph = (struct iphdr*)skb_put(skb, sizeof (struct iphdr));   skb->h.th = (struct tcphdr*)skb_put(skb, sizeof (struct tcphdr));   pdata = skb_put (skb, pkt_len);   {     if (NULL != pkt)      memcpy (pdata, pkt, pkt_len);   }      {     tcph = (struct tcphdr *) skb->h.th;     memset (tcph, 0, sizeof (struct tcphdr));     tcph->source = sport;     tcph->dest = dport;     tcph->seq = seq;     tcph->ack_seq = ack_seq;     tcph->doff = 5;     tcph->psh = psh;     tcph->fin = fin;     tcph->syn = 1;     tcph->ack = 0;     tcph->window = __constant_htons (5840);     skb->csum = 0;     tcph->check = 0;   }      {     iph = (struct iphdr*) skb->nh.iph;     iph->version = 4;     iph->ihl = sizeof(struct iphdr)>>2;     iph->frag_off = 0;     iph->protocol = IPPROTO_TCP;     iph->tos = 0;     iph->daddr = dip;     iph->saddr = sip;     iph->ttl = 0x40;     iph->tot_len = __constant_htons(skb->len);     iph->check = 0;   }      skb->csum = skb_checksum (skb, iph->ihl*4, skb->len - iph->ihl * 4, 0);   tcph->check = csum_tcpudp_magic (sip, dip, skb->len - iph->ihl * 4, IPPROTO_TCP, skb->csum);      skb->mac.raw = skb_push (skb, 14);   {     ethdr = (struct ethhdr *)skb->mac.raw;     memcpy (ethdr->h_dest, dmac, ETH_ALEN);     memcpy (ethdr->h_source, smac, ETH_ALEN);     ethdr->h_proto = __constant_htons (ETH_P_IP);   }   if (0 > dev_queue_xmit(skb)) goto out;      nret = 0; out:   if (0 != nret && NULL != skb) {dev_put (dev); kfree_skb (skb);}      return (nret); } static int __init init(void) {   printk("%s/n","insmod skb_diy module/n");        cp_dev_xmit_tcp (ETH, SMAC, DMAC,NULL, 0,                     in_aton(SIP),in_aton(DIP),                     htons(SPORT),htons(DPORT),                     0, 0, 0, 0);     return 0; } static void __exit fini(void) {     printk("%s/n","remove skb_diy module./n"); } module_init(init); module_exit(fini); 复制代码

    测试结果:

    下载 (67.45 KB) 2010-01-10 14:40

    我这里并没有填充上层的东西 但是已经提供接口 pdata = skb_put (skb, pkt_len);   {     if (NULL != pkt)      memcpy (pdata, pkt, pkt_len);   } 你可以自己先截获一个上层的包再填充进去...  关于上层的东西我开源应用层DPI--l7detect初步成果 已经很清楚了,都已经DPI了 就这么多吧,天冷在寝室又没有暖气和空调,考个研还不让我们进实验室^_^,经过这两天的奋战终于搞定修改skb DIY SKB再加上以前基于netfilter的深度数据包检测.... 至此我可以很自信的说对于netfilter不算静态也算熟练了... 由于本人也是菜鸟,纰漏和不对之处还望指正...   写这些东西纯属爱好,由于内核网络 代码这块变化看对于测试环境不一样的机子,概不保证正确性...但是思路肯定还是这样的,主要就是几个API的变化而已 参考:   http://blog.chinaunix .net/u/33048/showart_2043789.html   http://hi.baidu.com/zshidong/blo ... 5f1d00213f2eb0.html [ 本帖最后由 ubuntu er 于 2010-1-10 14:42 编辑 ]

    本主题由 send_linux 于 2010-06-19 14:53 鉴定为 原创
    最新回复(0)