📄 542.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>apue</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> ● UNIX网络编程 (BM: clown) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="516.htm">上一层</a>][<a href="543.htm">下一篇</a>]
<hr><p align="left"><small>: scz 于 2001-1-31 21:26:25 加贴在 绿盟科技论坛(bbs.nsfocus.com)--UNIX系统安全: <br>
: <br>
libpcap的连载(我写的那个)就一篇,还是为了移植性被迫用了一回libpcap <br>
其余的连载1-12是libnet。不大清楚你是什么地方不清楚。pcap_t这个结构 <br>
吗?如果把问题具体化,可以帮助你的朋友就会翻倍。随便说两句好了, <br>
libpcap抓到单包后可能会在其前部增加一个结构头,用于描述后面这个包, <br>
比如本来的长度、截断后的长度(根据snaplen截断)、抓取的时间等等。 <br>
所以你不能直接按照单包间隔处理连续的报文流,必须越过这个pcap头。 <br>
至于bpf,下面是以前在华中地区网络中心security版写过的原文,后来没有 <br>
修正过,可能有点小毛病,懒得仔细检查了。你自己看看是否有帮助 <br>
标题:内核包捕获过滤机制介绍 <br>
关于bpf的详细内容请参看 << TCP/IP Illustrated, Volume 2 >> <br>
可以去看看这个主页 http://bobdai.126.com ,在这个主页上讲述了一些bpf的内容 <br>
(中文),虽然没有太多深入分析,但关于设置bpf filter的部分还是很清楚的。关于 <br>
bpf的编程实例就不要乱找了,tcpdump和libpcap的源代码就是。下面就bpf过滤机制 <br>
再介绍介绍。前面提到的主页不提供这种源代码,可能有想法,其实libpcap已经够 <br>
详细了。 <br>
dlpi和sock_packet都没有内核过滤机制么?tcpdump对dlpi并没有使用pfmod这种技 <br>
术,而是使用了用户空间的过滤机制。bpf提供了内核空间的过滤机制,类似于下面 <br>
的代码将设置内核过滤函数。 <br>
int pcap_setfilter ( struct bpf_program * fp ) <br>
{ <br>
if ( ioctl( if_eth_fd, BIOCSETF, ( caddr_t )fp ) < 0 ) <br>
{ <br>
fprintf( outputFile, "BIOCSETF error: %s\n", strerror( errno ) ); <br>
exit( FAILURE ); <br>
} <br>
return( SUCCESS ); <br>
} /* end of pcap_setfilter */ <br>
find /usr/include -name "*.h" | xargs grep "struct bpf_program" <br>
得到如下输出: <br>
/usr/include/net/bpf.h:struct bpf_program { <br>
/* Structure for BIOCSETF. */ <br>
struct bpf_program <br>
{ <br>
u_int bf_len; <br>
struct bpf_insn * bf_insns; <br>
}; <br>
我们可以举个例子: <br>
/* 这是一个简单的icmp过滤,目标IP要求是192.168.7.90 */ <br>
struct bpf_insn icmp_insns[] = <br>
{ <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 12 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, ETH_P_IP, 0, 5 ), <br>
BPF_STMT( BPF_LD + BPF_W + BPF_ABS, 30 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 0xc0a8075a, 0, 3 ), <br>
BPF_STMT( BPF_LD + BPF_B + BPF_ABS, 23 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 1 ), <br>
BPF_STMT( BPF_RET + BPF_K, 128 ), <br>
BPF_STMT( BPF_RET + BPF_K, 0 ), <br>
}; <br>
struct bpf_program icmp_filter = <br>
{ <br>
8, <br>
icmp_insns, <br>
}; <br>
BPF_B 1bytes <br>
BPF_H 2bytes <br>
BPF_W 4bytes <br>
BPF_IND 相对偏移 <br>
BPF_ABS 相对于物理帧头的绝对偏移 <br>
BPF_JEQ 相等则转移 <br>
BPF_JA <br>
BPF_JGT <br>
第一句 BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 12 ) <br>
将物理帧的第12字节(从0开始)处的两个字节送入累加器。 <br>
第二句 BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, ETH_P_IP, 0, 5 ) <br>
将累加器的内容与常数 ETH_P_IP 比较,相等则PC(Program Counter)加0(相当于继 <br>
续执行第三句),否则PC加3跳到第八句。ETH_P_IP 等于 0x0800 。 <br>
第三句 BPF_STMT( BPF_LD + BPF_W + BPF_ABS, 30 ) <br>
将物理帧的第30字节处的4个字节送入累加器,这32位数据正是目标IP地址 <br>
第四句 BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 0xc0a8075a, 0, 3 ) <br>
将累加器的内容与常数 0xc0a8075a 比较,相等则执行第五句,否则执行第八句。注 <br>
意这里 0xc0a8075a 对应IP地址192.168.7.90,这里使用了网络字节顺序。 <br>
第五句 BPF_STMT( BPF_LD + BPF_B + BPF_ABS, 23 ) <br>
将物理帧的第23字节处的1个字节送入累加器,这个字节是高层协议类型 <br>
第六句 BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 1 ) <br>
将累加器的内容与常数 1 比较,相等则执行第七句,否则执行第八句。1就是icmp协 <br>
议号。 <br>
第七句 BPF_STMT( BPF_RET + BPF_K, 128 ) <br>
程序返回,过滤输出所收到帧的前128字节 <br>
第八句 BPF_STMT( BPF_RET + BPF_K, 0 ) <br>
程序返回,不向上层提供数据 <br>
对比下面这个输出,现在应该又加深了一下理解吧 <br>
./tcpdump -d less 74 and ip src host 192.168.8.90 and ip proto \\icmp <br>
(000) ld #pktlen <br>
(001) jgt #0x4a jt 9 jf 2 <br>
(002) ldh [12] <br>
(003) jeq #0x800 jt 4 jf 9 <br>
(004) ld [26] <br>
(005) jeq #0xc0a8085a jt 6 jf 9 <br>
(006) ldb [23] <br>
(007) jeq #0x1 jt 8 jf 9 <br>
(008) ret #68 <br>
(009) ret #0 <br>
什么感觉啊,有点写BASIC程序的感觉吧,反正我不是很爽,10年没有写一行BASIC程 <br>
序了,现在居然要自己计算GOTO目标。好在bpf过滤就这么点东西,再多也不过几行 <br>
而已。如果汇编语言还残留了一点点在你那已不堪重负的脑海深处,最好不过。 <br>
现在我们只需要目标端口是21、23、110、513的TCP报文,目标IP地址任意,可以设 <br>
置内核过滤机制如下: <br>
1) 过滤IP报文 <br>
2) 过滤TCP报文 <br>
3) 过滤21端口 <br>
4) 过滤23端口 <br>
5) 过滤110端口 <br>
6) 过滤513端口 <br>
struct bpf_insn password_insns[] = <br>
{ <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 12 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, ETH_P_IP, 0, 11 ), <br>
BPF_STMT( BPF_LD + BPF_B + BPF_ABS, 23 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 0, 9 ), <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 36 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 21, 6, 0 ), <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 36 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 23, 4, 0 ), <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 36 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 110, 2, 0 ), <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 36 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 513, 0, 1 ), <br>
BPF_STMT( BPF_RET + BPF_K, 256 ), <br>
BPF_STMT( BPF_RET + BPF_K, 0 ), <br>
}; <br>
struct bpf_program password_filter = <br>
{ <br>
14, <br>
password_insns, <br>
}; <br>
(000) ldh [12] <br>
(001) jeq #0x800 jt 2 jf 13 <br>
(002) ldb [23] <br>
(003) jeq #0x6 jt 4 jf 13 <br>
(004) ldh [36] <br>
(005) jeq #0x15 jt 12 jf 6 <br>
(006) ldh [36] <br>
(007) jeq #0x17 jt 12 jf 8 <br>
(008) ldh [36] <br>
(009) jeq #0x6e jt 12 jf 10 <br>
(010) ldh [36] <br>
(011) jeq #0x201 jt 12 jf 13 <br>
(012) ret #256 <br>
(013) ret #0 <br>
我们尝试把它修改一下: <br>
struct bpf_insn password_insns[] = <br>
{ <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 12 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, ETH_P_IP, 0, 8 ), <br>
BPF_STMT( BPF_LD + BPF_B + BPF_ABS, 23 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 0, 6 ), <br>
BPF_STMT( BPF_LD + BPF_H + BPF_ABS, 36 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 21, 3, 0 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 23, 2, 0 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 110, 1, 0 ), <br>
BPF_JUMP( BPF_JMP + BPF_JEQ + BPF_K, 513, 0, 1 ), <br>
BPF_STMT( BPF_RET + BPF_K, 256 ), <br>
BPF_STMT( BPF_RET + BPF_K, 0 ), <br>
}; <br>
struct bpf_program password_filter = <br>
{ <br>
11, <br>
password_insns, <br>
}; <br>
(000) ldh [12] <br>
(001) jeq #0x800 jt 2 jf 10 <br>
(002) ldb [23] <br>
(003) jeq #0x6 jt 4 jf 10 <br>
(004) ldh [36] <br>
(005) jeq #0x15 jt 9 jf 6 <br>
(006) jeq #0x17 jt 9 jf 7 <br>
(007) jeq #0x6e jt 9 jf 8 <br>
(008) jeq #0x201 jt 9 jf 10 <br>
(009) ret #256 <br>
(010) ret #0 <br>
FreeBSD 4.0 下验证通过。 <br>
使用内核过滤机制的好处就在于减少了从内核空间向用户空间的数据拷贝过程,提高 <br>
了包过滤的速率。上面已经举了两个例子,其实设置NetXray就和设置bpf过滤一个道 <br>
理,只要对协议分析熟悉就没有什么技术难度的。 <br>
---------------------------------------------------------------------- <br>
事实上本期月刊上的例子代码可以达到tcpdump -d的效果,感兴趣了自己再看看。 <br>
snoop -C 类似 tcpdump -d,产生过滤规则的解释输出,不做抓包。 <br>
</small><hr>
<p align="center">[<a href="index.htm">回到开始</a>][<a href="516.htm">上一层</a>][<a href="543.htm">下一篇</a>]
<p align="center"><a href="http://cterm.163.net">欢迎访问Cterm主页</a></p>
</table>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -