📄 sniff_snort.c
字号:
(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字节将被送到应用程序去。
这个过滤器也不是总是有效的,因为它产生于一般的使用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
安装BPF是一个一往直前的操作,所有你需要做的就是建立一个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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <linux/filter.h>
#include <sys/ioctl.h>
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 #0x0800 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)))<0) {
perror("socket");
exit(1);
}
/* Set the network card in promiscuos mode */
strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
if (ioctl(sock,SIOCGIFFLAGS, ðreq)==-1) {
perror("ioctl");
close(sock);
exit(1);
}
ethreq.ifr_flags|=IFF_PROMISC;
if (ioctl(sock,SIOCSIFFLAGS, ðreq)==-1) {
perror("ioctl");
close(sock);
exit(1);
}
/* Attach the filter to the socket */
if(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
&Filter, sizeof(Filter))<0){
perror("setsockopt");
close(sock);
exit(1);
}
while (1) {
printf("----------\n");
n = recvfrom(sock,buffer,2048,0,NULL,NULL);
printf("%d bytes read\n",n);
/* Check to see if the packet contains at least
* complete Ethernet (14), IP (20) and TCP/UDP
* (8) headers.
*/
if (n<42) {
perror("recvfrom():");
printf("Incomplete packet (errno is %d)\n", errno);
close(sock);
exit(0);
}
ethhead = buffer;
printf("Source MAC address: "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
ethhead[0],ethhead[1],ethhead[2],
ethhead[3],ethhead[4],ethhead[5]);
printf("Destination MAC address: "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
ethhead[6],ethhead[7],ethhead[8],
ethhead[9],ethhead[10],ethhead[11]);
iphead = buffer+14; /* Skip Ethernet header */
if (*iphead==0x45) { /* Double check for IPv4 and no options present */
printf("Source host %d.%d.%d.%d\n",
iphead[12],iphead[13],
iphead[14],iphead[15]);
printf("Dest host %d.%d.%d.%d\n",
iphead[16],iphead[17],
iphead[18],iphead[19]);
printf("Source,Dest ports %d,%d\n",
(iphead[20]<<8)+iphead[21],
(iphead[22]<<8)+iphead[23]);
printf("Layer-4 protocol %d\n",iphead[9]);
}
}
}
这个程序和开始的前两个程序很类似,只是增加了LSP代码和setsockopt( )调用,这个过滤器被用来嗅探UDP报文(源地址或者目的地址为192.168.9.10且源地址的端口为5000)为了测试这个程序,你需要一个简单的方法去生成不断的UDP报文(如发送或者接受IP),而且你可以把程序修改为你指定的机器的IP地址,你只需要把代码中的0xc0a8090a替换成你要的IP地址的16进制形式!
最后想说的就是关闭程序后如果我们没有重新设置网卡,那么它还是处于混杂模式!你可以加一个Control-C信号句柄来使网卡在程序关闭前恢复以前的默认配置。
进行网络嗅探对于诊断网络运行错误或者进行流量分析都是非常重要的!有些时候常用的工具如tcpdump或者Ethereal不能非常符合我们的需要的时候,我们可以自己写一个嗅探器!为此我们应该感谢BPF,它使我们做到这点变的很容易!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -