免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2202 | 回复: 0
打印 上一主题 下一主题

libpcap BSD Packet Filter(BPF) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-11 14:15 |只看该作者 |倒序浏览
   为了做到尽可能的灵活,这个过滤程序可以根据使用者的定义来运行!这个程序是由一种名为BPF的伪机器码写成的。BPF看上去很象带着一对寄存器和保存值的汇编语言,完成数学运算和条件分支程序!过滤程序检查每个包,BP进程操作的内存空间包含着报文数据!过滤的结果是一个整数,指出有多少字节的报文需要送到应用层。这是一个更进一步的优点,因为我们一般只对报文的前面几个字节感兴趣,这样可以避免复制过量的数据以节省进程时间。
虽然BPF语言非常简单易学,但是大多数人还是习惯于人类可以阅读的表达方式!所以为了不用BPF语言去描述那些细节和代码,我们将下面开始讨论如何得到一个过滤器的代码!
首先,你需要安装tcpdump,可以从LBL得到!但是,如果你正在读本文,那似乎说明你已经会使用tcpdump了!第一个版本的作者也是那些写BPF的人,事实上,tcpdump要用到BPF,它要用到一个叫libpcap的类库,以此去抓包和过滤。这个类库是一个与操作系统无关的独立的包,为BPF的实现提供支持,当在装有LINUX的机器上使用时,BPF的功能就由LINUX包过滤器实现了!
libpcap提供的一个最有用的函数是pcap_compile(), 它可以把一个输入输出的逻辑表达式变为BPF代码!tcpdump利用这个函数完成在用户输入的命令行和BPF代码之间的转换!tcpdump有个我们很感兴趣但是很少使用的参数 ,-d,可以输出BPF代码!
举个例子,如果tcpdump host 192.168.9.10那么将启动嗅探器并只抓到源地址或者目的地址是192.168.9.10的报文!如果tcpdump -d host 192.168.9.10那么将显示出BPF代码,见下面
Example 3.
Seal:~# tcpdump -d host 192.168.9.10
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 6
(002) ld       [26]
(003) jeq      #0xc0a8090a      jt 12   jf 4
(004) ld       [30]
(005) jeq      #0xc0a8090a      jt 12   jf 13
(006) jeq      #0x806           jt 8    jf 7
(007) jeq      #0x8035          jt 8    jf 13
(008) ld       [28]
(009) jeq      #0xc0a8090a      jt 12   jf 10
(010) ld       [38]
(011) jeq      #0xc0a8090a      jt 12   jf 13
(012) ret      #68
(013) ret      #0
我们简单的分析一下这个代码,0-1,6-7行在确定被抓到的帧是否在传输着IP,ARP或者RARP协议,通过比较帧的第12格的值与协议的特征值(见/usr/include/linux/if_ether.h)!如果比较失败,那么丢弃这个包!
2-5,8-11行是比较帧的源地址和目的地址是否与192.168.9.10相同!注意,不同的协议,地址值在帧中的偏移位不同,如果是IP协议,它在26-30,如果是其他协议,它在28-38如果有一个地址符合,那么帧的前68字节将被送到应用程序去(第12行)
这个过滤器也不是总是有效的,因为它产生于一般的使用BPF的机器,没考虑到一些特殊结构的机器!在一些特殊情况下,过滤器由PF_PACKET进程运行,也许已经检查过以太协议了!这个根据你在socket()调用初使化的时候指定的那些协议!如果不是ETH_P_ALL(抓所有的报文),那么只有那些符合指定的协议类型的报文会流过过滤器!举个例子,如果是ETH_P_IP socket,我们可以重写一个更快且代码更紧凑的过滤器,代码如下
(000) ld       [26]
(001) jeq      #0xc0a8090a      jt 4    jf 2
(002) ld       [30]
(003) jeq      #0xc0a8090a      jt 4    jf 5
(004) ret      #68(005) ret      #0
安装LPF是一个一往直前的操作,所有你需要做的就是建立一个sock_filter并为它绑定一个打开的端口!一个过滤器很容易得到,只要把tcpdump -d中的-d换成-dd就可以了!过滤器将显示出一段C代码,你可以把它复制到自己的程中,见Example 4,这样你就可以通过调用setsockopt()来过滤端口!
Example 4.
Seal:~# tcpdump -dd host 192.168.9.01
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 4, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 8, 0, 0xc0a80901 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 6, 7, 0xc0a80901 },
{ 0x15, 1, 0, 0x00000806 },
{ 0x15, 0, 5, 0x00008035 },
{ 0x20, 0, 0, 0x0000001c },
{ 0x15, 2, 0, 0xc0a80901 },
{ 0x20, 0, 0, 0x00000026 },
{ 0x15, 0, 1, 0xc0a80901 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },
A Complete Example
我们将用一个完整的程序Example 5来结束这篇文章
Example 5
#include
#include
#include   
#include
#include
#include   
#include
#include
#include
#include
#include
int main(int argc, char **argv) {
  int sock, n;
  char buffer[2048];
  unsigned char *iphead, *ethhead;
  struct ifreq ethreq;
  /*
    udp and host 192.168.9.10 and src port 5000
    (000) ldh      [12]
    (001) jeq      #0x800           jt 2        jf 14
    (002) ldb      [23]
    (003) jeq      #0x11            jt 4        jf 14
    (004) ld       [26]
    (005) jeq      #0xc0a8090a      jt 8        jf 6
    (006) ld       [30]
    (007) jeq      #0xc0a8090a      jt 8        jf 14
    (008) ldh      [20]
    (009) jset     #0x1fff          jt 14       jf 10
    (010) ldxb     4*([14]&0xf)
    (011) ldh      [x + 14]
    (012) jeq      #0x1388          jt 13       jf 14
    (013) ret      #68
    (014) ret      #0
  */
  struct sock_filter BPF_code[]= {
    { 0x28, 0, 0, 0x0000000c },
    { 0x15, 0, 12, 0x00000800 },
    { 0x30, 0, 0, 0x00000017 },
    { 0x15, 0, 10, 0x00000011 },
    { 0x20, 0, 0, 0x0000001a },
    { 0x15, 2, 0, 0xc0a8090a },
    { 0x20, 0, 0, 0x0000001e },
    { 0x15, 0, 6, 0xc0a8090a },
    { 0x28, 0, 0, 0x00000014 },
    { 0x45, 4, 0, 0x00001fff },
    { 0xb1, 0, 0, 0x0000000e },
    { 0x48, 0, 0, 0x0000000e },
    { 0x15, 0, 1, 0x00001388 },
    { 0x6, 0, 0, 0x00000044 },
    { 0x6, 0, 0, 0x00000000 }
  };                           
  struct sock_fprog Filter;
   
  Filter.len = 15;
  Filter.filter = BPF_code;
  
  if ( (sock=socket(PF_PACKET, SOCK_RAW,
                    htons(ETH_P_IP)))

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/76292/showart_2145583.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP