参考:https://blog.csdn.net/skullsky/article/details/52244770
实验目的:
根据协议规定的ICMP数据包的标准格式,编写程序向指定子网中的目的主机(例如从192.168.1.1到192.168.1.10)发送ICMP数据包进行解析,所发现处于活动状态的主机。在本练习中只显示活动主机的IP地址,并采用多线程来提高主机扫描速度。
实验要求:
要求程序为命令行程序。例如可执行文件名为ScanHost.exe,则程序的命令行格式为: ScanHost start_addr end_addr
其中,start_addr为开始搜索的IP地址,end_addr为结束搜索的IP地址。
(2) 要求将计算的子网地址显示在控制台,具体格式为:
开始主机扫描;
活动主机:xx.xx.xx.xx
活动主机:xx.xx.xx.xx
…
(3) 要求有良好的编程规范与注释。编程所使用的操作系统、语言和编译环境不限,但是在提交的文档中需要加以注明。
(4) 要求撰写说明文档,包括程序的开发思路、工作流程、关键问题、解决思路以及进一步的改进等内容。
实际原理以及其他的代码功能介绍可参考:https://blog.csdn.net/skullsky/article/details/52244770
在这里本人把代码整理了下,外加稍微修改,代码如下:
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib,"Ws2_32.lib")
int qq=0;
USHORT checksum(USHORT* buff, int size)
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if(size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16); // ???
return (USHORT)(~cksum);
}
typedef struct iphdr{ //IP头
unsigned int headlen:4; //IP头长度
unsigned int version:4; //IP版本号
unsigned char tos; //服务类型
unsigned short id; //ID号
unsigned short flag; //标记
unsigned char ttl; //生存时间
unsigned char prot; //协议
unsigned short checksum; //效验和
unsigned int sourceIP; //源IP
unsigned int destIP; //目的IP
}IpHeader;
typedef struct icmp_hdr
{
unsigned char icmp_type; // 消息类型
unsigned char icmp_code; // 代码
unsigned short icmp_checksum; // 校验和
// 下面是回显头
unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID
unsigned short icmp_sequence; // 序列号
unsigned long icmp_timestamp; // 时间戳
} ICMP_HDR, *PICMP_HDR;
//SOCKET sRaw=::socket(AF_INET,SOCKET_RAW,TPPROTO_ICMP);
//SetTimeout(sRaw,1000,TRUE);
int SetTimeout(SOCKET s, int nTime, BOOL bRecv)
{
int ret = ::setsockopt(s, SOL_SOCKET, bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char*)&nTime, sizeof(nTime));
return ret != SOCKET_ERROR;
}
int Computer(char szDestIP[30]) //扫描主机是否存活
{
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(1,1);
if (WSAStartup(wVersionRequested , &wsaData))
{
printf("Winsock Initialization failed.\n");
exit(1);
}
SOCKET sRaw=::socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
SetTimeout(sRaw,1000,TRUE);
SOCKADDR_IN dest;
dest.sin_family=AF_INET;
dest.sin_port=htons(0);
dest.sin_addr.S_un.S_addr=inet_addr(szDestIP);
char buff[sizeof(ICMP_HDR)+32];
ICMP_HDR * pIcmp=(ICMP_HDR *)buff;
pIcmp->icmp_type=8;
pIcmp->icmp_code=0;
pIcmp->icmp_id=(USHORT)::GetCurrentProcessId();
pIcmp->icmp_checksum=0;
pIcmp->icmp_sequence=0;
memset(&buff[sizeof(ICMP_HDR)],'E',32);
USHORT nSeq=0;
char revBuf[1024];
SOCKADDR_IN from;
int nLen=sizeof(from);
static int nCount=0;
int nRet;
/* if (nCount++==4)
{
break;
}*/
pIcmp->icmp_checksum=0;
pIcmp->icmp_timestamp=::GetTickCount();
pIcmp->icmp_sequence=nSeq++;
pIcmp->icmp_checksum=checksum((USHORT *)buff,sizeof(ICMP_HDR)+32);
nRet=::sendto(sRaw,buff,sizeof(ICMP_HDR)+32,0,(SOCKADDR *)&dest,sizeof(dest));
if (nRet==SOCKET_ERROR)
{
printf("sendto() failed:%d\n",::WSAGetLastError());
return -1;
}
nRet=::recvfrom(sRaw,revBuf,1024,0,(sockaddr *)&from,&nLen);
if (nRet==SOCKET_ERROR)
{
//printf("%s 主机没有存活!\n",szDestIP);
return -1;
}
printf("活动主机:%s \n",szDestIP);
closesocket(nRet);
WSACleanup();
return 0;
}
void Port(char adr[20]) //扫描存活主机端口
{
int mysocket,m=0;//这里扫描的
int pcount = 0;
struct sockaddr_in my_addr;
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(1,1);
//printf("请输入要扫描的端口范围(例如1-1024):");
//scanf("%d-%d",&m,&n);
if (WSAStartup(wVersionRequested , &wsaData))
{
printf("Winsock Initialization failed.\n");
exit(1);
}
for(int i=m; i<qq; i++)
{
if((mysocket = socket(AF_INET, SOCK_STREAM,0)) == INVALID_SOCKET)
exit(1);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(i);
my_addr.sin_addr.s_addr = inet_addr(adr);
if(connect(mysocket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == SOCKET_ERROR)
{
printf("Port %d - 关闭\n", i);
closesocket(mysocket);
}
else
{
pcount++;
printf("Port %d - 打开\n", i);
}
}
//不知道啥,注释掉~ printf("%d ports open on host - %s\n", pcount, adr);
closesocket(mysocket);
WSACleanup();
}
void change(int a,int b,int c,int d,char IP[20]) //IP转换
{
char IPPort[4][4]={'\0'};
char temp[2]={'.','\0'};
itoa(a,IPPort[0],10);
itoa(b,IPPort[1],10);
itoa(c,IPPort[2],10);
itoa(d,IPPort[3],10);
strcat(IP,IPPort[0]);
strcat(IP,temp);
strcat(IP,IPPort[1]);
strcat(IP,temp);
strcat(IP,IPPort[2]);
strcat(IP,temp);
strcat(IP,IPPort[3]);
}
int main()
{
int a[4],b[4];
loop1:
printf("ScanHost start_addr end_addr:");
scanf("%d.%d.%d.%d %d.%d.%d.%d",&a[0],&a[1],&a[2],&a[3],&b[0],&b[1],&b[2],&b[3]);
if (a[0]>255||a[1]>255||a[2]>255||a[3]>255||b[0]>255||b[1]>255||b[2]>255||b[3]>255)
{
printf("输入的起始地址有误!请重新输入!\n");
goto loop1;
}
//ip地址的计数次数,只能计算最后一小数点位,当然想更完美的可以修改下代码
qq=b[0]-a[0];
while(!(a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2]&&a[3]==(b[3]+1)))
{
char IP[20]={'\0'};
change(a[0],a[1],a[2],a[3],IP);
if((Computer(IP))==0)
{
Port(IP);
}
a[3]++;
if (a[3]>=255)
{
a[3]=0;
a[2]++;
}
if (a[2]>=255)
{
a[2]=0;
a[1]++;
}
if (a[1]>=255)
{
a[1]=0;
a[0]++;
}
if (a[0]>=255)
{
printf("地址溢出!\n");
break;
}
}
}
运行结果截图:
这个代码只能计算出最后一小数点的数,如果是程序更加完美可以在“qq”出修改~
编译环境:VC 6.0 尝试了DEV C++发现出错,哎。。。