一、 概要说明1.1 目的要求
掌握ARP协议原理,熟悉Winpcap使用方法;使用Winpcap,手动封装ARP数据包,并实现ARP响应数据包的接收和解析,实现基于ARP协议获取本局域网内全部活动主机MAC地址与IP地址对应关系列表。
1.2 开发平台
1.2.1操作系统:Windows XP (sp2)
1.2.2开发语言:C语言
1.2.3开发工具:VC 6.0 和 Winpcap
二、 相关知识介绍2.1 winpcap驱动简介
winpcap(windows packet capture)是windows平台下一个免费,公共的网络访问系统。开发winpcap这个项目的目的在于为win32应用程序提供访问网络底层的能力。它提供了以下的各项功能:
1> 捕获原始数据报,包括在共享网络上各主机发送/接收的以及相互之间交换的数据报;
2> 在数据报发往应用程序之前,按照自定义的规则将某些特殊的数据报过滤掉;
3> 在网络上发送原始的数据报;
4> 收集网络通信过程中的统计信息。
winpcap的主要功能在于独立于主机协议(如TCP-IP)而发送和接收原始数据报。也就是说,winpcap不能阻塞,过滤或控制其他应用程序数据报的发收,它仅仅只是监听共享网络上传送的数据报。因此,它不能用于QoS调度程序或个人防火墙。
2.2 Packet32.h修改
老师提供的Packet32.h文件确实如下结构体:
struct sockaddr_storage {
u_char sa_len;
u_char sa_family;
u_char padding[128];
};
在写程序过程中自行将此程序加入。
三、 程序设计思路3.1 总体思路
程序设计的总体思路是:首先获得本地主机网络适配器信息;若有多个网络适配器时,打开指定的网络适配器,获得该网络适配器的相关信息,如本地主机IP等;再创建线程接收所有流过的数据报;创建线程向本局域网内所有主机发送arp数据报。
3.2 打开网络适配器,接收所有流过的数据报
首先打开指定的网络适配器,获得本地主机网络适配器信息后,创建线程,将网络适配器设置为混杂模式,接收所有流过的数据报。接受数据报成功时候调用dealData函数处理解析数据报。
3.3 发送ARP请求数据报,获得指定IP的MAC地址
首先初始化ethernet_head、arp_head结构,然后将本地主机IP与子网掩码进行“与”运算,得到网关地址,然后依次对本局域网内所有IP发送ARP数据报。发送不成功时提示错误返回。
下面是代码:
/******************************************
*文件名称:arpHomeWork.c
*作 者:武执正
*摘 要:使用Winpcap,手动封装ARP数据包,
* 实现ARP响应数据报的接收和解析,
* 获取本局域网内全部活动主机MAC地址
* 与IP地址对应列表
*完成时间:2006-10-25
*/
#include
#include
#include
#include
#pragma comment(lib,"ws2_32")
#pragma comment(lib,"packet")
#define ETH_IP 0x0800
#define ETH_ARP 0x0806
#define ARP_REQUEST 0x0001
#define ARP_REPLY 0x0002
#define ARP_HARDWARE 0x0001
#define MAX_NUM_ADAPTER 10
#pragma pack(push,1)
//最终发送的数据报结构
typedef struct ethernet_head
{
unsigned char dest_mac[6]; //目标主机mac
unsigned char source_mac[6]; //本地主机mac
unsigned short eh_type; //类型
}ethernet_head, *pEthernetHead;
//ARP数据报头结构
typedef struct arpHead
{
unsigned short hardware_type; //硬件类型
unsigned short protocol_type; //协议类型
unsigned char add_len; //硬件地址长度
unsigned char pro_len; //协议地址长度
unsigned short option; //操作号
unsigned char sour_addr[6]; //发送者硬件地址
unsigned long sour_ip; //发送者协议地址
unsigned char dest_addr[6]; //目的站硬件地址
unsigned long dest_ip; //目的站协议地址
unsigned char padding[18];
}arpHead, *pArpHead;
//IP数据报头结构
typedef struct ipHead
{
unsigned char h_lenver;
unsigned char tos;
unsigned short total_len;
unsigned short ident;
unsigned short frag_and_flags;
unsigned char ttl;
unsigned char proto;
unsigned short checksum; //校验和
unsigned int sourceip; //本地主机IP
unsigned int destip; //目标主机IP
}ipHead, *PipHead;
#pragma pack(push)
LPADAPTER lpadapter = 0;
LPPACKET lppacketr, lppackets;
unsigned long myip; //本地主机IP
unsigned char myMac[6] = {0}; //本地主机mac初始化
char adapterlist[MAX_NUM_ADAPTER][1024]; //本地主机网络适配器列表数组
int num = 0; //用于发送ARP包时候递增
/******************
*处理接收到的数据报
*******************/
void dealData(LPPACKET lp)
{
unsigned long ulbytesreceived, off;
unsigned long k;
char *buf, *pChar, *base;
PipHead ip;
struct bpf_hdr *hdr;
struct sockaddr_in sin;
ethernet_head *eth;
arpHead *arp;
ulbytesreceived = lp->ulBytesReceived;
buf = (char *)lp->Buffer;
off = 0;
//解析数据报
while (off < ulbytesreceived)
{
hdr = (struct bpf_hdr *)(buf+off);
off += hdr->bh_hdrlen; //修改off
pChar = (char *)(buf+off);
base = pChar;
off = Packet_WORDALIGN(off + hdr->bh_caplen);
eth = (pEthernetHead)pChar;
arp = (pArpHead)(pChar + sizeof(ethernet_head));
if (eth->eh_type == htons(ETH_IP))
{
ip = (PipHead)(pChar+sizeof(ethernet_head));
}//if
else if((eth->eh_type==htons(ETH_ARP)) && (arp->option==htons(ARP_REPLY)))
{
sin.sin_addr.s_addr = arp->sour_ip;
if (sin.sin_addr.s_addr == htonl(myip))
{
memcpy(myMac, eth->source_mac, 6);
}//if
//输出ip地址
printf(" %.16s <-> ", inet_ntoa(sin.sin_addr));
//输出mac地址
for(k=0;k<5;k++)
{
printf("%.2x-",eth->source_mac[k]);
}//if
printf("%.2x",eth->source_mac[5]);
}//else
}//while
return ;
}
/************************************************
*将网络适配器设置为混杂模式,接收所有流过的数据报,
*然后解析该数据报
************************************************/
DWORD WINAPI sniff()
{
char recvbuf[1024*250];
//将网络适配器设置为混杂模式失败
if (!PacketSetHwFilter(lpadapter, NDIS_PACKET_TYPE_PROMISCUOUS))
{
printf("Warning:不能设置网络适配器为混杂模式 ");
}//if
//自定义网络适配器的内核缓冲的大小为 500*1024失败
if (!PacketSetBuff(lpadapter, 500*1024))
{
printf("PacketSetBuff Error! ");
return -1;
}//if
//设置接收一个数据报后等待的时间为1毫秒失败
if (!PacketSetReadTimeout(lpadapter, 1))
{
printf("Warning: 不能设置超时时间 ");
}//if
if (!(lppacketr=PacketAllocatePacket()))
{
printf("PacketAllocatePacket receive Error! ");
return -1;
}//if
//初始化一个packet结构
PacketInitPacket(lppacketr, (char *)recvbuf, sizeof(recvbuf));
//没有按键按下时候
while (!kbhit())
{
//接收数据报,不成功时退出;成功时解析数据报
if (!PacketReceivePacket(lpadapter, lppacketr, TRUE))
{
printf("PacketReceivePacket Error!");
return -1;
}//if
dealData(lppacketr);
}//while
return 0;
}
/**********************************************
*发送ARP Request数据报,请求获得指定ip的mac地址
***********************************************/
DWORD WINAPI sendArp()
{
int k;
char sendbuf[1024];
ethernet_head eth;
arpHead arp;
//初始化目的主机mac和IP
for (k=0; k<6; k++)
{
eth.dest_mac[k] = 0xff;
arp.dest_addr[k] = 0x00;
}//for
memcpy(eth.source_mac,myMac,6);
eth.eh_type = htons(ETH_ARP);
arp.hardware_type = htons(ARP_HARDWARE);
arp.protocol_type = htons(ETH_IP);
arp.add_len = 6;
arp.pro_len = 4;
arp.option = htons(ARP_REQUEST);
arp.sour_ip = htonl(myip);
memcpy(arp.sour_addr, myMac,6);
//本地主机IP与子网掩码“与”运算得到网关IP
arp.dest_ip = htonl((myip&0xffffff00)+1+(num++));
memset(sendbuf, 0, sizeof(sendbuf)); //初始化发送缓存
memcpy(sendbuf, ð, sizeof(eth)); //把eth放入发送缓存
memcpy(sendbuf+sizeof(eth), &arp, sizeof(arp)); //把arp放入发送缓存
//初始化一个_PACKET结构
PacketInitPacket(lppackets, sendbuf, sizeof(eth)+sizeof(arp));
//发送数据报,不成功时退出
if (!PacketSendPacket(lpadapter, lppackets, TRUE))
{
printf("PacketSendPacket in sendArp Error! ");
return -1;
}//if
return 0;
}
/****************************************
*主函数:首先获得本地主机网络适配器数量,
*再打开特定的网络适配器,然后调用函数发送
*解析ARP数据报
*****************************************/
int main(int argc,char *argv[])
{
HANDLE sthread,rthread;
unsigned char adaptername[8192];
unsigned char *temp1, *temp2;
unsigned long adapterlength;
unsigned long threadsid, threadrid; //线程ID
struct bpf_stat stat;
struct sockaddr_in sin;
struct npf_if_addr ipbuff;
int adapternum = 0; //网络适配器数量,初始化为0
int open; //标识打开第几个网络适配器
int i;
long npflen;
adapterlength = sizeof(adaptername);
//不能得到网络适配器信息时退出
if (!PacketGetAdapterNames((char *)adaptername, &adapterlength))
{
printf("PacketGetAdapterNames Error! ");
return -1;
}//if
temp1 = adaptername;
temp2 = adaptername;
i = 0;
//分离出网络适配器信息,并计算网络适配器个数
// while ((*temp1!= '
//////////////////////////////////////////////////////////////剩下的代码不全 http://hi.baidu.com/tkblue/blog/item/636b8dc346a40451b319a893.html
阅读(3312) | 评论(0) | 转发(0) |