c语言Winpcap编程构造并接收解析arp包

时间:2023-03-08 19:28:21

/*

程序功能:

1、构造arp包,并发送。程序参数顺序:源IP、目的IP、mac地址、flag

2、获取网络中的ARP数据包,解析数据包的内容。程序参数:日志文件名

winpacp中文技术文档(基本是英文的):http://www.ferrisxu.com/WinPcap/html/index.html

*/

一、构造arp包

在构造之前先了解一下arp包的结构,先从网上找了张图

c语言Winpcap编程构造并接收解析arp包

从图中可以看出以太网首部占14字节,以太网ARP字段占28字节。其中op字段为操作类型,1表示ARP请求、2表示ARP应答

再介绍几个要用到的pcap函数

  1. int pcap_findalldevs ( pcap_if_t ** alldevsp,  char * errbuf)

函数功能:列出当前所有可用的网络设备(网卡),将设备信息存入pcap_if_t结构列表中

参数:1、alldevsp    指向pcap_if_t结构列表的指针的地址(注意这里是pcap_if_t指针的地址,而不是pcap_if_t结构的地址)

    有些地方这里可能会写pcap_if结构,其实pcap_if和pcap_if_t就是同一个东西,我们来看看在pcap.h中是怎么定义的

c语言Winpcap编程构造并接收解析arp包

   pcap_if结构体成员:

Struct pcap_if {

struct pcap_if  *next;  //指向下一个链表成员

char *name; //网卡名称

chat *description; //网卡描述信息

struct pcap_addr address;

u_int flags; //接口标志

}

    2、errbuf   错误缓冲区,要求长度至少为PCAP_ERRBUF_SIZE 字节,那么PCAP_ERRBUF_SIZE是多大呢

          这在pcap.h中宏定义的,如下图

c语言Winpcap编程构造并接收解析arp包

     这个错误缓冲区用来做什么呢?在函数错误返回时(返回值为-1)会向错误缓冲中填充错误信息,错误信息为可打印ASCII码

     函数正确时返回0

  2、pcap_t * pcap_open_live ( char * device, int snaplen, int promisc,int to_ms, char * errbuf )

函数功能:在网络中打开一个活动的捕获<这是winpcap技术文档给出的说明,也就是指定从一个网络设备捕获数据包,我是这么理解的>

    函数的返回值为一个结构体指针pcap_t即为struct pcap。pcap_t结构体有点长就不做说明了,里面就是捕获句柄的一些信息

参数: <文档是英文的不知道有没有翻译对>

    device 设备名

    snaplen 单包最大捕捉字节数(若数据包大于snaplen,只有前面snaplen字节大小的数据被捕获)  

    promisc 混杂模式(即使该参数是false,也可能由其他原因导致网络接口为混杂模式)

    to_ms 指定毫秒级读超时(当一个数据包被发现时,并不一定立即返回数据包,它会等待一段时间,允许一个操作从系统内核读取多个数据  包。不是所有的平台都支持读超时,在不支持的平台上读超时会被忽略。)<在支持读超时的平台上若读超时为0,将导致永不超时>

    errbuf 用于返回错误或警告信息

  3、void pcap_close ( pcap_t  *p )

    关闭pcap_open_live()获取的包捕获句柄,释放相关资源

