通过上一次的配置环境,现在就可以使用了,那么这里我们就先来发一个arp请求吧,这里建议大家安装一个wireshark(抓包工具,操作简单),以便于我们随时查看数据包,方便调试。
首先,我们需要知道的是,ARP是地址解析协议,工作在数据链路层,用来在一个局域网内获取一个IP的mac地址,当我们想要知道一个IP的MAC地址的时候,我们就向局域网广播一个arp请求,如果此IP为活动主机,则其就会回复一个arp应答,我们就是通过有没有收到arp应答来判断此ip主机是否为活动的。
按照上次文章所说的建立好工程,我建立的是简单的控制台程序,配好环境,写上需要的头文件,这里我们还需要#include"packet32.h",#include "ntddndis.h"这两个头文件,我们会用到里面的一些东西。
先定义三个结构体,用来表示我们的包,以太网首部和arp包
//以太网首部
struct ethernet_head
{
unsigned char dest_mac[6];//目的MAC地址
unsigned char source_mac[6];//源MAC地址
unsigned short eh_type;//帧类型
};
//arp数据包struct arp_head{ unsigned short hardware_type;//硬件类型 unsigned short protocol_type;//协议类型 unsigned char add_len;//硬件地址长度 unsigned char pro_len;//协议地址长度 unsigned short option;//操作类型,arp请求与应答,rarp请求与应答 unsigned char sour_addr[6];//源MAC unsigned long sour_ip;//源IP unsigned char dest_addr[6];//目的MAC unsigned long dest_ip;//源IP unsigned char padding[18];//填充数据};//发送的数据包struct arp_packet{ ethernet_head eth;//以太网首部 arp_head arp;//arp包};这里有个问题需要主要,在vs中,由于编译的优化问题,结构体的大小不等于结构体中每个变量的大小和,这样在后面的使用会存在问题(将我们构造的数据包强转为char*数据和将收到数据强转回来时),主要原因可以看一下这位的这篇 点击打开链接
这里可以这样设置,取消这种对齐项目->属性->C/C++->代码生成,结构成员对齐中设为1字节即可。
需要的变量:(不是所有都用了,有些是我用来做测试的,自己看着用吧)
pcap_if_t *alldevs; //全部网卡列表
pcap_if_t *d; //一个网卡
int inum; //用户选择的网卡序号
int i = 0; //循环变量
pcap_t *adhandle; //一个pcap实例
char errbuf[PCAP_ERRBUF_SIZE]; //错误缓冲区
unsigned char *mac; //本机MAC地址
unsigned char *packet; //ARP包
unsigned long sourceIP; //要伪装成的IP地址
unsigned long destIP;
pcap_addr_t *pAddr; //网卡地址
unsigned long ip; //IP地址
unsigned long netmask; //子网掩码
struct in_addr net_ip_address;//网卡IP信息,在pcap.h里面有定义
struct in_addr net_mask_address;
char *net_ip_string;
char *net_mask_string;
1.获取本机网卡列表
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
cout << "Error in pcap_findalldevs:" << errbuf << endl;
}
2.这里我们可以输出一下我们获取到的所有网卡信息,当然这在发送arp请求中不是必须的,但考虑到一个电脑可能有多个网卡,这时我们需要让用户选择自己想要用来发送arp请求的网卡
for (d = alldevs; d; d = d->next)3.打开我们需要的那个网卡,这里我需要的就是第一个网卡,所以直接打开,如果你需要的是其它网卡,先用一个循环跳转到你需要的网卡(d->next表示下一个网卡信息:d=d->next)
{
cout << ++i;
if (d->description)
{
cout << "." << d->description << ";"<< d->addresses << endl;
}
else
cout << ".No description available" << endl;
}
//如果没有网卡
if (i == 0)
{
cout << "\nNo interfaces found! Make sure WinPcapis installed.\n" << endl;
return -1;
}
d = alldevs;4.获取本机ip地址(这里为什么是pAddr->next->addr,很多地方直接是pAddr->addr,实际我在使用的时候,后者不行,获取的ip这些信息是空的,必须要先next,你们可以自己试试,原因布吉岛。。。。。。。。)
if ((adhandle = pcap_open_live(d->name, 65536, 0, 1000, errbuf)) == NULL)
{
cout << "Unable to open the adapter." << d->name << " is not supported bu WinPcap" << endl;
return -1;
}
pAddr = d->addresses5.获取本机mac(这个函数自己实现的)
ip = ((struct sockaddr_in *)pAddr->next->addr)->sin_addr.s_addr;
mac = GetSelfMac(alldevs->name);
unsigned char* GetSelfMac(char* pDevName){ static u_char mac[6]; LPADAPTER lpAdapter = PacketOpenAdapter(pDevName); if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE)) return NULL; PPACKET_OID_DATA OidData = (PPACKET_OID_DATA)malloc(6 + sizeof(PACKET_OID_DATA)); if(OidData == NULL) { PacketCloseAdapter(lpAdapter); return NULL; } OidData->Oid = OID_802_3_CURRENT_ADDRESS; OidData->Length = 6; memset(OidData->Data, 0, 6); BOOLEAN Status = PacketRequest(lpAdapter, FALSE, OidData); if (Status) memcpy(mac, (u_char*)(OidData->Data), 6); free(OidData); PacketCloseAdapter(lpAdapter); return mac;}
6.构造包(函数自己实现),不修改vs的结构体对齐,这里就会出错
destIP = inet_addr("192.168.218.128");//我要探测的IP
packet = BuildArpPacket(mac, sourceIP, destIP);
unsigned char *BuildArpPacket(unsigned char* source_mac, unsigned long srcIP, unsigned long destIP){ static struct arp_packet packet; //目的MAC地址为广播地址,FF-FF-FF-FF-FF-FF memset(packet.eth.dest_mac, 0xFF, 6); //源MAC地址 memcpy(packet.eth.source_mac, source_mac, 6); //上层协议为ARP协议,0x0806 packet.eth.eh_type = htons(0x0806); //硬件类型,Ethernet是0x0001 packet.arp.hardware_type = htons(0x0001); //上层协议类型,IP为0x0800 packet.arp.protocol_type = htons(0x0800); //硬件地址长度:MAC地址长度为0x06 packet.arp.add_len = 0x06; //协议地址长度:IP地址长度为0x04 packet.arp.pro_len = 0x04; //操作:ARP请求为1 packet.arp.option = htons(0x0001); //源MAC地址 memcpy(packet.arp.sour_addr, source_mac, 6); //源IP地址 packet.arp.sour_ip = srcIP; //目的MAC地址,填充0 memset(packet.arp.dest_addr, 0, 6); //目的IP地址 packet.arp.dest_ip = destIP; //填充数据,18个字节 memset(packet.arp.padding, 0, 18); return (unsigned char*)&packet;}
7.发送数据包,这里之后,wireshark(以后我就叫他鲨鱼了)就能抓到数据包了
if (pcap_sendpacket(adhandle, packet, 60) == -1)
{
cout << "pcap_sendpacket error" << endl;
}
至此,arp请求的发送完成,下次我们来看接收,通过对一个我们想要扫描的ip发送arp请求,如果收到了来自该ip的arp应答,则此ip的主机为活动主机