在《基于ARP协议获取局域网内主机MAC地址》中使用了WinpCap来发送ARP请求,查询局域网内主机MAC地址,这篇来试试直接用Windows API函数来实现,最后再来探索用于IP,TCP,UDP等众多协议的网际校验和算法。
1,查询局域网主机MAC地址
- #include <WinSock2.h>
- #include <IPHlpApi.h>
- #include <iostream>
- using namespace std;
- #pragma comment(lib,"Iphlpapi")
- #pragma comment(lib,"Ws2_32")
- int _tmain(int argc, _TCHAR* argv[])
- {
- MIB_IPADDRTABLE* pIPAddrTable = (MIB_IPADDRTABLE*)malloc(sizeof(MIB_IPADDRTABLE));
- ULONG dwSize=0,dwRetVal=0;
- if (GetIpAddrTable(pIPAddrTable,&dwSize,0)==ERROR_INSUFFICIENT_BUFFER)
- {
- free(pIPAddrTable);
- pIPAddrTable = (MIB_IPADDRTABLE*)malloc(dwSize);
- }
- if ((dwRetVal=GetIpAddrTable(pIPAddrTable,&dwSize,0))==NO_ERROR)
- {
- ULONG ulHostIp = ntohl(pIPAddrTable->table[0].dwAddr); //本机IP
- ULONG ulHostMask = ntohl(pIPAddrTable->table[0].dwMask); //子网掩码
- for (ULONG i= 1;i<(~ulHostMask);++i)
- {
- static ULONG ulNo = 0;
- HRESULT hr;
- IPAddr ipAddr;
- ULONG pulMac[2];
- ULONG ulLen;
- ipAddr = htonl(i+(ulHostIp&ulHostMask));
- memset(pulMac,0xff,sizeof(pulMac));
- ulLen = 6;
- hr = SendARP(ipAddr,0,pulMac,&ulLen); //发送ARP请求
- if (ulLen==6)
- {
- ulNo++;
- PBYTE pbHexMax = (PBYTE)pulMac;
- unsigned char * strIpAddr = (unsigned char *)(&ipAddr);
- printf("%d:MAC地址% 02X:% 02X:% 02X:% 02X:% 02X:% 02X IP地址% d. % d. % d. % d\n",ulNo,pbHexMax[0],pbHexMax[1],pbHexMax[2],pbHexMax[3],pbHexMax[4],pbHexMax[5],strIpAddr[0],strIpAddr[1],strIpAddr[2],strIpAddr[3]);
- }
- }
- }
- else
- {
- printf("失败");
- }
- printf("结束!\n");
- free(pIPAddrTable);
- return 0;
- }
2,获取本机网卡信息
- #include <WinSock2.h>
- #include <IPHlpApi.h>
- #include <iostream>
- using namespace std;
- #pragma comment(lib,"Iphlpapi")
- #pragma comment(lib,"Ws2_32")
- int _tmain(int argc, _TCHAR* argv[])
- {
- PIP_ADAPTER_INFO pAdapterInfo;
- PIP_ADAPTER_INFO pAdapter = NULL;
- DWORD dwRetVal = 0;
- pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
- ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
- dwRetVal = GetAdaptersInfo(pAdapterInfo,&ulOutBufLen);
- if (dwRetVal==ERROR_BUFFER_OVERFLOW)
- {
- free(pAdapterInfo);
- pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
- dwRetVal = GetAdaptersInfo(pAdapterInfo,&ulOutBufLen);
- }
- if (dwRetVal==NO_ERROR)
- {
- pAdapter = pAdapterInfo;
- while (pAdapter)
- {
- printf("适配器名称:\t% s\n",pAdapter->AdapterName);
- printf("适配器描述信息:\t% s\n",pAdapter->Description);
- printf("MAC地址:\t% 02X:% 02X:% 02X:% 02X:% 02X:% 02X\n",pAdapter->Address[0],pAdapter->Address[1],
- pAdapter->Address[2],pAdapter->Address[3],pAdapter->Address[4],pAdapter->Address[5]);
- printf("IP地址:\t% s\n",pAdapter->IpAddressList.IpAddress.String);
- printf("子网掩码:\t% s\n",pAdapter->IpAddressList.IpMask.String);
- printf("网关地址:\t% s\n",pAdapter->GatewayList.IpAddress.String);
- if (pAdapter->DhcpEnabled)
- {
- printf("DHCP enabled: yes\n");
- printf("DHCP服务器:\t% s\n",pAdapter->DhcpServer.IpAddress.String);
- printf("租约:% ld\n",pAdapter->LeaseObtained);
- }
- else
- {
- printf("DHCP enabled:no\n");
- }
- if (pAdapter->HaveWins)
- {
- printf("Have Wins:Yes\n");
- printf("Primary Wins Server:\t% s\n",pAdapter->PrimaryWinsServer.IpAddress.String);
- printf("Secondary Server:\t% s\n",pAdapter->SecondaryWinsServer.IpAddress.String);
- }
- else
- {
- printf("Have Wins:No\n");
- }
- pAdapter = pAdapter->Next;
- }
- }
- else
- {
- printf("失败\n");
- }
- return 0;
- }
3,网际校验和(internet checksum)算法
IP,TCP,UDP等许多协议的头部都设置了校验和项,计算校验和的算法一般采用网际校验和算法,它将被校验的数据按16位进行划分(若数据字节长度为奇数,则在数据尾部补一个字节0),对每16位求反码和,然后再对和取反码。
- #include<iostream>
- #include<fstream>
- using namespace std;
- #include<winsock.h> // 本机字节序转换为网络字节序:htons
- #pragma comment(lib, "WS2_32.LIB")
- /**************************************************************************
- * 计算给定数据的校验和
- *
- * 输入参数:
- * pBuffer 指向需要校验的数据缓冲区
- * nSize 需要校验的数据的大小,以字节为单位
- *
- * 返回值:
- * 16位的校验结果
- *
- **************************************************************************/
- unsigned short checksum_calculating(unsigned short *pBuffer, int nSize)
- {
- unsigned long dwCksum = 0; // 32位累加和
- // 以两字节为单位反复累加
- while(nSize > 1)
- {
- dwCksum += *pBuffer++;
- nSize -= sizeof(unsigned short);
- }
- // 如果总字节数为奇数则加上最后一个字节
- if (nSize)
- {
- dwCksum += *(unsigned char*) pBuffer;
- }
- // 将位累加和的高位与低位第一次相加
- dwCksum = (dwCksum >> 16) + (dwCksum & 0xffff);
- // 将上一步可能产生的高位进位再次与低位累加
- dwCksum += (dwCksum >> 16);
- // 返回位校验和
- return (unsigned short) (~dwCksum);
- }
- int main(int argc, char * argv[])
- {
- // 创建输入文件流
- ifstream fInfile;
- fstream fOutfile; // 创建输出文件流
- fInfile.open(argv[1], ios::in|ios::binary); // 以二进制方式打开指定的输入文件
- fInfile.seekg(0, ios::end); // 把文件指针移到文件末尾
- unsigned short wLen = (unsigned short)fInfile.tellg();// 取得输入文件的长度
- fInfile.seekg(0, ios::beg); // 文件指针位置初始化
- // 定义数据报缓冲区,缓冲区大小为+wLen ,其中为数据报类型字段、长度字段
- // 以及校验和字段的长度和,wLen为数据字段长度,即输入文件长度(以字节为单位)
- char * pBuf = new char[4 + wLen];
- pBuf[0] = unsigned char(0xab); // 给数据报类型字段赋值,这里随便弄了个0Xab
- pBuf[1] = unsigned char(wLen); // 给数据报长度字段赋值
- *(unsigned short *)(pBuf + 2) = 0; // 计算校验和之前,校验和字段先置为0
- fInfile.read(pBuf+4, wLen); // 根据输入文件填充数据报的数据字段
- // 计算校验和并把结果填入到数据报的校验和字段
- *(unsigned short *)(pBuf+2) = checksum_calculating((unsigned short *)pBuf,4+wLen);
- // 输出校验和计算结果
- cout.width(4);
- cout << "校验和为:x" << hex << htons( *(unsigned short *)(pBuf+2) )
- << " (以网络顺序显示)"<< endl;
- // 以二进制方式打开输出文件
- fOutfile.open(argv[2],ios::in|ios::out|ios::binary|ios::trunc);
- // 将pBuf中的数据报写入输出文件
- fOutfile.write((char *)pBuf, wLen+4);
- cout<< "数据报已成功保存在" << argv[2] << "文件中!" << endl;
- delete [] pBuf; // 释放数据报缓冲区
- fInfile.close(); // 关闭输入文件流
- fOutfile.close(); // 关闭输出文件流
- return 0;
- }