源码:

 /*
构造并发送ARP包
2015年6月24日15:44:21
blog:http://www.cnblogs.com/wd1001/
*/
#include <stdlib.h>
#include <stdio.h>
#include <pcap.h> #pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "ws2_32.lib") main(int argc, char **argv)
{
u_char packet[];
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=,j,k,temp[];
pcap_t * adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
/* 获取设备列表 */ if (argc != )//argc==5,及程序后面有四个参数
{
printf("usage: %s inerface", argv[]);
return -;
} if (pcap_findalldevs(&alldevs, errbuf) == -)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit();
}
/* 数据列表 */
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}
if(i==)
{
printf("\n找不到网卡! 检查是否安装WinPcap.\n");
return -;
}
printf("Enter the interface number (1-%d):",i);
scanf("%d", &inum);
if(inum < || inum > i)
{
printf("\nInterface number out of range.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -;
}
/* 转到选择的设备 */
for(d=alldevs, i=; i< inum-;d=d->next, i++);
/* 打开设备 */
if ( (adhandle= pcap_open_live(d->name, //设备名
, // 最大捕捉字节数
, // 混杂模式
, // 读入超时
errbuf // 错误缓冲
) ) == NULL)
{
/*打开失败*/
fprintf(stderr,"\n打开失败. %s 不被winpcap支持\n",d->name);
/* 释放列表 */
pcap_freealldevs(alldevs);
return -;
}
/* 释放设备列表 */
pcap_freealldevs(alldevs); /* 填充数据段 */ //flag为1表示ARP请求
if(''==argv[][])
{
//源MAC地址
k=;
for(i=;i<;i=i+)
{
temp[]=(int)argv[][i];
temp[]=(int)argv[][i+];
if(temp[]>) //当输入mac为小写字母时字符转换为16进制
temp[]=temp[]-;
else if(temp[]>)
temp[]=temp[]-;//当输入mac为大写字母时字符转换为16进制
else
temp[]=temp[]-;//当输入mac为数字时字符转换为16进制
if(temp[]>)
temp[]=temp[]-;
else if(temp[]>)
temp[]=temp[]-;
else
temp[]=temp[]-;
packet[+k]=packet[+k]=temp[]*+temp[];
k++;
} //发送ARP请求时目的MAC全置为ff
for(i=;i<;i++)
{
packet[i]=packet[+i]=0xff;
}
} //flag=2:ARP应答
else
{
//目的MAC地址
k=;
for(i=;i<;i=i+)
{
temp[]=(int)argv[][i];
temp[]=(int)argv[][i+];
if(temp[]>)
temp[]=temp[]-;
else if(temp[]>)
temp[]=temp[]-;
else
temp[]=temp[]-;
if(temp[]>)
temp[]=temp[]-;
else if(temp[]>)
temp[]=temp[]-;
else
temp[]=temp[]-;
packet[k]=packet[+k]=temp[]*+temp[];
k++;
}
//应答ARP请求时把源MAC置为0
for(i=;i<;i++)
{
packet[+i]=packet[+i]=0x00;
}
} //源IP地址
k=;
temp[]=; //指向每个字节初始位置
for(i=;i<;i++)
{
temp[]=;
temp[]=;
for(j=;j<;j++)
{
if(argv[][j+temp[]]>=''&&argv[][j+temp[]]<='')
{
temp[]=(int)argv[][j+temp[]]-;
temp[]=temp[]*+temp[];
//printf("%d %d\n",temp[0],temp[1]);
}
else
{
//当遇到点时j自加1目的是让temp[2]+j指向下一字节的第一位
j++;
break;
}
}
packet[+k]=temp[];
k++;
temp[]+=j;
}
//目标IP地址
k=;
temp[]=;
for(i=;i<;i++)
{
temp[]=;
temp[]=;
for(j=;j<;j++)
{
if(argv[][j+temp[]]>=''&&argv[][j+temp[]]<='')
{
temp[]=(int)argv[][j+temp[]]-;
temp[]=temp[]*+temp[];
//printf("%d %d\n",temp[0],temp[1]);
}
else
{
j++;
break;
}
}
packet[+k]=temp[];
k++;
temp[]+=j;
}
//ARP首部
packet[]=0x08;//12、13位为帧类型
packet[]=0x06;
packet[]=0x00;//14、15位为硬件类型
packet[]=0x01;
packet[]=0x08;//16、17位为协议类型
packet[]=0x00;
packet[]=0x06;//硬件地址长度
packet[]=0x04;//协议地址长度
packet[]=0x00;//op
packet[]=(int)argv[][]-;//op(1为请求2为应答) /* 填充发送包的剩余部分 */
for(i=;i<;i++)
{
packet[+i]=;
}
//这里后四个字节本应该是校验位,这里就不算了,写个日期纪念一下
packet[]=0x20;
packet[]=0x15;
packet[]=0x6;
packet[]=0x24;
/* 发送包 */
pcap_sendpacket(adhandle, packet, );
printf("Success!\n"); return ;
}

运行结果:

输入参数:随意设置源IP,目的IP和mac地址

c语言Winpcap编程构造并接收解析arp包

选择网卡并发送,抓包结果如下

c语言Winpcap编程构造并接收解析arp包


二、获取网络中的ARP数据包,解析数据包的内容

在上面的基础上再解析ARP数据包就简单了

同样在解析前先介绍几个要用到的函数

  1、int pcap_compile ( pcap_t * p, struct bpf_program * fp, char * str,int optimize , bpf_u_int32 netmask )

函数功能:将字符串str编译进一个过滤程序,将程序中高级的过滤表达式,转换成能被内核级的过滤引擎所处理的东西

参数:1、p为pcap_open_live返回的一个捕获句柄

   2、fp为一个指向bpf_program结构的指针,由pcap_compile()函数填写

bpf_program结构为:

struct bpf_program {
u_int bf_len;
struct bpf_insn *bf_insns;
};

bpf_insn结构为:

struct bpf_insn {
u_short code;
u_char jt;
u_char jf;
bpf_int32 k;
};

    3、str 过滤串表达式

    4、optimize 优化控制,是否执行结果代码优化(optimize controls whether optimization on the resulting code is performed)

    5、netmask 子网掩码

  2、int pcap_setfilter (pcap_t *p, struct bpf_program *fp)

函数功能:在捕获过程中绑定一个过滤器

