当前位置: 首页 > news >正文

网络扫描程序的详细分析与实现

1.网络扫描简介

  网络扫描是一种自动化程序,用于检测远程或本地主机的弱点和漏洞。漏洞扫描是入侵防范最基本的工作,攻击者正式利用各种漏洞入侵系统。借助自动化的扫描工作,在攻击者之前发现漏洞问题,并给予相应的修正程序。

  一名攻击者入侵系统,一般分为四个步骤:系统发现,漏洞探测,漏洞利用和痕迹清除。

  本文的重点就是在于系统发现与漏洞探测方面。

2.端口扫描技术

  端口扫描能够用来查找目标主机已开放的端口,包括TCP和UDP端口。当前针对TCP端口的扫描技术有三种,分别为:全连接扫描,SYN扫描和FIN扫描。针对UDP端口的扫描技术一般是采用ICMP报文中端口不可达的信息来识别UDP端口是否开放。

  • 全连接扫描

  作为最基本的TCP扫描方式,它利用connect()函数,向每一个目标主机的端口发起连接,若端口处于侦听状态,则函数返回成功;否则可以判断出该端口没有开放。

  全连接扫描有两个好处,首先它不许要得到系统权限,任何用户都可以使用,另外一个是,可以采用多线程并发扫描,扫描的速度也不慢。

  • SYN扫描

  SYN扫描通常认为是“半开放”的扫描,这是因为扫描程序不必打开一个完全的TCP连接。扫描程序构造并发送一个SYN数据包,执行TCP三次握手的第一步,若返回一个SYN|ACK数据包,则表示端口处于侦听状态,否则返回一个RST数据包,表示端口没有开放。这种方式需要root权限,并且它不会在主机上留下记录。

  • FIN扫描

  FIN扫描与SYN扫描类似,也是构造数据包,通过识别回应来判断端口状态。发送FIN数据包后,如果返回一个RST数据包,则表示端口没有开放,处于关闭状态,否则,开放端口会忽略这种报文。这种方式难以被发现,但是这种方式可能不准确。

  • UDP的ICMP端口不可达扫描

  UDP扫描的方法并不常见。虽然UDP协议相对比较简单,无论UDP端口是否开放,对于接受到的探测包,他本身默认是不会发送回应信息的。不过UDP扫描有一种方法,就是利用主机ICMP报文的回应信息来识别,当一个关闭的UDP端口发送一个数据包时,会返回一个ICMP_PORT_UNREACH的错误。但由于UDP不可靠,ICMP报文也是不可靠,因此存在数据包中途丢失的可能。

3.具体实现

  首先介绍全连接扫描模块的设计。

  具体流程:

  1. 判断端口是否处于扫描范围
  2. 初始化套接字,发起连接
  3. 调用getservbyport函数,获取端口映射的TCP服务
  4. 输出结果
  5. 退出程序

实现代码:

复制代码
//全连接扫描
void scan(char *ip,int minport,int maxport)
{
    if(maxport<minport){
        printf("error:can't scan int this condition\n");
        return;
    }
    printf("test ip:%s,port:%d,port:%d\n",ip,minport,maxport);
    
    int i=0;
    printf("Now start scanning.....\n");
    for(i=minport;i<maxport;++i){
        struct sockaddr_in clientaddr;
        
        clientaddr.sin_family=AF_INET;
        clientaddr.sin_addr.s_addr=inet_addr(ip);
        clientaddr.sin_port=htons(i);
        
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0){
            perror("error:create socket\n");
            return;
        }
        
        int error=connect(sock,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
        if(error<0){
            printf("Port:%5d | Tatus:closed\n",i);
            fflush(stdout);//清空缓存
        }
        else{
            struct servent* sptr;
            if((sptr=getservbyport(htons(i),"tcp"))!=NULL)
                printf("Port:%5d | Server:%s | Status:open\n",i,sptr->s_name);
            else
                printf("Port:%5d | Status:open\n",i);
        }
        close(sock);
    }
}
复制代码

  可以对上述代码进行改进,可以加入多线程的的方式来解决扫描速度较慢的问题。

  在这里需要定义一个结构体,来为线程传递必要的参数。

typedef struct threadpara{
    char ip[20];
    int minport;
    int maxport;
}tp;

  这样的话,我们就可以定义一个线程函数用于连接不同的端口。

