rootkit for linux 3.小窥数据链路层

来源:互联网 发布:淘宝店铺名称设计 编辑:程序博客网 时间:2024/06/09 19:46

 

 

上篇文章讲了怎样寻找符号表。那个方法不算好。今天我想到了一个方法,要用到反汇编引擎的,一时实现不了。实现后我再发新的文章出来啊。

 

好了,现在你已经可以获得内核的符号表了。你可以认为你控制了一切。但是现在的问题是,你拿到了枪,但是你不会用。

作为一个木马,最重要的是什么?是与外界沟通。一个不能与外界沟通的木马,那能叫木马么,那叫铁马。所以,我们先对网络下毒手,就从数据链路层入手。

 

这时候数据链路层发飙了,它说我冤枉啊,在osi分层模型中我有7个兄弟,你咋光知道欺负我呢?

没办法,既然是rootkit,搞啥都要搞最底层的。要是天下的网卡都一个样,我们还可以考虑从硬件驱动下手。其实搞不懂OSI模型也没啥关系,我也搞不懂,我没想过搞懂,我非差痛恨的就是那些老师讲的,课本里写的,很不实在的概念。所以我们从代码看起吧。

 

这里推荐一本书《Understand Linux Network Internals》,是本好书。我就是看着这本书来分析网络部分的代码的。

 

最底层当然是硬件驱动。首先把你的网卡驱动从drivers/net里面找出来。我的是sky2.c

打开代码一看,我靠,4k多行。吓傻了吧。

先找到sky2_probe。你的网卡驱动里也有对应的函数。就是pci_driver.probe这个字段的内容。内核初始化pci设备时要调用的函数,里面包含着初始化net_device这个数据结构的代码。

 

找到关键性的一句话

  1.     err = request_irq(pdev->irq, sky2_intr,
  2.               (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
  3.               dev->name, hw);

这句话是申请irq的。所以,在我的sky2产生中断的时候,中断处理例程会调用sky2_intr函数。我们跳转到sky2_intr函数下。

 

  1. static irqreturn_t sky2_intr(int irq, void *dev_id)
  2. {
  3.     struct sky2_hw *hw = dev_id;
  4.     u32 status;
  5.     /* Reading this mask interrupts as side effect */
  6.     status = sky2_read32(hw, B0_Y2_SP_ISRC2);
  7.     if (status == 0 || status == ~0)
  8.         return IRQ_NONE;
  9.     prefetch(&hw->st_le[hw->st_idx]);
  10.     napi_schedule(&hw->napi);
  11.     return IRQ_HANDLED;
  12. }

这函数才这么几行。关键性的一行是napi_schedule(&hw->napi);。

这里说明一下。现在的网卡从中断发生到将数据传给网络层的这个过程,使用的接口有两种。一种叫New API,简称NAPI。另外一种不知啥名字,就叫它Old API。使用Old API的网卡驱动的代码我没看过。不管是新的api,还是旧的api。在接收中断后,都要努力地把自己加入softnet_data.poll_list中。NAPI就是调用napi_schedule(&hw->napi);这个来实现的。

 

慢慢道来。linux内核网络的底层都是用的软中断机制。你在硬中断里是要禁用中断的,你不能在硬中断里处理太多的东西,不然其他设备老得不到中断,要发飙的。但中断到来的时候,有很多东西要处理啊。所以,linux引入了软中断机制。软中断发生在硬中断之后,在软中断下不必禁用中断,可以干任何事。而且软中断可以充分利用cpu资源,如果你是双核或者更多核,软中断可能发生在这个cpu上,也可能发生在另外的cpu上。所以对网络这东西挺适用的。在硬中断里,各个网卡都告诉内核,等下软中断来了,记得处理我。而softnet_data是个per_cpu结构,就是每个cpu一个,内核引入这个东西来避免加锁。网卡把自己加进了poll_list里,那等下在软中断里这个网卡就会被处理。

 

好,我们看看软中断的代码。网络共使用了两个软中断,一个是NET_RX_ACTION,一个是NET_TX_ACTION。我们看的是接收数据,就是RX。找到net_rx_action中的这一行。

  1.         if (test_bit(NAPI_STATE_SCHED, &n->state))
  2.             work = n->poll(n, weight);

在这里,sky2就要被轮询了。再转到sky2_poll。

  1. static int sky2_poll(struct napi_struct *napi, int work_limit)
  2. {
  3. 。。。
  4.     while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
  5.         work_done += sky2_status_intr(hw, work_limit - work_done, idx);
  6.         if (work_done >= work_limit)
  7.             goto done;
  8.     }
  9.   。。。
  10.     return work_done;
  11. }

