/*

程序功能:

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

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

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

*/

一、构造arp包

在构造之前先了解一下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中是怎么定义的

   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中宏定义的,如下图

     这个错误缓冲区用来做什么呢?在函数错误返回时(返回值为-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地址

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


二、获取网络中的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包的更多相关文章

  1. 11. Go 语言网络编程

    Go 语言网络编程 Go语言在编写 web 应用方面非常得力.因为目前它还没有 GUI(Graphic User Interface 图形化用户界面)的框架,通过文本或者模板展现的 html 界面是目 ...

  2. Go语言备忘录(3):net/http包的使用模式和源码解析

    本文是晚辈对net/http包的一点浅显的理解,文中如有错误的地方请前辈们指出,以免误导! 转摘本文也请注明出处:Go语言备忘录(3):net/http包的使用模式和源码解析,多谢!  目录: 一.h ...

  3. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  4. (转)Java并发编程:volatile关键字解析

    转:http://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或 ...

  5. WinPcap编程(前言&&学习)

    计算机网络课设要求用WinPcap写对ARP包的接收与发送. 所以学了一下WinPcap的内容. 参考的博客: http://blog.csdn.net/htttw/article/details/7 ...

  6. Mysql C语言API编程入门讲解

    原文:Mysql C语言API编程入门讲解 软件开发中我们经常要访问数据库,存取数据,之前已经有网友提出让鸡啄米讲讲数据库编程的知识,本文就详细讲解如何使用Mysql的C语言API进行数据库编程.   ...

  7. Java并发编程:volatile关键字解析(转载)

    转自https://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析   Java并发编程:volatile关键字解析 ...

  8. Go语言 并发编程

    Go语言 并发编程 作者:Eric 微信:loveoracle11g 1.创建goroutine // 并行 是两个队列同时使用两台咖啡机 // 并发 是两个队列交替使用一台咖啡机 package m ...

  9. C语言socket编程

    建议先去看一下思路 真的写的很不错呦~ 思路参考博客:https://www.cnblogs.com/renfanzi/p/5713054.html linux c语言socket编程代码(单一服务端 ...

随机推荐

  1. SRM 390(1-250pt)

    DIV1 250pt 题意:给定整数n和k,问最少需要多少个n连接在一起形成的新整数t,使得t是k的倍数.如果不能形成倍数,输出-1.k <= 10^5,n <= 10^9. 解法:不断增 ...

  2. mysql 添加定时任务

    之前定时任务都是用quartz 或者spring的任务调度来做的,易于管理,但是要写代码加 配置,其实mysql 自带了job ,先创建一个存储过程

  3. Thinking in C++: Pointers to members 指向成员的指针

    通常来说,一个指针(pointer)是一个存储地址的变量,你能在运行时去改变它,并且指针可以指向数据或函数. 但在C++中,指向成员的指针(pointer-to-member)指向的是class或st ...

  4. scrollview中停止滚动的监听

    [补充]可以在主线程控制,特别注意 scrollView3.postDelayed(new Runnable() { @Override public void run() { scrollView3 ...

  5. jni java和C之间的值传递(int String int[])

    我们通过jni调用C代码不可能每次只是去调一个方法,通常,我们需要传递一些值过去. 例如,播放电影,那就肯定需要你把电影的 url给 C的播放器吧,等等. 接下来就看一看怎么去传递这些值: 首先是最简 ...

  6. Enterprise Architect使用教程

    一.Enterprise Architect简介 Enterprise Architect是一个对于软件系统开发有着极好支持的CASE软件(Computer Aided Software Engine ...

  7. 关于Build Active Architecture Only属性

    关于Build Active Architecture Only属性 Architecture 属性在BuildSetting里. 这个属性设置为yes,是为了debug的时候编译速度更快,它只编译当 ...

  8. Template Method 模板方法

      简介 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的细节 抽象模板AbstractClass的方法分为两类: 基本 ...

  9. Java 数据类型转换(转换成字节型)

    package com.mystudypro.byteutil; import java.io.UnsupportedEncodingException; public class ConToByte ...

  10. MVC4使用EF6连接mysql数据库

    1.需要安装MySql.Data.Entity.EF6,此dll可以在项目——>管理NuGet程序包里联机搜索MySql.Data.Entity.EF6并安装即可 2.连接字符串需要添加prov ...