- 论坛徽章:
- 0
|
为了做到尽可能的灵活,这个过滤程序可以根据使用者的定义来运行!这个程序是由一种名为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 |
|