我们再看 sky2_status_intr中的这一行

  1.             skb->protocol = eth_type_trans(skb, dev);
  2.             dev->stats.rx_packets++;
  3.             dev->stats.rx_bytes += skb->len;
  4.             dev->last_rx = jiffies;
  5. #ifdef SKY2_VLAN_TAG_USED 
  6.             if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
  7.                 vlan_hwaccel_receive_skb(skb,
  8.                              sky2->vlgrp,
  9.                              be16_to_cpu(sky2->rx_tag));
  10.             } else
  11. #endif 
  12.                 netif_receive_skb(skb);

看到没,skb是在这里产生的。随后调用的netif_receive_skb这个函数,它是一个非常关键的函数!是所有网卡的必经之路!

 

  1. int netif_receive_skb(struct sk_buff *skb)
  2. {
  3.     struct packet_type *ptype, *pt_prev;
  4.     struct net_device *orig_dev;
  5.     int ret = NET_RX_DROP;
  6.     __be16 type;
  7.     /* if we've gotten here through NAPI, check netpoll */
  8.     if (netpoll_receive_skb(skb))
  9.         return NET_RX_DROP;
  10.     if (!skb->tstamp.tv64)
  11.         net_timestamp(skb);
  12.     if (!skb->iif)
  13.         skb->iif = skb->dev->ifindex;
  14.     orig_dev = skb_bond(skb);
  15.     if (!orig_dev)
  16.         return NET_RX_DROP;
  17.     __get_cpu_var(netdev_rx_stat).total++;
  18.     skb_reset_network_header(skb);
  19.     skb_reset_transport_header(skb);
  20.     skb->mac_len = skb->network_header - skb->mac_header;
  21.     pt_prev = NULL;
  22.     rcu_read_lock();
  23. #ifdef CONFIG_NET_CLS_ACT
  24.     if (skb->tc_verd & TC_NCLS) {
  25.         skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
  26.         goto ncls;
  27.     }
  28. #endif
  29.     list_for_each_entry_rcu(ptype, &ptype_all, list) {
  30.         if (!ptype->dev || ptype->dev == skb->dev) {
  31.             if (pt_prev)
  32.                 ret = deliver_skb(skb, pt_prev, orig_dev);
  33.             pt_prev = ptype;
  34.         }
  35.     }

看到什么没有。最后,系统遍历了ptype_all这个链表。并且对元素们调用了

  1. static inline int deliver_skb(struct sk_buff *skb,
  2.          struct packet_type *pt_prev,
  3.          struct net_device *orig_dev)
  4. {
  5.  atomic_inc(&skb->users);
  6.  return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
  7. }

看出来什么没有。如果我们把自己挂到ptype_all上,不就能截获所有skb了吗?

某些嗅探器会选择在这里挂钩。大多数防火墙会选择在netfilter上挂钩。这里是skb要经过的第一个钩子。看了这张图就明白了。

 

 

标红色的地方,是我们挂钩的位置。这地方真够底层的啊。

以前做过一个东西跟网络相关的,不过没有hook到这里,如果是应用程序,一般都只hook到netfilter。一般的网络分析已经够用了。但我们是要做rootkit。netfilter就不能满足我们的要求了。

 

有人问“什么是skb?什么是net_device”

其实《Understand Linux Network Internals》和《Linux Device Driver》这两本书里都讲得很清楚。我们的rootkit对skb的操作也不复杂。遇到是自己的包,留下。不是自己的,丢掉。就跟投名状一样,是兄弟的,留下。不是兄弟的,杀了。

 

下篇文章将介绍怎样hook数据链路层,以截获所有的skb。本文说的一些概念如果不清楚的话,边看书,边看代码,过两天就会清楚了。我现在也不大清楚。如果说错了什么,希望高手严厉批评。

原创粉丝点击