复制代码
void *conntthread(void *threadp)
{
    struct threadpara *pThreadpara=(struct threadpara*)threadp;
    
    int i;
    printf("minport:%d maxport:%d thread....\n",pThreadpara->minport,pThreadpara->maxport);
    
    for(i=pThreadpara->minport;i<pThreadpara->maxport;++i){
        struct sockaddr_in clientaddr;
        
        clientaddr.sin_family=AF_INET;
        clientaddr.sin_addr.s_addr=inet_addr(pThreadpara->ip);
        clientaddr.sin_port=htons(i);
        
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0){
            perror("error:create socket\n");
            return 0;
        }
        
        int error=connect(sock,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
        if(error<0){
            printf("Port:%5d | Tatus:closed\n",i);
            fflush(stdout);//清空缓存
        }
        else{
            struct servent* sptr;
            if((sptr=getservbyport(htons(i),"tcp"))!=NULL)
                printf("Port:%5d | Server:%s | Status:open\n",i,sptr->s_name);
            else
                printf("Port:%5d | Status:open\n",i);
        }
        close(sock);
    }
    pthread_exit(NULL);
}
复制代码

  接下来,就是在主扫描程序中添加启动线程的模块,主要代码如下:

复制代码
int count=(maxport-minport)/thread_num;
    
    printf("count:%d num:%d ip:%s,port:%d,port:%d\n",count,thread_num,ip,minport,maxport);
    printf("Now start scanning....\n");
    
    int i;
    for(i=0;i<count;++i){
        struct threadpara threadpara;
        strcpy(threadpara.ip,ip);
        threadpara.minport=minport+count*i;
        if(i==thread_num-1)
            threadpara.maxport=maxport;
        else
            threadpara.maxport=threadpara.minport+count;
            
        printf("i:%d maxport:%d,minport:%d\n",i,threadpara.maxport,threadpara.minport);
        
        int temp;
        pthread_t threadid;
        temp=pthread_create(&threadid,NULL,conntthread,(void*)&threadpara);
        if(temp<0)
            printf("create thread error...\n");
        pthread_join(threadid,NULL);
    }
复制代码

  接下来,再介绍一个SYN半连接扫描的例子。

  扫描模块的设计流程图如下:

  具体流程:

  1. 初始化套接字
  2. 设置信号量以及全局变量flag(控制扫描状态)
  3. 启动接收线程
  4. 构造SYN数据包,开始扫描

  首先,构造并发送syn包模块的代码如下: 

复制代码
int sendSyn(int sendSocket,u_long sourceIP,struct sockaddr_in *dest)
{
    unsigned char netPacket[sizeof(struct tcphdr)];
    struct tcphdr* tcp;
    u_char * pPseudoHead;
    u_char pseudoHead[12+sizeof(struct tcphdr)];
    u_short tcpHeadLen;
    
    memset(netPacket,0,sizeof(struct tcphdr));    
    //构造syn数据包
    tcpHeadLen=htons(sizeof(struct tcphdr));
    tcp=(struct tcphdr*)netPacket;
    tcp->source=htons(10240);
    tcp->dest=dest->sin_port;
    tcp->seq=htonl(12345);
    tcp->ack_seq=0;
    tcp->doff=5;
    tcp->syn=1;
    tcp->window=htons(10052);
    tcp->check=0;
    tcp->urg_ptr=0;
    pPseudoHead=pseudoHead;
    
    memset(pPseudoHead,0,12+sizeof(struct tcphdr));
    memcpy(pPseudoHead,&sourceIP,4);
    
    pPseudoHead+=4;
    
    memcpy(pPseudoHead,&(dest->sin_addr),4);
    pPseudoHead+=5;
    
    memset(pPseudoHead,6,1);
    pPseudoHead++;
    
    memcpy(pPseudoHead,&tcpHeadLen,2);
    pPseudoHead+=2;
    
    memcpy(pPseudoHead,tcp,sizeof(struct tcphdr));
    tcp->check=checksum((u_short*)pPseudoHead,sizeof(struct tcphdr)+12);
    
    int temp=sendto(sendSocket,netPacket,sizeof(struct tcphdr),0,(struct sockaddr*)dest,sizeof(struct sockaddr_in));
    return temp;
}

复制代码

  TCP校验和代码:

复制代码
unsigned short checksum(unsigned short *addr,int len)//TCP校验和
{
    int nleft=len;
    int sum=0;
    unsigned short *w=addr;
    unsigned short answer=0;
    
    while(nleft>1){
        sum+=*w++;
        nleft-=2;
    }
    
    if(nleft==1){
        *(unsigned char*)(&answer)=*(unsigned char*)w;
        sum+=answer;
    }
    
    sum=(sum>16)+(sum&0xffff);
    sum+=(sum>>16);
    answer=~sum;
    return(answer);
}
复制代码

  接收返回数据包的代码如下:

