netfilter
ip/tables
防火墙技术分析讲义
标题: A new place to LKM:netfilter 作者: yawl <yawl@nsfocus.com> 时间: 2000-10
目录: -.前言 二.分析 三.例子代码 四.附录:与2.2在应用方面的区别简介 五.后记
-.前言
在linux2.2内核中的防火墙ipchains已经被用户广泛认可,它提供了完整的防火墙功能(包过滤,地址伪装,透明代理),又避免了商业防火墙那高的惊人的价格。如果你用的是某款国产防火墙,那么十有八九你实际在受到ipchains(有些甚至是2.0系列中ipfwadm)的保护:-).在未来的2.4内核中,被称为netfilter(http://netfilter.kernelnotes.org/)的防火墙以更好的结构重新构造,并实现了许多新功能,如完整的动态NAT(2.2内核实际是多对一的"地址伪装"),基于MAC及用户的过滤,真正的基于状态的过滤(不再是简单的查看tcp的标志位等),包速率限制等。 在原有的网络部分的LKM中,如果对网络部分进行处理,一般是先生成struct packet_type结构,在用dev_add_pack将其插入网络层(注意此时的packet_type实际相当于一个的三层的协议,如ip_packet_type,ipx_8023_packet_type等),具体的例子可参见phrack 55期<Building into the linux network layer>和本月小四写的月刊文章<利用LLKM处理网络通信----对抗IDS、Firewall>。 而netfilter本身在IP层内提供了另外的5个插入点(其文档中称为HOOK):NF_IP_PRE_ROUTING,NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,NF_IP_POST_ROUTING,分别对应IP层的五个不同位置,这样理论上在写lkm时便可以选择更适合的切入点,再辅以netfilter内置的新功能(如connect tracking),应该会帮助写出功能更强的lkm。 本来准备写出一个完整的例子(限制IP连接数),但计划总赶不上变化:-(,只好先贴出个简单的例子来,权且自我安慰成抛砖引玉了。 本文的参考配置是linux2.4.0-test4和iptable-1.1.1,好,开始抛砖,闪人喽!
二.分析 通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是用户自定义的功能)。 IP层的五个HOOK点的位置如下图所示(copy from <packet filter howto>) :
--->[1]--->[ROUTE]--->[3]--->[5]---> | ^ | | | [ROUTE] v | [2] [4] | ^ | | v | [local process]
[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测),源地址转换在此点 进行; [2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行; [3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行; [4]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行; [5]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的目的地址转换功能(包括地址伪 装)在此点进行。
在IP层代码中,有一些带有NF_HOOK宏的语句,如IP的转发函数中有:
<-ipforward.c ip_forward()-> NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2, ip_forward_finish);
其中NF_HOOK宏的定义提炼如下:
<-/include/linux/netfilter.h-> #ifdef CONFIG_NETFILTER #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ (list_empty(&nf_hooks[(pf)][(hook)]) \ ? (okfn)(skb) \ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn))) #else /* !CONFIG_NETFILTER */ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) #endif /*CONFIG_NETFILTER*/
如果在编译内核时没有配置netfilter时,就相当于调用最后一个参数,此例中即执行ip_forward_finish函数;否则进入HOOK点,执行通过nf_register_hook()登记的功能(这句话表达的可能比较含糊,实际是进入 nf_hook_slow()函数,再由它执行登记的函数)。
NF_HOOK宏的参数分别为: 1.pf:协议族名,netfilter架构同样可以用于IP层之外,因此这个变量还可以有诸如PF_INET6,PF_DECnet等名字。 2.hook:HOOK点的名字,对于IP层,就是取上面的五个值; 3.skb:不用多解释了吧; 4.indev:进来的设备,以struct net_device结构表示; 5.outdev:出去的设备,以struct net_device结构表示; (后面可以看到,以上五个参数将传到用nf_register_hook登记的处理函数中。) 6.okfn:是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程。
这些点是已经在内核中定义好的,除非你是这部分内核代码的维护者,否则无权增加或修改,而在此检测点进行的处理,则可由用户指定。像packet filter,NAT,connection track这些功能,也是以这种方式提供的。正如netfilter的当初的设计目标--提供一个完善灵活的框架,为扩展功能提供方便。
如果我们想加入自己的代码,便要用nf_register_hook函数,其函数原型为: int nf_register_hook(struct nf_hook_ops *reg) 我们考察一下struct nf_hook_ops结构:
struct nf_hook_ops { struct list_head list;
/* User fills in from here down. */ nf_hookfn *hook; int pf; int hooknum; /* Hooks are ordered in ascending priority. */ int priority; };
我们的工作便是生成一个struct nf_hook_ops结构的实例,并用nf_register_hook将其HOOK上。其中list项我们总要初始化为{NULL,NULL};由于一般在IP层工作,pf总是PF_INET;hooknum就是我们选择的HOOK点;一个HOOK点可能挂多个处理函数,谁先谁后,便要看优先级,即priority的指定了。netfilter_ipv4.h中用一个枚举类型指定了内置的处理函数的优先级:
enum nf_ip_hook_priorities { NF_IP_PRI_FIRST = INT_MIN, NF_IP_PRI_CONNTRACK = -200, NF_IP_PRI_MANGLE = -150, NF_IP_PRI_NAT_DST = -100, NF_IP_PRI_FILTER = 0, NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_LAST = INT_MAX, };
hook是提供的处理函数,也就是我们的主要工作,其原型为:
unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *));
它的五个参数将由NFHOOK宏传进去。 了解了这些,基本上便可以可以写一个lkm出来了。
三.例子代码
这段代码是一个例子,其功能实现了一个IDS,检测几个简单攻击(land,winnuke)和特殊扫描(nmap),当然,不会有人真把 它当严肃的IDS使用吧:-)。可以利用类似结构干点别的。。。
<-example.c begin->
/* * netfilter module example: it`s a kernel IDS(be quie,donot laugh, my friend) * yawl@nsfocus.com * Compile:gcc -O -c -Wall sample.c ,under linux2.4 kernel,netfilter is needed. */
#define __KERNEL__ #define MODULE
#include <linux/module.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/config.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/netfilter_ipv4.h>
#define ALERT(fmt,args...) printk("nsfocus: " fmt, ##args) /*message will be print to screen(too many~),and logged to /var/log/message*/
static unsigned int sample(unsigned int hooknum,struct sk_buff **skb, const struct net_device *in, const struct net_device *out,int (*okfn)(struct sk_buff *)) { struct iphdr *iph; struct tcphdr *tcph; struct udphdr *udph;
__u32 sip; __u32 dip; __u16 sport; __u16 dport;
iph=(*skb)->nh.iph; sip=iph->saddr; dip=iph->daddr;
/*play ip packet here (note:checksum has been checked,if connection track is enabled,defrag have been done )*/ if(iph->ihl!=5){ ALERT("IP packet with packet from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); }
if(iph->protocol==6){ tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl); sport=tcph->source; dport=tcph->dest; /*play tcp packet here*/ if((tcph->syn)&&(sport==dport)&&(sip==dip)){ ALERT("maybe land attack\n"); } if(ntohs(tcph->dest)==139&&tcph->urg){ ALERT("maybe winnuke a from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); } if(tcph->ece&&tcph->cwr){ ALERT("queso from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); } if((tcph->fin)&&(tcph->syn)&&(!tcph->rst)&&(!tcph->psh)&&(!tcph->ack)&&(!tcph->urg)){ ALERT("SF_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); } if((!tcph->fin)&&(!tcph->syn)&&(!tcph->rst)&&(!tcph->psh)&&(!tcph->ack)&&(!tcph->urg)){ ALERT("NULL_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); } if(tcph->fin&&tcph->syn&&tcph->rst&&tcph->psh&&tcph->ack&&tcph->urg){ ALERT("FULL_Xmas_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); } if((tcph->fin)&&(!tcph->syn)&&(!tcph->rst)&&(tcph->psh)&&(!tcph->ack)&&(tcph->urg)){ ALERT("XMAS_Scan(FPU)from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); } }
else if(iph->protocol==17){ udph=(struct udphdr *)((__u32 *)iph+iph->ihl); sport=udph->source; dport=udph->dest; /*play udp packet here*/ }
else if(iph->protocol==1){ /*play icmp packet here*/ }
else if(iph->protocol==2){ ALERT("igmp packet from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip)); /*play igmp packet here*/ }
else{ ALERT("unknown protocol%d packet from %d.%d.%d.%d to %d.%d.%d.%d\n",iph->protocol,NIPQUAD(sip),NIPQUAD(dip)); } return NF_ACCEPT; /*for it is IDS,we just accept all packet, if you really want to drop this skb,just return NF_DROP*/
}
static struct nf_hook_ops iplimitfilter ={ {NULL,NULL} ,sample,PF_INET,NF_IP_PRE_ROUTING,NF_IP_PRI_FILTER-1};
int init_module(void) { return nf_register_hook(&iplimitfilter); }
void cleanup_module(void) { nf_unregister_hook(&iplimitfilter); }
<-example.c end->
四.附录:与2.2在应用方面的区别简介
本来还想详细介绍一下iptables的用法,但如果说的太详细的话,还不如索性将HOWTO翻译一下,于是干脆了却了这个念头,只顺便简介一下与以前版本的变化(而且是我认为最重要的)。如果ipchains本来便没有在你的脑子中扎根,其实便没有必要看这部分。 netfilter,又可称为iptables.开发初期准备将packet filter和NAT的配置工具完全分开,一个称为iptables,另一个称为ipnatctl,而将整个项目成为netfilter.但后来可能是还是习惯2.2内核中用ipchians一个工具干两件事的用法,又改为全部用iptables配置了。 理论上还可以用2.2系列的ipchains和2.0系列的ipfwadm作配置工具,但只是做兼容或过渡的考虑了。通过源码看到他们也是通过现有的结构HOOK上去的(主要是net/ipv4/netfilter目录下的ip_fw_compat.c,ip_fw_compat_masq.c, ip_fw_compat_redir.c,ipchains_core.c,ipfwadm_core.c这几个文件)。 一个重要的变化是原有的INPUT,OUTPUT(原来是小写的input,ouput)链(现在应称为表?)的位置变了,原来的input,output的位置相当于现在的NF_IP_PRE_ROUTING,NF_IP_POST_ROUTING 。原有的结构确实不太合理,转发的包要经过三条链,现在INPUT专指去往本机的,OUPUT专指从本机发出的,而FOWARD仍旧是转发的包。 举两个简单的例子: 1.作地址伪装(场景:对外通过拨号连接internet)注意原来的MASQ变成好长的MASQUERATE,而伪装相当于SNAT,因此位置是在POSTROUTING:
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERATE
2.还有一个限制包速率的功能比较好玩,例如如下规则:
iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT
简单的说就是在转发时(-A FORWARD:因为是防火墙嘛),如果是tcp协议,且有syn标记(-p tcp --syn),可以限制为每秒一个(-m limit --limit 1/s ),行动项是ACCEPT。最后连起来意义就是每秒只允许转发一个tcp连接请求。
五.后记 netfilter还提供了许多新功能,如可以将包转发到应用层,由应用程序进行处理等,可目前我还没有分析多少,慢慢抽出点时间看吧。唉,尽管以前看过ipchains的代码,但netfilter实在变动太大了,一切都要从头看起:-( 最后,当然要感谢Rusty Russell,netfilter项目的负责人,不仅为我们提供了这个强大好用的工具,还写了大量非常优秀的文档。
参考文献:
[1.] Linux 2.4 Packet Filtering HOWTO Rusty Russell, mailing list netfilter@lists.samba.org v1.0.1 Mon May 1 18:09:31 CST 2000 [2.] Linux IPCHAINS-HOWTO Paul Russell, ipchains@rustcorp.com v1.0.7, Fri Mar 12 13:46:20 CST 1999 [3.] Linux 2.4 NAT HOWTO Rusty Russell, mailing list netfilter@lists.samba.org v1.0.1 Mon May 1 18:38:22 CST 2000 [4.] Linux netfilter Hacking HOWTO Rusty Russell, mailing list netfilter@lists.samba.org v1.0.1 Sat Jul 1 18:24:41 EST 2000 [5.] Writing a Module for netfilter by Paul "Rusty" Russell Linux Magazine June 2000 http://www.linux-mag.com/2000-06/gear_01.html [6.] Salvatore Sanfilippo<antirez@invece.org>写的一份netfilter sample,但可惜我找不到出处了,只剩下手头一份打印稿,But anyway,thanks to Salvatore.
ip/tables
1 /* 2 * 25-Jul-1998 Major changes to allow for ip chain table 3 * 4 * 3-Jan-2000 Named tables to allow packet selection for different uses. 5 */ 6 7 /* 8 * Format of an IP firewall descriptor 9 * 注意这里的说明:IP地址和掩码是按照网络字节存储(大端存储)标志字节和端口号是按照主机字节序存储(依主机硬件结构而定) 10 * src, dst, src_mask, dst_mask are always stored in network byte order. 11 * flags are stored in host byte order (of course). 12 * Port numbers are stored in HOST byte order. 13 */ 14 15 #ifndef _IPTABLES_H 16 #define _IPTABLES_H 17 18 #ifdef __KERNEL__ 19 #include linux/if.h 20 #include linux/types.h 21 #include linux/in.h 22 #include linux/ip.h 23 #include linux/skbuff.h 24 #endif 25 #include linux/netfilter_ipv4.h 26 27 #define IPT_FUNCTION_MAXNAMELEN 30 28 #define IPT_TABLE_MAXNAMELEN 32 29 这个结构存储与IP头部有关的防火墙规则信息。这里的注释说“这个结构无须填充零字节”,就是说这个结构的大小正好是4的倍数。这里由于IFNAMSIZ等于16,所以整个结构大小确实是4的倍数。 30 /* Yes, Virginia, you have to zero the padding. */ 31 struct ipt_ip { 32 /* Source and destination IP addr */ 33 struct in_addr src, dst; 34 /* Mask for src and dest IP addr */ 35 struct in_addr smsk, dmsk; 36 char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; 37 unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; 38 39 /* Protocol, 0 = ANY */ 40 u_int16_t proto; 41 42 /* Flags word */ 43 u_int8_t flags; 44 /* Inverse flags */ 45 u_int8_t invflags; 46 }; 47这个结构存储match的信息,这里的匹配主要是指与IP无关的防火墙规则信息。由系统缺省设置的匹配主要有三个“tcp”、“udp”,“icmp”,我在分析ip_tables.c时将详细描述。 48 struct ipt_entry_match 49 { 50 union { 51 struct { 52 u_int16_t match_size; 53 54 /* Used by userspace */ 55 char name[IPT_FUNCTION_MAXNAMELEN]; 56 } user; 57 struct { 58 u_int16_t match_size; 59 60 /* Used inside the kernel */ 61 struct ipt_match *match; 62 } kernel; 63 64 /* Total length */ 65 u_int16_t match_size; 66 } u; 67 68 unsigned char data[0]; 69 }; 70 target结构信息,是决定一个分组命运的信息。也可以理解为action信息,其意义是指当一个分组与rule和match信息匹配后,如何处置该分组。处置方法一般有三种:一,命令常数,比如DROP ACCEPT等等;二 系统预定义的模块处理函数,比如”SNAT DNAT"等等;第三种是用户自己写模块函数。 71 struct ipt_entry_target 72 { 73 union { 74 struct { 75 u_int16_t target_size; 76 77 /* Used by userspace */ 78 char name[IPT_FUNCTION_MAXNAMELEN]; 79 } user; 80 struct { 81 u_int16_t target_size; 82 83 /* Used inside the kernel */ 84 struct ipt_target *target; 85 } kernel; 86 87 /* Total length */ 88 u_int16_t target_size; 89 } u; 90 91 unsigned char data[0]; 92 }; 93这个结构已经很明显给出了target的形式:命令常数、或者模块函数。 94 struct ipt_standard_target 95 { 96 struct ipt_entry_target target; 97 int verdict; 98 }; 99 计数器结构,每一个rule都有一个计数器结构用来统计匹配该条规则的分组数目和字节数目。为基于统计的安全工具提供分析基础。 100 struct ipt_counters 101 { 102 u_int64_t pcnt, bcnt; /* Packet and byte counters */ 103 }; 104 标志字段,各个常数后面的注释已经给出了明确的解释,这里不再赘述。 105 /* Values for "flag" field in struct ipt_ip (general ip structure). */ 106 #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ 107 #define IPT_F_MASK 0x01 /* All possible flag bits mask. */ 108 109 /* Values for "inv" field in struct ipt_ip. */ 110 #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ 111 #define IPT_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ 112 #define IPT_INV_TOS 0x04 /* Invert the sense of TOS. */ 113 #define IPT_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ 114 #define IPT_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ 115 #define IPT_INV_FRAG 0x20 /* Invert the sense of FRAG. */ 116 #define IPT_INV_PROTO 0x40 /* Invert the sense of PROTO. */ 掩码标志。用法是当出现超出掩码范围的标志时,确认是非法标志。 117 #define IPT_INV_MASK 0x7F /* All possible flag bits mask. */ 118 其实这个结构的构成这里的注释已经说的很清楚,但是从论坛上有人问"关于netfilter的问题“时,可以看出很多人还是不理解。与前面ipchains版本防火墙不同的是iptables的防火墙规则构成发生了变化。ipchains的构成是rule+target,而iptables的构成是ip匹配信息+match+target。同时iptables构成的每一个部分都是可变大小的,由于经常出现”char XXX[0]“就可以看出。但是我个人认为规则的组织有点不好理解,它经常是先分配一段空间,然后将规则一条一条放入。如同文件系统存放变长记录的文件时,总要在记录中放入记录长度,以便以后取出记录,这里iptables正是使用这种方法,在每个规则中都放入长度字段,这样方便提取各个组成部分和计算下一条规则的位置。 119 /* This structure defines each of the firewall rules. Consists of 3 120 parts which are 1) general IP header stuff 2) match specific 121 stuff 3) the target to perform if the rule matches */ 122 struct ipt_entry 123 { 124 struct ipt_ip ip; 125 126 /* Mark with fields that we care about. */ 127 unsigned int nfcache; 128下面两个字段用来计算target的位置和下一条规则的位置。 129 /* Size of ipt_entry + matches */ 130 u_int16_t target_offset; 131 /* Size of ipt_entry + matches + target */ 132 u_int16_t next_offset; 133 这个字段的存在,为发现规则中存在”环路“提供手段。 134 /* Back pointer */ 135 unsigned int comefrom; 136 137 /* Packet and byte counters. */ 138 struct ipt_counters counters; 139 140 /* The matches (if any), then the target. */ 141 unsigned char elems[0]; 142 }; 143 144 /* 145 * New IP firewall options for [gs]etsockopt at the RAW IP level. 146 * Unlike BSD Linux inherits IP options so you don't have to use a raw 147 * socket for this. Instead we check rights in the calls. */ 定义提供给set/getsockopt系统调用的命令常数的基常数。 148 #define IPT_BASE_CTL 64 /* base for firewall socket options */ 149 150 #define IPT_SO_SET_REPLACE (IPT_BASE_CTL) 151 #define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1) 152 #define IPT_SO_SET_MAX IPT_SO_SET_ADD_COUNTERS 153 154 #define IPT_SO_GET_INFO (IPT_BASE_CTL) 155 #define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) 156 #define IPT_SO_GET_MAX IPT_SO_GET_ENTRIES 157 158 /* CONTINUE verdict for targets */ 159 #define IPT_CONTINUE 0xFFFFFFFF 160 161 /* For standard target */ 162 #define IPT_RETURN (-NF_MAX_VERDICT - 1) 163 Tcp匹配规则信息。 164 /* TCP matching stuff */ 165 struct ipt_tcp 166 { 167 u_int16_t spts[2]; /* Source port range. */ 168 u_int16_t dpts[2]; /* Destination port range. */ 169 u_int8_t option; /* TCP Option iff non-zero*/ 170 u_int8_t flg_mask; /* TCP flags mask byte */ 171 u_int8_t flg_cmp; /* TCP flags compare byte */ 172 u_int8_t invflags; /* Inverse flags */ 173 }; 174 tcp的取反标志值。 175 /* Values for "inv" field in struct ipt_tcp. */ 176 #define IPT_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ 177 #define IPT_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ 178 #define IPT_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */ 179 #define IPT_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */ 180 #define IPT_TCP_INV_MASK 0x0F /* All possible flags. */ 181 udp匹配规则信息 182 /* UDP matching stuff */ 183 struct ipt_udp 184 { 185 u_int16_t spts[2]; /* Source port range. */ 186 u_int16_t dpts[2]; /* Destination port range. */ 187 u_int8_t invflags; /* Inverse flags */ 188 }; 189 190 /* Values for "invflags" field in struct ipt_udp. */ 191 #define IPT_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ 192 #define IPT_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ 193 #define IPT_UDP_INV_MASK 0x03 /* All possible flags. */ 194 195 /* ICMP matching stuff */ ICMP匹配规则信息 196 struct ipt_icmp 197 { 198 u_int8_t type; /* type to match */ 199 u_int8_t code[2]; /* range of code */ 200 u_int8_t invflags; /* Inverse flags */ 201 }; 202 203 /* Values for "inv" field for struct ipt_icmp. */ 204 #define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */ 205 这个结构实质上用户通过getsockopt系统调用获取table信息时所传递参数的类型。 206 /* The argument to IPT_SO_GET_INFO */ 207 struct ipt_getinfo 208 { 209 /* Which table: caller fills this in. */ 210 char name[IPT_TABLE_MAXNAMELEN]; 211 212 /* Kernel fills these in. */ 213 /* Which hook entry points are valid: bitmask */ 214 unsigned int valid_hooks; 215 216 /* Hook entry points: one per netfilter hook. */ 217 unsigned int hook_entry[NF_IP_NUMHOOKS]; 218 219 /* Underflow points. */ 220 unsigned int underflow[NF_IP_NUMHOOKS]; 221 222 /* Number of entries */ 223 unsigned int num_entries; 224 225 /* Size of entries. */ 226 unsigned int size; 227 }; 228 这个结构是用户通过系统调用更换table是所传递的参数类型。 229 /* The argument to IPT_SO_SET_REPLACE. */ 230 struct ipt_replace 231 { 232 /* Which table. */ 233 char name[IPT_TABLE_MAXNAMELEN]; 234 235 /* Which hook entry points are valid: bitmask. You can't 236 change this. */ 237 unsigned int valid_hooks; 238 239 /* Number of entries */ 240 unsigned int num_entries; 241 242 /* Total size of new entries */ 243 unsigned int size; 244 245 /* Hook entry points. */ 246 unsigned int hook_entry[NF_IP_NUMHOOKS]; 247 248 /* Underflow points. */ 249 unsigned int underflow[NF_IP_NUMHOOKS]; 250 251 /* Information about old entries: */ 252 /* Number of counters (must be equal to current number of entries). */ 253 unsigned int num_counters; 254 /* The old entries' counters. */ 255 struct ipt_counters *counters; 256 257 /* The entries (hang off end: not really an array). */ 258 struct ipt_entry entries[0]; 259 }; 260 这个更改计数器时传递的参数类型。 261 /* The argument to IPT_SO_ADD_COUNTERS. */ 262 struct ipt_counters_info 263 { 264 /* Which table. */ 265 char name[IPT_TABLE_MAXNAMELEN]; 266 267 unsigned int num_counters; 268 269 /* The counters (actually `number' of these). */ 270 struct ipt_counters counters[0]; 271 }; 272 这个是想获取防火墙规则时,传递给系统调用的参数类型。 273 /* The argument to IPT_SO_GET_ENTRIES. */ 274 struct ipt_get_entries 275 { 276 /* Which table: user fills this in. */ 277 char name[IPT_TABLE_MAXNAMELEN]; 278 279 /* User fills this in: total entry size. */ 280 unsigned int size; 281 282 /* The entries. */ 283 struct ipt_entry entrytable[0]; 284 }; 285 286 /* Standard return verdict, or do jump. */ 287 #define IPT_STANDARD_TARGET "" 288 /* Error verdict. */ 289 #define IPT_ERROR_TARGET "ERROR" 290 现面定义了一些使用例程 291 /* Helper functions */ 获取一条防火墙规则的target位置 292 extern __inline__ struct ipt_entry_target * 293 ipt_get_target(struct ipt_entry *e) 294 { 295 return (void *)e + e->target_offset; 296 } 297 下面的宏遍历处理一条防火墙规则的所有匹配。我已经说过每一条防火墙规则在iptables中分为三部分,而且每一部分的大小都是可变的。比如match部分,它本身可以有多个match项。 298 /* fn returns 0 to continue iteration */ 299 #define IPT_MATCH_ITERATE(e, fn, args...) \ 300 ({ \ 301 unsigned int __i; \ 302 int __ret = 0; \ 303 struct ipt_entry_match *__m; \
这个for语句我来解释一下:首先__i取值为ipt_entry结构的大小,实质上就是match匹配的开始处的偏移地址,将其与e相加就得到了match匹配的地址,然后调用fn处理这个匹配。如果函数返回值为零,当前匹配的偏移地址加上当前匹配的大小,如果不超过target的偏移地址,则继续处理下一条匹配。 304 \ 305 for (__i = sizeof(struct ipt_entry); \ 306 __i target_offset; \ 307 __i += __m->u.match_size) { \ 308 __m = (void *)(e) + __i; \ 309 \ 310 __ret = fn(__m , ## args); \ 311 if (__ret != 0) \ 312 break; \ 313 } \ 314 __ret; \ 315 }) 316 这个宏处理一个table中的所有防火墙规则。对比对上一个宏的理解,这里我就不解释了。 317 /* fn returns 0 to continue iteration */ 318 #define IPT_ENTRY_ITERATE(entries, size, fn, args...) \ 319 ({ \ 320 unsigned int __i; \ 321 int __ret = 0; \ 322 struct ipt_entry *__e; \ 323 \ 324 for (__i = 0; __i next_offset) { \ 325 __e = (void *)(entries) + __i; \ 326 \ 327 __ret = fn(__e , ## args); \ 328 if (__ret != 0) \ 329 break; \ 330 } \ 331 __ret; \ 332 }) 333 334 /* 335 * Main firewall chains definitions and global var's definitions. 336 */ 337 #ifdef __KERNEL__ 338 339 #include · 340 extern void ipt_init(void) __init; 341 所有的匹配处理都注册到一个match处理链表中,链表结点的类型就是这里的结构类型。当处理匹配时都是调用这里注册的处理函数。每个结点实质上由三个函数构成,一个匹配处理函数,一个合法性检查函数,一个析构函数。最后一个是反身指针,指针的作用如注释所示。 342 struct ipt_match 343 { 344 struct list_head list; 345 346 const char name[IPT_FUNCTION_MAXNAMELEN]; 347 348 /* Return true or false: return FALSE and set *hotdrop = 1 to 349 force immediate packet drop. */ 350 int (*match)(const struct sk_buff *skb, 351 const struct net_device *in, 352 const struct net_device *out, 353 const void *matchinfo, 354 int offset, 355 const void *hdr, 356 u_int16_t datalen, 357 int *hotdrop); 358 359 /* Called when user tries to insert an entry of this type. */ 360 /* Should return true or false. */ 361 int (*checkentry)(const char *tablename, 362 const struct ipt_ip *ip, 363 void *matchinfo, 364 unsigned int matchinfosize, 365 unsigned int hook_mask); 366 367 /* Called when entry of this type deleted. */ 368 void (*destroy)(void *matchinfo, unsigned int matchinfosize); 369 370 /* Set this to THIS_MODULE if you are a module, otherwise NULL */ 371 struct module *me; 372 }; 373 和match一样,所有的target都注册到这个结构类型的全局链表中,每个target的处理函数都是这里注册的函数。和上面的解释一样,这里也主要包含三个函数指针。 374 /* Registration hooks for targets. */ 375 struct ipt_target 376 { 377 struct list_head list; 378 379 const char name[IPT_FUNCTION_MAXNAMELEN]; 380 381 /* Returns verdict. */ 382 unsigned int (*target)(struct sk_buff **pskb, 383 unsigned int hooknum, 384 const struct net_device *in, 385 const struct net_device *out, 386 const void *targinfo, 387 void *userdata); 388 389 /* Called when user tries to insert an entry of this type: 390 hook_mask is a bitmask of hooks from which it can be 391 called. */ 392 /* Should return true or false. */ 393 int (*checkentry)(const char *tablename, 394 const struct ipt_entry *e, 395 void *targinfo, 396 unsigned int targinfosize, 397 unsigned int hook_mask); 398 399 /* Called when entry of this type deleted. */ 400 void (*destroy)(void *targinfo, unsigned int targinfosize); 401 402 /* Set this to THIS_MODULE if you are a module, otherwise NULL */ 403 struct module *me; 404 }; 405 注册函数 406 extern int ipt_register_target(struct ipt_target *target); 407 extern void ipt_unregister_target(struct ipt_target *target); 408 409 extern int ipt_register_match(struct ipt_match *match); 410 extern void ipt_unregister_match(struct ipt_match *match); 411 table结构 412 /* Furniture shopping... */ 413 struct ipt_table 414 { 415 struct list_head list; 416 417 /* A unique name... */ 418 char name[IPT_TABLE_MAXNAMELEN]; 419 420 /* Seed table: copied in register_table */ 421 struct ipt_replace *table; 422 423 /* What hooks you will enter on */ 424 unsigned int valid_hooks; 425 426 /* Lock for the curtain */ 427 rwlock_t lock; 428 429 /* Man behind the curtain... */ 430 struct ipt_table_info *private; 431 }; 432 小结iptables的防火墙组织结构:
现在我们可以给出iptables防火墙规则的组织结构了。第一级是table级,每一个防火墙可以有多个table;第二级是hook级,每一个table都有一个hook集合,每个hook都有一个防火墙规则链;第三级基本规则级,基本规则级的规则包括三部分,IP规则信息、匹配规则信息和target。而这三个组成部分的每一个都可以包括同类型的多个部分规则。 433 extern int ipt_register_table(struct ipt_table *table); 434 extern void ipt_unregister_table(struct ipt_table *table); 435 extern unsigned int ipt_do_table(struct sk_buff **pskb, 436 unsigned int hook, 437 const struct net_device *in, 438 const struct net_device *out, 439 struct ipt_table *table, 440 void *userdata); 441 有关table结构对齐的宏。
442 #define IPT_ALIGN(s) (((s) + (__alignof__(struct ipt_entry)-1)) ~(__alignof__(struct ipt_entry)-1)) 443 #endif /*__KERNEL__*/ 444 #endif /* _IPTABLES_H */ 445
西安交通大学 王灏
防火墙技术分析讲义
防火墙技术分析讲义 yawl@docshow.net
一 基本概念
1.1 防火墙分类: 包过滤 代理(应用层网关)
1.2 代理:
两个连接(browser与proxy之间,proxy与web server之间)。 工作在应用层。
直接发往服务器的包: GET / HTTP/1.1 Accept: */* Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) Host: www.lisoleg.net Connection: Keep-Alive
往代理发出的包: GET http://www.lisoleg.net/ HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: zh-cn Accept-Encoding: gzip, deflate If-Modified-Since: Thu, 14 Dec 2000 07:24:52 GMT If-None-Match: "8026-185-3a3875c4" User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) Host: www.lisoleg.net Proxy-Connection: Keep-Alive
增强: cache
1.3 包过滤 单IP包检测 缺陷:无状态
1.4 增强1-状态检测(Stateful Inspection),又称动态包过滤(dynamic packet filtering) 1.4.1 规则表和动态状态表
1.4.2 ftp的例子: A 4847->B 21 PORT 192,168,7,60,18,241 B 21->A 4847 PORT command successful.
B 20->A 4849 syn > A classic example is transferring files using FTP. The firewall remembers the details of the > incoming request to get a file from an FTP server. The firewall then tracks the back-channel > request (the FTP Port command) by the server for transferring information back to the client. > As long as the information agrees (same IP addresses, no changes in port numbers, and no > non-FTP requests), the firewall allows the traffic. After the transfer is complete, the > firewall closes the ports involved.
1.4.3 两种实现方法: 1.4.3.1 checkpoint FW1,netfilter 1.4.3.2 动态添加规则(ipchains patch) > I believe it does exactly what I want: Installing a temporary > "backward"-rule to let packets in as a response to an > outgoing request.
1.5 增强2-地址转换: 1.5.1 静态NAT 1.5.2 动态NAT 1.5.3 地址伪装
1.6 增强3-VPN: 位置的优越性
二 Linux下防火墙的实现之一(2.2内核):
2.1 截获位置: 网络层
---------------------------------------------------------------- | ACCEPT/ lo interface | v REDIRECT _______ | --> C --> S --> ______ --> D --> ~~~~~~~~ -->|forward|----> _______ --> h a |input | e {Routing } |Chain | |output |ACCEPT e n |Chain | m {Decision} |_______| --->|Chain | c i |______| a ~~~~~~~~ | | ->|_______| k t | s | | | | | s y | q | v | | | u | v e v DENY/ | | v m | DENY/ r Local Process REJECT | | DENY/ | v REJECT a | | | REJECT | DENY d --------------------- | v e ----------------------------- DENY
2.2 提炼出的代码:
输入检测: /* * Main IP Receive routine. */ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { #ifdef CONFIG_FIREWALL int fwres; u16 rport; #endif /* CONFIG_FIREWALL */
......
#ifdef CONFIG_FIREWALL /* * See if the firewall wants to dispose of the packet. * * We can't do ICMP reply or local delivery before routing, * so we delay those decisions until after route. --RR */ fwres = call_in_firewall(PF_INET, dev, iph, &rport, &skb); if (fwres < FW_ACCEPT && fwres != FW_REJECT) goto drop; iph = skb->nh.iph; #endif /* CONFIG_FIREWALL */
......
#ifdef CONFIG_FIREWALL if (fwres == FW_REJECT) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); goto drop; } #endif /* CONFIG_FIREWALL */
return skb->dst->input(skb); //根据路由查找的结果决定是转发(ip_forward)还是发往上层(ip_local_deliver) drop: kfree_skb(skb); //如果规则匹配的结果是FW_REJECT,FW_BLOCK,丢弃此包 return(0);
}
转发检测: int ip_forward(struct sk_buff *skb) { ... #ifdef CONFIG_FIREWALL fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb); switch (fw_res) { case FW_ACCEPT: case FW_MASQUERADE: break; case FW_REJECT: icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); /* fall thru */ default: kfree_skb(skb); return -1; } #endif ... }
输出检测:(不同的上层协议走不同的流程,因此检测点较多) UDP/RAW/ICMP报文:ip_build_xmit TCP报文:ip_queue_xmit 转发的包:ip_forward 其他:ip_build_and_send_pkt
实际的匹配: /* * Returns one of the generic firewall policies, like FW_ACCEPT. * * The testing is either false for normal firewall mode or true for * user checking mode (counters are not updated, TOS & mark not done). */ static int ip_fw_check(struct iphdr *ip, //IP头位置 const char *rif, //出口网卡的名字 __u16 *redirport, //端口转发时用到 struct ip_chain *chain, //规则链的名字 struct sk_buff *skb, //要检测的数据包 unsigned int slot, int testing) //见函数本身的注释
调用举例: call_in_firewall实际调用ipfw_input_check,而ipfw_input_check中有: int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb) { return ip_fw_check(phdr, dev->name, arg, IP_FW_INPUT_CHAIN, *pskb, SLOT_NUMBER(), 0); }
实际流程:
ip_fw_check { 从传入的skb参数中提取源地址src,目的地址dst,源端口src_port,目的端口dst_port, TCP发起连接标志tcpsyn,分片包位移offset,IP包TOS消息oldtos;
......
f = chain->chain; //取出规则链的的一条规则,规则链由chain参数传入 count = 0; do { for (; f; f = f->next) { //遍历规则链中的规则,直到匹配(ip_rule_match返回1) count++; if (ip_rule_match(f,rif,ip, tcpsyn,src_port,dst_port,offset)) { if (!testing && !ip_fw_domatch(f, ip, rif, chain->label,//作些标记,一般返回1 skb, slot, src_port, dst_port, count, tcpsyn)) { ret = FW_BLOCK; goto out; } break; } } if(f) { //找到匹配规则
......
}else { //这次遍历根本没找到 是从别的地方跳转过来的,则转回去,然后继续遍历; 否则应用这条链的缺省规则; }
} while (ret == FW_SKIP+2); out:
......
return ret; }
碎片: 根据第一个片的消息进行过滤,其他分片则允许通过。如果规则是丢弃的话,虽然后面的分片都可到达主机, 但由于第一片被滤掉了,无法重组成功,因此从效果上也相当于整个IP包被丢弃。
存在的漏洞等.
2.3 规则:
from 192.168.7.0/24 to 192.168.6.32/32 tcp 80 BLOCK
规则的数据结构表示:
规则链 struct ip_chain { ip_chainlabel label; /* Defines the label for each block */ struct ip_chain *next; /* Pointer to next block */ struct ip_fwkernel *chain; /* Pointer to first rule in block */ __u32 refcount; /* Number of refernces to block */ int policy; /* Default rule for chain. Only * * used in built in chains */ struct ip_reent reent[0]; /* Actually several of these */ };
规则 struct ip_fwkernel { struct ip_fw ipfw; struct ip_fwkernel *next; /* where to go next if current * rule doesn't match */ struct ip_chain *branch; /* which branch to jump to if * current rule matches */ int simplebranch; /* Use this if branch == NULL */ struct ip_counters counters[0]; /* Actually several of these */ };
待匹配的数据包消息 struct ip_fw { struct in_addr fw_src, fw_dst; /* Source and destination IP addr */ struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */ __u32 fw_mark; /* ID to stamp on packet */ __u16 fw_proto; /* Protocol, 0 = ANY */ __u16 fw_flg; /* Flags word */ __u16 fw_invflg; /* Inverse flags */ __u16 fw_spts[2]; /* Source port range. */ __u16 fw_dpts[2]; /* Destination port range. */ __u16 fw_redirpt; /* Port to redirect to. */ __u16 fw_outputsize; /* Max amount to output to NETLINK */ char fw_vianame[IFNAMSIZ]; /* name of interface "via" */ __u8 fw_tosand, fw_tosxor; /* Revised packet priority */ };
2.4 地址转换 ip_fw_demasquerade ip_fw_masquerade
三 Linux下防火墙的实现之二(2.4内核): 3.1 A Packet Traversing the Netfilter System:
--->PRE------>[ROUTE]--->FWD---------->POST------> Conntrack | Filter ^ NAT (Src) Mangle | | Conntrack NAT (Dst) | [ROUTE] (QDisc) v | IN Filter OUT Conntrack | Conntrack ^ Mangle | | NAT (Dst) v | Filter
3.2 例子
## Insert connection-tracking modules (not needed if built into kernel). # insmod ip_conntrack # insmod ip_conntrack_ftp
## Create chain which blocks new connections, except if coming from inside. # iptables -N block # iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT # iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT # iptables -A block -j DROP
## Jump to that chain from INPUT and FORWARD chains. # iptables -A INPUT -j block # iptables -A FORWARD -j block
3.3 规则的描述 一条规则分为三部分: struct ipt_entry //主要用来匹配IP头 struct ip_match //额外的匹配(tcp头,mac地址等) struct ip_target //除缺省的动作外(如ACCEPT,DROP),可以增加新的(如REJECT)。
3.4 代码提炼
ip_input.c: /* * Main IP Receive routine. */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { ... return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); ... }
netfilter.h: #ifdef CONFIG_NETFILTER #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ (list_empty(&nf_hooks[(pf)][(hook)]) \ ? (okfn)(skb) \ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn))) #else /* !CONFIG_NETFILTER */ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) #endif /*CONFIG_NETFILTER*/
大的框架:"HOOK表": struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]; //netfilter.c 通过nf_register_hook和nf_unregister_hook完成添加删除工作,nf_iterate负责执行hook上的函数。
增加用户自定义的HOOK,参见【8】,【10】:
重要流程(建议结合netfilter hacking howto 4.1.3来看): /* Returns one of the generic firewall policies, like NF_ACCEPT. */ unsigned int ipt_do_table(struct sk_buff **pskb, unsigned int hook, const struct net_device *in, const struct net_device *out, struct ipt_table *table, void *userdata) { struct ipt_entry *e; struct ipt_entry_target *t; unsigned int verdict = NF_DROP;
table_base = (void *)table->private->entries + TABLE_OFFSET(table->private, cpu_number_map(smp_processor_id())); e = get_entry(table_base, table->private->hook_entry[hook]);
... ip_packet_match(ip, indev, outdev, &e->ip, offset);
... IPT_MATCH_ITERATE(e, do_match, *pskb, in, out, offset, protohdr, datalen, &hotdrop)
... t = ipt_get_target(e);
... verdict = t->u.kernel.target->target(pskb, hook, in, out, t->data, userdata);//非标准的target走这一步
... return verdict; }
要加强对这段话的理解(netfilter hacking howto 4.1节) : >iptables does not register with any netfilter hooks: it relies on >other modules to do that and feed it the packets as appropriate; a >module must register the netfilter hooks and ip_tables separately, and >provide the mechanism to call ip_tables when the hook is reached.
四 Linux下防火墙的实现之三(checkpoint FW1)
让我们看看checkpoint的在linux上的防火墙是如何实现的,最终我们会发现,竟然和lkm使用的手段差不多:)
fw1通过dev_add_pack的办法加载输入过滤函数,但是在net_bh()中,传往网络层的skbuff是克隆的,即 skb2=skb_clone(skb, GFP_ATOMIC); if(skb2) pt_prev->func(skb2, skb->dev, pt_prev); 而fw1是怎么解决这个问题的呢?见下面的代码:
输入一:
; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? align 4
; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
; Attributes: bp-based frame
public fwinstallin fwinstallin proc near ; CODE XREF: fwinstall+E9p ; fwinstall+149p
var_18 = byte ptr -18h arg_0 = dword ptr 8
push ebp mov ebp, esp sub esp, 10h push esi push ebx mov esi, ds:dev_base cmp [ebp+arg_0], 0 jz short loc_0_802CBD0 add esp, 0FFFFFFF4h push offset fw_ip_packet_type call dev_add_pack mov ebx, fw_ip_packet_type+10h ;如果考虑字节对齐问题的话fw_ip_packet_type+10h这时应该是ip_packet_type mov dword ptr ds:fw_type_list, ebx jmp short loc_0_802CB9C ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? align 4
loc_0_802CB90: ; CODE XREF: fwinstallin+41j add esp, 0FFFFFFF4h push ebx call dev_remove_pack ;fw1把ip_packet_type歇载掉了,然后自己在自己的处理函数(fw_filterin)中调ip_recv mov ebx, [ebx+10h]
loc_0_802CB9C: ; CODE XREF: fwinstallin+2Dj add esp, 10h test ebx, ebx jnz short loc_0_802CB90 test esi, esi jz short loc_0_802CC14
loc_0_802CBA7: ; CODE XREF: fwinstallin+68j test byte ptr fwdebug, 81h jz short loc_0_802CBC3 add esp, 0FFFFFFF8h mov eax, [esi] push eax push offset aFwinstallinS ; "fwinstallin: %s\n" call fwkdebug_printf add esp, 10h
loc_0_802CBC3: ; CODE XREF: fwinstallin+4Ej mov esi, [esi+28h] test esi, esi jnz short loc_0_802CBA7 jmp short loc_0_802CC14 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? align 8
loc_0_802CBD0: ; CODE XREF: fwinstallin+12j cmp dword ptr ds:fw_type_list, 0 jz short loc_0_802CC14 add esp, 0FFFFFFF4h push offset fw_ip_packet_type call dev_remove_pack add esp, 10h cmp dword ptr ds:fw_type_list, 0 jz short loc_0_802CC14
loc_0_802CBF2: ; CODE XREF: fwinstallin+B2j add esp, 0FFFFFFF4h mov eax, dword ptr ds:fw_type_list push eax call dev_add_pack mov eax, dword ptr ds:fw_type_list add esp, 10h mov eax, [eax+10h] mov dword ptr ds:fw_type_list, eax test eax, eax jnz short loc_0_802CBF2
loc_0_802CC14: ; CODE XREF: fwinstallin+45j ; fwinstallin+6Aj ... lea esp, [ebp+var_18] xor eax, eax pop ebx pop esi mov esp, ebp pop ebp retn fwinstallin endp
输入二: public fw_ip_packet_type fw_ip_packet_type dd 8, 0, offset fw_filterin, 2 dup(0) ; DATA XREF: fwinstallin+17o
输出的挂载和lkm的手法一样,更改dev->hard_start_xmit。dev结构在2.2版本的发展过程中变了一次, 为了兼容fw1对这点也做了处理。
输出一: ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? align 4
; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
; Attributes: bp-based frame
public fwinstallout fwinstallout proc near ; CODE XREF: fwinstall+FBp ; fwinstall+153p
var_18 = byte ptr -18h arg_0 = dword ptr 8
push ebp mov ebp, esp sub esp, 0Ch push edi push esi push ebx mov edi, [ebp+arg_0] xor esi, esi mov ebx, ds:dev_base jmp short loc_0_802D0A8 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
loc_0_802D096: ; CODE XREF: fwinstallout+50j add esp, 0FFFFFFFCh push edi push esi push ebx call installout_on_device add esp, 10h mov ebx, [ebx+28h] inc esi
loc_0_802D0A8: ; CODE XREF: fwinstallout+14j test ebx, ebx &n |