源码:

 /*
获取网络中的ARP数据包,解析数据包的内容
2015年6月24日19:36:36
blog:http://www.cnblogs.com/wd1001/
*/
#include <stdlib.h>
#include <stdio.h>
#include <pcap.h>
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "ws2_32.lib")
//定义ARP包数据
typedef struct arppkt
{
unsigned short hdtyp;//硬件类型
unsigned short protyp;//协议类型
unsigned char hdsize;//硬件地址长度
unsigned char prosize;//协议地址长度
unsigned short op;//(操作类型)操作值:ARP/RARP
u_char smac[];//源MAC地址
u_char sip[];//源IP地址
u_char dmac[];//目的MAC地址
u_char dip[];//目的IP地址
}arpp;
int main(int argc,char * argv[] )
{
struct tm * timeinfo;
struct tm *ltime;
time_t rawtime;
FILE * fp=NULL;
int result;
int i=,inum;
pcap_if_t * alldevs;//指向pcap_if_t结构列表指针
pcap_if_t * d;
pcap_t * adhandle;//定义包捕捉句柄
char errbuf[PCAP_ERRBUF_SIZE];//错误缓冲最小为256
u_int netmask; //定义子网掩码
char packet_filter[]="ether proto \\arp";
struct bpf_program fcode;
struct pcap_pkthdr * header;
const u_char * pkt_data;
//打开日志文件
if((fp=fopen(argv[],"a"))==NULL)
{
printf("打开文件失败!\n");
exit();
}
if (argc != )//argc==2,及程序后面有1个参数
{
printf("程序%s需要一个日志文件名参数!\n", argv[]);
return -;
}
//当前所有可用的网络设备
if (pcap_findalldevs(&alldevs, errbuf) == -)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit();
}
//列出网络设备
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (没有描述可用)\n");
}
if(i==)
{
printf("\n找不到网卡! 检查是否安装WinPcap.\n");
return -;
}
printf("选择对应网卡编号 (1-%d):",i);
scanf("%d", &inum);
if(inum < || inum > i)
{
printf("\n输入的编号超出范围!\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -;
}
/* 转到选择的设备 */
i=;
d=alldevs;
while(i<inum-)
{
d=d->next;
i++;
}
if ( (adhandle= pcap_open_live(d->name,, ,,errbuf) ) == NULL)
{
/*打开失败*/
fprintf(stderr,"\n打开失败. %s 不被winpcap支持\n",d->name);
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -;
}
/* 释放设备列表 */
pcap_freealldevs(alldevs); //获得子网掩码
netmask=((sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
//编译过滤器,只捕获ARP包
if(pcap_compile(adhandle,&fcode,packet_filter,,netmask)<)
{
printf("\nUnable to compile the packet filter.Check the syntax.\n");
pcap_freealldevs(alldevs);
return -;
}
//设置过滤器
if(pcap_setfilter(adhandle,&fcode)<)
{
printf("\nError setting the filter.\n");
pcap_freealldevs(alldevs);
return -;
}
//输出每次修改文件时间
time ( &rawtime );
timeinfo = localtime ( &rawtime );
printf("--------------修改时间:%s",asctime (timeinfo));
fprintf(fp,"-----------修改时间:%s",asctime (timeinfo));
fflush(fp);//刷新缓冲流
while((result=pcap_next_ex(adhandle,&header,&pkt_data))>=)
{
//循环解析ARP数据包
if(result==)
continue;
//解析ARP包,结果输出到屏幕与文件
arppkt* arph = (arppkt *)(pkt_data +);
//输出操作时间
ltime=localtime(&header->ts.tv_sec);
printf("时间:%s",asctime (ltime));
fprintf(fp,"时间:%s",asctime (ltime));
//输出源IP
printf("源IP:");
fprintf(fp,"源IP:");
for(i=;i<;i++)
{
printf("%d.",arph->sip[i]);
fprintf(fp,"%d.",arph->sip[i]);
}
printf("%d\t",arph->sip[]);
fprintf(fp,"%d.\t",arph->sip[]);
//输出目的IP
printf("目的IP:");
fprintf(fp,"目的IP:");
for(i=;i<;i++)
{
printf("%d.",arph->dip[i]);
fprintf(fp,"%d.",arph->dip[i]);
}
printf("%d\t",arph->dip[]);
fprintf(fp,"%d\t",arph->dip[]);
//输出源mac
printf("源mac:");
fprintf(fp,"源mac:");
for(i=;i<;i++)
{
printf("%x-",arph->smac[i]);
fprintf(fp,"%x-",arph->smac[i]);
}
printf("%x\t",arph->smac[]);
fprintf(fp,"%x\t",arph->smac[]);
//输出目的mac
printf("目的mac:");
fprintf(fp,"目的mac:");
for(i=;i<;i++)
{
printf("%x-",*(pkt_data+i));
fprintf(fp,"%x-",*(pkt_data+i));
}
printf("%x\t",*(pkt_data+));
fprintf(fp,"%x\t",*(pkt_data+));
//输出操作类型
printf("操作类型(ARP/RARP):");
fprintf(fp,"操作类型(ARP/RARP):");
if(arph->op==)
{
printf("ARP\t");
fprintf(fp,"ARP\t");
}
else
{
printf("RARP\t");
fprintf(fp,"RARP\t");
}
printf("\n");
fprintf(fp,"\n");
printf("--------------------------------------\n");
fprintf(fp,"--------------------------------------\n");
fflush(fp);
}
fclose(fp);
return ;
}
c语言Winpcap编程构造并接收解析arp包
c语言Winpcap编程构造并接收解析arp包