C++活动主机扫描
- (1)以命令方式运行:DOS>scanHost start_ip end_ip
- (2)输出内容:活动主机IP地址。
实现原理:
- (1)通过某IP发送ICMP_ECHO请求报文,接收到ICMP_response 报文,表明该IP主机活动。
- (2)利用原始套接字
- (3)为了提高检测时间,利用多线程技术。
#include
#include
#include <winsock2.h>
#include
#pragma comment (lib, “ws2_32.lib”)
using namespace std;
//ip包头部结构
typedef struct iphdr
{
unsigned int headlen : 4; //头部长度
unsigned int version : 4; //版本号
unsigned char tos; //服务类型
unsigned short totallen; //总长度
unsigned short id; //ip包id号
unsigned short flag; //标记
unsigned char ttl; //生存时间
unsigned char prot; //协议类型
unsigned short checksum; //校验和
unsigned int sourceIp; //源IP地址
unsigned int destIp; //目的ip地址
}IpHeader;
typedef struct icmphdr
{
BYTE type; //ICMP类型码
BYTE code; //ICMP子类型
USHORT checksum; //校验和
USHORT id; //ICMP包ID号
USHORT seq; //序列号
}IcmpHeader;
#define ICMP_ECHO 8 //ICMP回送请求
#define ICMP_ECHO_REPLY 0 //ICMP回送应答
#define ICMP_MIN 8 //ICMP包头长度
#define STATUS_FAILED 0xFFFF //错误码
#define DEF_PACKET_SIZE 32 //缺省数据长度
#define MAX_PACKET 1024 //最大数据长度
#define MAX_PING_PACKET_SIZE MAX_PACKET + sizeof(IpHeader)
WSADATA wsaData;
SOCKET sockRaw; //原始套接字
struct sockaddr_in dest, from, end1;
int fromlen = sizeof(from); //接受ICMP包长度
char* recvbuf = new char[MAX_PING_PACKET_SIZE];
unsigned int addr = 0; //IP地址
long ThreadNumCounter = 0, ThreadNumLimit = 20;
long* aa = &ThreadNumCounter;
//填充ICMP包的函数
void fill_icmp_data(char* icmp_data, int datasize)
{
IcmpHeader* icmp_hdr;
char* datapart;
icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->type = ICMP_ECHO; //设置信息类型
icmp_hdr->id = (USHORT)GetCurrentThreadId(); //设置当前线程的ID号
datapart = icmp_data + sizeof(IcmpHeader); //计算ICMP包数据部分
memset(datapart, ‘A’, datasize - sizeof(IcmpHeader));
}
//解析IP地址的函数
void decode_resp(char* buf, int bytes, struct sockaddr_in* from)
{
IpHeader* iphdr;
IcmpHeader* icmphdr;
unsigned short iphdrlen;
iphdr = (IpHeader*)buf;
//跳过IP包头部
iphdrlen = iphdr->headlen * 4;
icmphdr = (IcmpHeader*)(buf + iphdrlen);
//数据包过短,丢弃
if (bytes < iphdrlen + ICMP_MIN)
{
return;
}
//不是回送响应,丢弃
if (icmphdr->type != ICMP\_ECHO\_REPLY)
{
return;
}
//ID号不相符,丢弃
if (icmphdr->id != (USHORT)GetCurrentThreadId())
{
return;
}
//输出处于活动状态的主机
cout << "活动主机: " << inet\_ntoa(from->sin\_addr) << endl;
}
//校验和计算函数
USHORT checksum(USHORT* buffer, int size)
{
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += (UCHAR)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
}
//线程调用函数
DWORD WINAPI FindIP(LPVOID pIPAddrTemp)
{
InterlockedIncrement(aa);
char icmp\_data\[MAX\_PACKET\];
memset(icmp\_data, 0, MAX\_PACKET);
int datasize = DEF\_PACKET\_SIZE;
datasize += sizeof(IcmpHeader);
//填充ICMP包
fill\_icmp\_data(icmp_data, datasize);
((IcmpHeader*)icmp_data)->checksum = 0;
((IcmpHeader*)icmp_data)->seq = 0;
//计算校验和后填入
((IcmpHeader*)icmp\_data)->checksum = checksum((USHORT*)icmp\_data, datasize);
//发送ICMP包
int bwrote = sendto(sockRaw, icmp_data, datasize, 0, (struct sockaddr*)pIPAddrTemp, sizeof(dest));
int n = 0;
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "time out" << endl;
}
cout << "Sendto failed: " << WSAGetLastError() << endl;
ExitProcess(STATUS_FAILED);
n = 1;
}
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "Timed out" << endl;
ExitProcess(STATUS_FAILED);
n = 1;
}
if (bwrote < datasize)
{
cout << "Wrote " << bwrote << " bytes" << endl;
ExitProcess(STATUS_FAILED);
n = 1;
}
//接收ICMP包
int bread = recvfrom(sockRaw, recvbuf, MAX\_PING\_PACKET_SIZE, 0, (struct sockaddr*)&from, &fromlen);
if (bread == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "Timed out" << endl;
}
cout << "Recvfrom failed: " << WSAGetLastError() << endl;
ExitProcess(STATUS_FAILED);
n = 1;
}
//去掉IP包头部,判断并输出IP地址
if (n == 0)
{
decode_resp(recvbuf, bread, &from);
}
InterlockedDecrement(aa);
return 0;
}
int main(int argc, char* argv[])
{
//检查输入命令格式
if (argc != 3)
{
cout << “Please input command: ScanHost start_address end_address” << endl;
return 0;
}
//开始使用Ws2_32.dll
if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0)
{
cout << "WSAStartup failed: " << GetLastError() << endl;
ExitProcess(STATUS_FAILED);
}
//创建原始套接字
sockRaw = WSASocket(AF\_INET, SOCK\_RAW, IPPROTO\_ICMP, NULL, 0, WSA\_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)
{
cout << "WSASocket() failed: " << WSAGetLastError() << endl;
ExitProcess(STATUS_FAILED);
}
//设置接收延时
int timeout = 1000;
int bread = setsockopt(sockRaw, SOL\_SOCKET, SO\_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
cout << "Failed to set recv timeout: " << WSAGetLastError() << endl;
ExitProcess(STATUS_FAILED);
}
//设置发送延时
timeout = 1000;
bread = setsockopt(sockRaw, SOL\_SOCKET, SO\_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
cout << "Failed to set recv timeout: " << WSAGetLastError() << endl;
ExitProcess(STATUS_FAILED);
}
//初始化地址结构
memset(&dest, 0, sizeof(dest));
unsigned long startIP, endIP;
dest.sin\_family = AF\_INET;
dest.sin\_addr.s\_addr = inet_addr(argv\[1\]);
startIP = inet_addr(argv\[1\]);
end1.sin\_family = AF\_INET;
end1.sin\_addr.s\_addr = inet_addr(argv\[2\]);
endIP = inet_addr(argv\[2\]);
HANDLE hThread;
while (htonl(startIP) <= htonl(endIP))
{
//判断线程数目
if (ThreadNumCounter > ThreadNumLimit)
{
Sleep(5000);
continue;
}
DWORD ThreadID;
sockaddr\_in * pIPAddrTemp = new(sockaddr\_in);
if (!pIPAddrTemp)
{
cout << "Memory alloc failed" << endl;
return 0;
}
*pIPAddrTemp = dest;
//创建新线程
clock_t start;
start = clock();
hThread = CreateThread(NULL, NULL, FindIP, (LPVOID)pIPAddrTemp, NULL, &ThreadID);
long i = 60000000L;
while (i--)
;
TerminateThread(hThread, 0);
InterlockedDecrement(aa);
memset(&from, 0, sizeof(from));
startIP = htonl(htonl(startIP) + 1);
dest.sin\_addr.s\_addr = startIP;
}
//是否有结束的线程
while (ThreadNumCounter != 0)
{
Sleep(2000);
return 0;
}
system("pause");
return 0;
}