上篇文章讲了怎样寻找符号表。那个方法不算好。今天我想到了一个方法,要用到反汇编引擎的,一时实现不了。实现后我再发新的文章出来啊。
好了,现在你已经可以获得内核的符号表了。你可以认为你控制了一切。但是现在的问题是,你拿到了枪,但是你不会用。
作为一个木马,最重要的是什么?是与外界沟通。一个不能与外界沟通的木马,那能叫木马么,那叫铁马。所以,我们先对网络下毒手,就从数据链路层入手。
这时候数据链路层发飙了,它说“我冤枉啊,在osi分层模型中我有7个兄弟,你咋光知道欺负我呢?”
没办法,既然是rootkit,搞啥都要搞最底层的。要是天下的网卡都一个样,我们还可以考虑从硬件驱动下手。其实搞不懂OSI模型也没啥关系,我也搞不懂,我没想过搞懂,我非差痛恨的就是 那些 老师讲的,课本里写的,很不实在的概念。所以我们从代码看起吧。
这里推荐一本书《Understand Linux Network Internals》,是本好书。我就是看着这本书来分析网络部分的代码的。
最底层当然是硬件驱动。首先把你的网卡驱动从drivers/net里面找出来。我的是sky2.c。
打开代码一看,我靠,4k多行。吓傻了吧。
先找到sky2_probe。你的网卡驱动里也有对应的函数。就是pci_driver.probe这个字段的内容。内核初始化pci设备时要调用的函数,里面包含着初始化net_device这个数据结构的代码。
找到关键性的一句话
- err = request_irq(pdev->irq, sky2_intr,
- (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
- dev->name, hw);
这句话是申请irq的。所以,在我的sky2产生中断的时候,中断处理例程会调用sky2_intr函数。我们跳转到sky2_intr函数下。
- static irqreturn_t sky2_intr(int irq, void *dev_id)
- {
- struct sky2_hw *hw = dev_id;
- u32 status;
- /* Reading this mask interrupts as side effect */
- status = sky2_read32(hw, B0_Y2_SP_ISRC2);
- if (status == 0 || status == ~0)
- return IRQ_NONE;
- prefetch(&hw->st_le[hw->st_idx]);
- napi_schedule(&hw->napi);
- return IRQ_HANDLED;
- }
这函数才这么几行。关键性的一行是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中的这一行。
- if (test_bit(NAPI_STATE_SCHED, &n->state))
- work = n->poll(n, weight);
在这里,sky2就要被轮询了。再转到sky2_poll。
- static int sky2_poll(struct napi_struct *napi, int work_limit)
- {
- 。。。
- while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
- work_done += sky2_status_intr(hw, work_limit - work_done, idx);
- if (work_done >= work_limit)
- goto done;
- }
- 。。。
- return work_done;
- }
我们再看 sky2_status_intr中的这一行
- skb->protocol = eth_type_trans(skb, dev);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
- dev->last_rx = jiffies;
- #ifdef SKY2_VLAN_TAG_USED
- if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
- vlan_hwaccel_receive_skb(skb,
- sky2->vlgrp,
- be16_to_cpu(sky2->rx_tag));
- } else
- #endif
- netif_receive_skb(skb);
看到没,skb是在这里产生的。随后调用的netif_receive_skb这个函数,它是一个非常关键的函数!是所有网卡的必经之路!
- int netif_receive_skb(struct sk_buff *skb)
- {
- struct packet_type *ptype, *pt_prev;
- struct net_device *orig_dev;
- int ret = NET_RX_DROP;
- __be16 type;
- /* if we've gotten here through NAPI, check netpoll */
- if (netpoll_receive_skb(skb))
- return NET_RX_DROP;
- if (!skb->tstamp.tv64)
- net_timestamp(skb);
- if (!skb->iif)
- skb->iif = skb->dev->ifindex;
- orig_dev = skb_bond(skb);
- if (!orig_dev)
- return NET_RX_DROP;
- __get_cpu_var(netdev_rx_stat).total++;
- skb_reset_network_header(skb);
- skb_reset_transport_header(skb);
- skb->mac_len = skb->network_header - skb->mac_header;
- pt_prev = NULL;
- rcu_read_lock();
- #ifdef CONFIG_NET_CLS_ACT
- if (skb->tc_verd & TC_NCLS) {
- skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
- goto ncls;
- }
- #endif
- list_for_each_entry_rcu(ptype, &ptype_all, list) {
- if (!ptype->dev || ptype->dev == skb->dev) {
- if (pt_prev)
- ret = deliver_skb(skb, pt_prev, orig_dev);
- pt_prev = ptype;
- }
- }
看到什么没有。最后,系统遍历了ptype_all这个链表。并且对元素们调用了
- static inline int deliver_skb(struct sk_buff *skb,
- struct packet_type *pt_prev,
- struct net_device *orig_dev)
- {
- atomic_inc(&skb->users);
- return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
- }
看出来什么没有。如果我们把自己挂到ptype_all上,不就能截获所有skb了吗?
某些嗅探器会选择在这里挂钩。大多数防火墙会选择在netfilter上挂钩。这里是skb要经过的第一个钩子。看了这张图就明白了。
标红色的地方,是我们挂钩的位置。这地方真够底层的啊。
以前做过一个东西跟网络相关的,不过没有hook到这里,如果是应用程序,一般都只hook到netfilter。一般的网络分析已经够用了。但我们是要做rootkit。netfilter就不能满足我们的要求了。
有人问“什么是skb?什么是net_device”
其实《Understand Linux Network Internals》和《Linux Device Driver》这两本书里都讲得很清楚。我们的rootkit对skb的操作也不复杂。遇到是自己的包,留下。不是自己的,丢掉。就跟投名状一样,是兄弟的,留下。不是兄弟的,杀了。
下篇文章将介绍怎样hook数据链路层,以截获所有的skb。本文说的一些概念如果不清楚的话,边看书,边看代码,过两天就会清楚了。我现在也不大清楚。如果说错了什么,希望高手严厉批评。