scanhost——主机扫描程序

主机扫描概述

使用“ICMP响应-请求”,“ICMP响应-应答”,“TCP连接请求和TCP协议的RST标志”、“UDP协议的包和ICMP不能到达的报文”可以探索目的主机是否存在于互连的网络中
在进行主机扫描时,按照顺序改变接收端的IP地址,发送ICMP响应-请求,对返回的ICMP响应-应答进行列表,就能得到在此计算机网络中互联的主机或者路由器的IP地址。
提醒:对于公网上的计算机进行扫描可能会存在犯罪行为,慎重

程序原理

向探测的目的IP地址发送ICMP响应-请求包,设置倒计时,如果在此倒计时中收到了来自目的IP地址的ICMP响应-应答包,则推断该主机存在,否则认为此IP主机不存在,增加IP地址,循环扫描后面的IP地址。

scannhost会使用raw IP,需要root权限

使用方法

./scanhost first_ip last_ip

在这里插入图片描述

程序代码

#include<sys/time.h>

#include<sys/types.h>
#include<arpa/inet.h>

#include<sys/socket.h>
#include<netdb.h>

#include<netinet/in_systm.h>
#include<unistd.h>

#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>

#include<cstdio>
#include<cstdlib>
#include<cstring>

#include<iostream>
#include<sys/select.h>


using namespace std;

#define CHKADDRESS(_saddr_)\
{\
    u_char *p=(u_char*)&(_saddr_);\
    if((p[0]==10)||(p[0]==168&&16<=p[1]&&p[1]<=31)||(p[0]==192&&p[1]==168))\
    ;\
    else{\
    fprintf(stderr,"ip address error!\n");\
    exit(EXIT_FAILURE);\
    }\
}

enum{CMD,START_IP,LAST_IP};

#define BUFSIZE 4096
#define PACKET_LEN 72
//创建icmp请求并发送
void make_icmp8_packet(struct icmp* icmp,int len,int n);
//设置tv结构体,检查是否超时
void tvsub(struct timeval* out,struct timeval* in);

u_short checksum(u_short *data,int len);