复制代码
void* recv_packet(void* arg)
{
    struct sockaddr_in *in1;
    char *srcaddr;
    int size;
    u_char readbuff[1600];
    struct sockaddr from;
    int from_len;
    struct tcphdr* tcp;
    struct servent* sptr;
    tcp=(struct tcphdr*)(readbuff+20);
    int fd=*((int*)arg);
    
    while(1){
        if(flag==1)
            return 0;
            
        size=recvfrom(fd,(char*)readbuff,1600,0,&from,(socklen_t *)&from_len);
        if(size<40)
            continue;
        printf("data is ok...ack:seq:%u,destport:%u\n",ntohl(tcp->ack_seq),ntohs(tcp->dest));
        if((ntohl(tcp->ack_seq)!=123456)||(ntohs(tcp->dest)!=10240))
            continue;
        if(tcp->rst&&tcp->ack){
            printf("port:%5d | Status: closed\n",htons(tcp->source));
            continue;
        }
        if(tcp->ack&&tcp->syn){
            in1=(struct sockaddr_in*)&from;
            srcaddr=inet_ntoa(in1->sin_addr);
            printf("SERVER:%s\r",srcaddr);
        }
        if((sptr=getservbyport(tcp->source,"tcp"))!=NULL){
            printf("Port:%d Server:%s | Status: open\n",htons(tcp->source),sptr->s_name);
        }
        else
            printf("Port:%5d | Status:open\n",htons(tcp->source));
        fflush(stdout);
        continue;
    }
}
复制代码

  扫描程序的主要程序:

复制代码
void synscan(char* ip,int minport,int maxport)
{
    pthread_t tid;
    struct ifreq if_data;
    int fd;
    u_long addr_p;
    
    struct sockaddr_in clientaddr;
    fd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);//SOCK_RAW原始套接子(数据包式)
    if(fd<0){
        perror("error:create raw socket\n");
        return;
    }
    
    signal(SIGALRM,Alarm);
    
    strcpy(if_data.ifr_name,"eth0");
    if(ioctl(fd,SIOCGIFADDR,&if_data)<0){
        perror("error:ioctl\n");
        return;
    }
    memcpy((void*)&addr_p,(void*)&if_data.ifr_addr.sa_data+2,4);
    
    bzero(&clientaddr,sizeof(clientaddr));
    clientaddr.sin_family=AF_INET;
    clientaddr.sin_addr.s_addr=inet_addr(ip);
    
    printf("Now start scannning...\n");
    int err;
    err = pthread_create(&tid,NULL,recv_packet,(void*)&fd);
    if(err<0)
        perror("error:create thread\n");
    int i=minport;
    for(;i<maxport;++i){
        clientaddr.sin_port=htons(i);
        if(sendSyn(fd,addr_p,&clientaddr)<0){
            perror("error:send syn\n");
        }
        alarm(3);
    }
    pthread_join(tid,NULL);
}
复制代码

 

最后附上之前的IP地址扫描代码!


本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/archive/2013/04/10/3010087.html,如需转载请自行联系原作者

相关文章:

  • Linux集群--lvs
  • 容器技术贯穿什么基础的设施睿云智合Wise2C的体现
  • 在electron中实现跨域请求,无需更改服务器端设置
  • Java的多进程运行模式分析
  • Enterprise Library Step By Step系列(十):缓冲应用程序块——进阶篇
  • 在ISA 2006企业版环境下配置存储服务器(CSS)
  • Android基于Cling开发DLNA应用
  • linux备份和mysql登陆自动输入密码
  • 软件自动化部署脚本
  • 深度神经网络(DNN)的正则化
  • 敏捷个人手机应用:如何使用时中法目标
  • Vue.js系列之三模板语法
  • docker compose部署服务
  • SQL2005中时,Diagrams的问题
  • PreparedStatement--摘抄自http://blog.chinaunix.net/u/28512/showart_221625.html
  • css的样式优先级
  • jquery cookie
  • JS+CSS实现数字滚动
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • text-decoration与color属性
  • 解析带emoji和链接的聊天系统消息
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 找一份好的前端工作,起点很重要
  • 正则表达式小结
  • Java数据解析之JSON
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ![CDATA[ ]] 是什么东东
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (2)STM32单片机上位机
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (Java数据结构)ArrayList
  • (pytorch进阶之路)扩散概率模型
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (二)hibernate配置管理
  • (二十三)Flask之高频面试点
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (算法)N皇后问题
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (未解决)macOS matplotlib 中文是方框
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转载)PyTorch代码规范最佳实践和样式指南
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • .NET Core中Emit的使用
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .php结尾的域名,【php】php正则截取url中域名后的内容
  • @NoArgsConstructor和@AllArgsConstructor,@Builder
  • [2008][note]腔内级联拉曼发射的,二极管泵浦多频调Q laser——
  • [ASP.NET 控件实作 Day7] 设定工具箱的控件图标
  • [codeforces]Recover the String