自己动手写网络抓包工具.docx
- 文档编号:7074136
- 上传时间:2023-01-17
- 格式:DOCX
- 页数:10
- 大小:18.70KB
自己动手写网络抓包工具.docx
《自己动手写网络抓包工具.docx》由会员分享,可在线阅读,更多相关《自己动手写网络抓包工具.docx(10页珍藏版)》请在冰豆网上搜索。
自己动手写网络抓包工具
自己动手写网络抓包工具
当打开一个标准SOCKET套接口时,我们比较熟悉的协议往往是用AF_INET来建立基于TCP(SOCK_STREAM)或UDP(SOCK_DGRAM)的链接。
但是这些只用于IP层以上,要想从更底层抓包,我们需要使用AF_PACKET来建立套接字,它支持SOCK_RAW和SOCK_DGRAM,它们都能从底层抓包,不同的是后者得到的数据不包括以太网帧头(最开始的14个字节)。
好了,现在我们就知道该怎样建立SOCKET套接口了:
sock=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_IP));
最后一个参数ETH_P_IP指出,我们只对IP包感兴趣,而不是ARP,RARP等。
之后就可以用recvfrom从套接口读取数据了。
现在我们可以抓到发往本地的所有IP数据报了,那么有没有办法抓到那些“流经”本地的数据呢?
呵呵,当然可以了,这种技术叫网络嗅探(sniff),它很能威胁网络安全,也非常有用,尤其是当你对网内其他用户的隐私感兴趣时:
(由于以太网数据包是对整个网段广播的,所以网内所有用户都能收到其他用户发出的数据,只是默认的,网卡只接收目的地址是自己或广播地址的数据,而把不是发往自己的数据包丢弃。
但是多数网卡驱动会提供一种混杂模式(promiscousmode),工作在这种模式下的网卡会接收网络内的所有数据,不管它是发给谁的。
下面的方法可以把网卡设成混杂模式:
//setNICtopromiscousmode,sowecanrecieveallpacketsofthenetwork
strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
ioctl(sock,SIOCGIFFLAGS,ðreq);
ethreq.ifr_flags|=IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,ðreq);
通过ifconfig可以很容易的查看当前网卡是否工作在混杂模式(PROMISC)。
但是请注意,程序退出后,网卡的工作模式不会改变,所以别忘了关闭网卡的混杂模式:
//turnoffpromiscousmode
ethreq.ifr_flags&=~IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,ðreq);
现在我们可以抓到本网段的所有IP数据包了,但是问题也来了:
那么多的数据,怎么处理?
CPU可能会被严重占用,而且绝大多数的数据我们可能根本就不敢兴趣!
那怎么办呢?
用if语句?
可能要n多个,而且丝毫不会降低内核的繁忙程度。
最好的办法就是告诉内核,把不感兴趣的数据过滤掉,不要往应用层送。
BPF就为此而生。
BPF(BerkeleyPacketFilter)是一种类是汇编的伪代码语言,它也有命令代码和操作数。
例如,如果我们只对用户192.168.1.4的数据感兴趣,可以用tcpdump的-d选项生成BPF代码如下:
$tcpdump-dhost192.168.1.4
(000)ldh[12]
(001)jeq#0x800jt2jf6
(002)ld[26]
(003)jeq#0xc0a80104jt12jf4
(004)ld[30]
(005)jeq#0xc0a80104jt12jf13
(006)jeq#0x806jt8jf7
(007)jeq#0x8035jt8jf13
(008)ld[28]
(009)jeq#0xc0a80104jt12jf10
(010)ld[38]
(011)jeq#0xc0a80104jt12jf13
(012)ret#96
(013)ret#0
其中第一列代表行号,第二列是命令代码,后面是操作数。
下面我们采用汇编注释的方式简单的解释一下:
(000)ldh[12];loadh?
?
(2bytes)fromABSoffset12(theTYPEofethernetheader)
(001)jeq#0x800jt2jf6;compareandjump,jumptoline2iftrue;elsejumptoline6
(002)ld[26];loadword(4bytes)fromABSoffset26(srcIPaddressofIPheader)
(003)jeq#0xc0a80104jt12jf4;compareandjump,jumptoline12iftrue,elsejumptoline4
(004)ld[30];loadword(4bytes)fromABSoffset30(dstIPaddressofIPheader)
(005)jeq#0xc0a80104jt12jf13;seeline3
(006)jeq#0x806jt8jf7;comparewithARP,seeline1
(007)jeq#0x8035jt8jf13;comparewithRARP,seeline1
(008)ld[28];srcIPaddressforotherprotocols
(009)jeq#0xc0a80104jt12jf10
(010)ld[38];dstIPaddressforotherprotocols
(011)jeq#0xc0a80104jt12jf13
(012)ret#96;return96bytestouserapplication
(013)ret#0;dropthepacket
但是这样的伪代码我们是无法在应用程序里使用的,所以tcpdum提供了一个-dd选项来输出一段等效的C代码:
$tcpdump-ddhost192.168.1.4
{0x28,0,0,0x0000000c},
{0x15,0,4,0x00000800},
{0x20,0,0,0x0000001a},
{0x15,8,0,0xc0a80104},
{0x20,0,0,0x0000001e},
{0x15,6,7,0xc0a80104},
{0x15,1,0,0x00000806},
{0x15,0,5,0x00008035},
{0x20,0,0,0x0000001c},
{0x15,2,0,0xc0a80104},
{0x20,0,0,0x00000026},
{0x15,0,1,0xc0a80104},
{0x6,0,0,0x00000060},
{0x6,0,0,0x00000000},
该代码对应的数据结构是structsock_filter,该结构在linux/filter.h中定义如下:
structsock_filter//Filterblock
{
__u16code;//Actualfiltercode
__u8jt;//Jumptrue
__u8jf;//Jumpfalse
__u32k;//Genericmultiusefield
};
code对应命令代码;jt是jumpiftrue后面的操作数,注意这里用的是相对行偏移,如2就表示向前跳转2行,而不像伪代码中使用绝对行号;jf为jumpiffalse后面的操作数;k对应伪代码中第3列的操作数。
了解了BPF伪代码和结构,我们就可以自己定制更加简单有效的BPFfilter了,如上例中的6-11行不是针对IP协议的,而我们的套接字已经指定只读取IP数据了,所以就可以把他们删除,不过要注意,行偏移也要做相应的修改。
另外,tcpdump默认只返回96字节的数据,但对大部分应用来说,96字节是远远不够的,所以tcpdump提供了-s选项用于指定返回的数据长度。
OK,下面我们就来看看怎样把过滤器安装到套接口上吧:
$tcpdumpip-d-s2048host192.168.1.2
(000)ldh[12]
(001)jeq#0x800jt2jf7
(002)ld[26]
(003)jeq#0xc0a80102jt6jf4
(004)ld[30]
(005)jeq#0xc0a80102jt6jf7
(006)ret#2048
(007)ret#0
structsock_filterbpf_code[]={
{0x28,0,0,0x0000000c},
{0x15,0,5,0x00000800},
{0x20,0,0,0x0000001a},
{0x15,2,0,0xc0a80102},
{0x20,0,0,0x0000001e},
{0x15,0,1,0xc0a80102},
{0x6,0,0,0x00000800},
{0x6,0,0,0x00000000}
};
structsock_fprogfilter;
filter.len=sizeof(bpf_code)/sizeof(bpf_code[0]);
filter.filter=bpf_code;
setsockopt(sock,SOL_SOCKET,SO_ATTACH_FILTER,&filter,sizeof(filter));
最后加上信号处理器,以便能在程序退出前恢复网卡的工作模式。
到现在我们已经可以看到一个小聚规模抓包小工具了,呵呵,麻雀虽小,但也五脏俱全啊!
下面给出完整的代码。
#include<sys/types.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<sys/socket.h>
#include<linux/types.h>
#include<netinet/in.h>
#include<netinet/udp.h>
#include<netinet/ip.h>
#include<netpacket/packet.h>
#include<net/ethernet.h>
#include<arpa/inet.h>
#include<string.h>
#include<signal.h>
#include<net/if.h>
#include<stdio.h>
#include<sys/uio.h>
#include<fcntl.h>
#include<unistd.h>
#include<linux/filter.h>
#include<stdlib.h>
#defineETH_HDR_LEN14
#defineIP_HDR_LEN20
#defineUDP_HDR_LEN8
#defineTCP_HDR_LEN20
staticintsock;
voidsig_handler(intsig)
{
structifreqethreq;
if(sig==SIGTERM)
printf("SIGTERMrecieved,exiting...\n");
elseif(sig==SIGINT)
printf("SIGINTrecieved,exiting...\n");
elseif(sig==SIGQUIT)
printf("SIGQUITrecieved,exiting...\n");
//turnoffthePROMISCOUSmode
strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
if(ioctl(sock,SIOCGIFFLAGS,ðreq)!
=-1){
ethreq.ifr_flags&=~IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,ðreq);
}
close(sock);
exit(0);
}
intmain(intargc,char**argv){
intn;
charbuf[2048];
unsignedchar*ethhead;
unsignedchar*iphead;
structifreqethreq;
structsigactionsighandle;
#if0
$tcpdumpip-s2048-dhost192.168.1.2
(000)ldh[12]
(001)jeq#0x800jt2jf7
(002)ld[26]
(003)jeq#0xc0a80102jt6jf4
(004)ld[30]
(005)jeq#0xc0a80102jt6jf7
(006)ret#2048
(007)ret#0
#endif
structsock_filterbpf_code[]={
{0x28,0,0,0x0000000c},
{0x15,0,5,0x00000800},
{0x20,0,0,0x0000001a},
{0x15,2,0,0xc0a80102},
{0x20,0,0,0x0000001e},
{0x15,0,1,0xc0a80102},
{0x6,0,0,0x00000800},
{0x6,0,0,0x00000000}
};
structsock_fprogfilter;
filter.len=sizeof(bpf_code)/sizeof(bpf_code[0]);
filter.filter=bpf_code;
sighandle.sa_flags=0;
sighandle.sa_handler=sig_handler;
sigemptyset(&sighandle.sa_mask);
//sigaddset(&sighandle.sa_mask,SIGTERM);
//sigaddset(&sighandle.sa_mask,SIGINT);
//sigaddset(&sighandle.sa_mask,SIGQUIT);
sigaction(SIGTERM,&sighandle,NULL);
sigaction(SIGINT,&sighandle,NULL);
sigaction(SIGQUIT,&sighandle,NULL);
//AF_PACKETallowsapplicationtoreadpecketfromandwritepackettonetworkdevice
//SOCK_DGRAMthepacketexcludeethernetheader
//SOCK_RAWrawdatafromthedeviceincludingethernetheader
//ETH_P_IPallIPpackets
if((sock=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_IP)))==-1){
perror("socket");
exit
(1);
}
//setNICtopromiscousmode,sowecanrecieveallpacketsofthenetwork
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);
}
//attachthebpffilter
if(setsockopt(sock,SOL_SOCKET,SO_ATTACH_FILTER,&filter,sizeof(filter))==-1){
perror("setsockopt");
close(sock);
exit
(1);
}
while
(1){
n=recvfrom(sock,buf,sizeof(buf),0,NULL,NULL);
if(n<(ETH_HDR_LEN+IP_HDR_LEN+UDP_HDR_LEN)){
printf("invalidpacket\n");
continue;
}
printf("%dbytesrecieved\n",n);
ethhead=buf;
printf("Ethernet:
MAC[%02X:
%02X:
%02X:
%02X:
%02X:
%02X]",ethhead[0],ethhead[1],ethhead[2],
ethhead[3],ethhead[4],ethhead[5]);
printf("->[%02X:
%02X:
%02X:
%02X:
%02X:
%02X]",ethhead[6],ethhead[7],ethhead[8],
ethhead[9],ethhead[10],ethhead[11]);
printf("type[%04x]\n",(ntohs(ethhead[12]|ethhead[13]<<8)));
iphead=ethhead+ETH_HDR_LEN;
//headerlengthas32-bit
printf("IP:
Version:
%dHeaderLen:
%d[%d]",(*iphead>>4),(*iphead&0x0f),(*iphead&0x0f)*4);
printf("TotalLen%d",(iphead[2]<<8|iphead[3]));
printf("IP[%d.%d.%d.%d]",iphead[12],iphead[13],iphead[14],iphead[15]);
printf("->[%d.%d.%d.%d]",iphead[16],iphead[17],iphead[18],iphead[19]);
printf("%d",iphead[9]);
if(iphead[9]==IPPROTO_TCP)
printf("[TCP]");
elseif(iphead[9]==IPPROTO_UDP)
printf("[UDP]");
elseif(iphead[9]==IPPROTO_ICMP)
printf("[ICMP]");
elseif(iphead[9]==IPPROTO_IGMP)
printf("[IGMP]");
elseif(iphead[9]==IPPROTO_IGMP)
printf("[IGMP]");
else
printf("[OTHERS]");
printf("PORT[%d]->[%d]\n",(iphead[20]<<8|iphead[21]),(iphead[22]<<8|iphead[23]));
}
close(sock);
exit(0);
}
本文来自CSDN博客,转载请标明出处:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 自己动手 网络 包工
![提示](https://static.bdocx.com/images/bang_tan.gif)