int main(int argc,char** argv)
{
    //发送端IP地址
    struct sockaddr_in  send_ip;
    
    //套接字描述符
    int sockfd;
    //发送缓冲区
    char send_buf[BUFSIZE];
    //接受缓冲区
    char recv_buf[BUFSIZE];
    //扫描的ip地址的初始值
    int start_ip;
    //扫描的ip地址的结束值
    int last_ip;
    //扫描的ip地址数
    int dstip;
    //时间信息
    struct timeval tv;
    //循环变量
    int i;
    //select的文件描述符
    fd_set readfd;
    //ip报头的结构体
    struct ip* ip;
    struct icmp* icmp;
    //报文长度
    int hlen;

    if(argc!=3)
    {
        fprintf(stderr,"usage:%s start_ip last_ip",argv[CMD]);
        exit(EXIT_FAILURE);
    }

    //设定扫描的ip范围
    //先转换为ip地址,然后转换为主机long
    start_ip=ntohl(inet_addr(argv[START_IP]));
    last_ip=ntohl(inet_addr(argv[LAST_IP]));

    memset((char*)&send_ip,0,sizeof(struct sockaddr_in));
    send_ip.sin_family=AF_INET;

    //raw套接字
    if((sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))<0)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }


    //扫描主机的主要子程序
    for(dstip=start_ip;dstip<last_ip;dstip++)
    {
        send_ip.sin_addr.s_addr=htonl(dstip);
        //检查发送端的ip地址
        CHKADDRESS(send_ip.sin_addr);

        //发送三次icmp请求
        for(i=0;i<3;i++)
        {
            printf("scan'%s(%d)'\n",inet_ntoa(send_ip.sin_addr),i+1);
            fflush(stdout);

            //生成icmp响应请求包
            make_icmp8_packet((struct icmp*)send_buf,PACKET_LEN,i);

            if(sendto(sockfd,(char*)&send_buf,PACKET_LEN,0,(struct sockaddr*)&send_ip,sizeof(send_ip))<0)
            {
                perror("sendto");
                exit(EXIT_FAILURE);
            }

            //设置select的超时值
            tv.tv_sec=0;
            tv.tv_usec=200*1000;

            while(1)
            {
                //设置使用select的检查描述符
                FD_ZERO(&readfd);
                FD_SET(sockfd,&readfd);

                if(select(sockfd+1,&readfd,NULL,NULL,&tv)<0)
                    break;
            

                //接受错误
                if(recvfrom(sockfd,recv_buf,BUFSIZE,0,NULL,NULL)<0)
                {
                    perror("recvfrom");
                    exit(EXIT_FAILURE);
                }

                //ip报头中ip长度字段
                ip=(struct ip*)recv_buf;
                hlen=ip->ip_hl<<2;
                
                //源地址为发送的ip地址
                if(ip->ip_src.s_addr==send_ip.sin_addr.s_addr)
                {
                    struct icmp* icmp;
                    //icmp
                    icmp=(struct icmp*)(recv_buf+hlen);

                    //icmp请求响应
                    if(icmp->icmp_type==ICMP_ECHOREPLY)
                    {
                        //输出主机的ip地址
                        printf("%15s",inet_ntoa(*(struct in_addr*)&(ip->ip_src.s_addr)));
                
                        gettimeofday(&tv,(struct timezone*)0);

                        tvsub(&tv,(struct timeval*)(icmp->icmp_data));

                        /*
                        __time_t tv_sec;		/* Seconds.   
                        __suseconds_t tv_usec;	/* Microseconds.  
                        将秒和微秒转化为毫秒
                        */
                        printf(":RTT:%8.4fms\n",tv.tv_sec*1000.0+tv.tv_usec/1000.0);

                        goto exit_loop;
                    }
                }
            }
        }
        exit_loop:;
    }

    //关闭
    close(sockfd);
    return EXIT_SUCCESS;
}
/*
function:生成icmp包

返回值:void
*/
void make_icmp8_packet(struct icmp* icmp,int len,int n)
{   
    memset((char*)icmp,0,len);
    
    //数据部分记录时间
    gettimeofday((struct timeval*)(icmp->icmp_data),(struct timezone *)0);

    //生成
    icmp->icmp_type=ICMP_ECHO;
    icmp->icmp_code=0;
    icmp->icmp_id=0;
    icmp->icmp_seq=n;
    
    //计算检查和
    icmp->icmp_cksum=0;
    icmp->icmp_cksum=checksum((u_short*)icmp,len);

}

/*
struct timeval的减法运算,计算结果存储到out中
*/

void tvsub(struct timeval* out,struct timeval* in)
{
    if((out->tv_usec-=in->tv_usec)<0)
    {
        out->tv_sec--;
        out->tv_usec+=1000000;
    }

    //计算时间差
    out->tv_sec-=in->tv_sec;
}

/*
function:计算检查和
big_endian little_endian 都适用
*/
u_short checksum(u_short *data,int len)
{
    u_long sum=0;
    //short为两个字节:: 16bit
    //每次两个字节
    for(;len>1;len-=2)
    {
        sum+=*data++;
        //右移,将超出的位置
        if(sum&0x80000000)
            sum=(sum&0xffff)+(sum>>16);
    }

    //奇数个字节的情况
    if(len==1)
    {
        u_short i=0;
        *(u_char*)(&i)=*(u_char*)data;
        sum+=i;
    }

    //对于溢出的位进行处理,sum占16位
    //在前十六位为1得情况下反复进行处理
    while(sum>>16)
    {
        sum=(sum&0xffff)+(sum>>16);
    }

    //在补码为0的情况下,并不使用一个补码,而是原封不动的将该值作为返回值
    return (sum==0xffff)?sum:~sum;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值