mac/unix系统:c++实现一个端口扫描器

方方和圆圆 方方和圆圆     2022-08-20     169

关键词:

  简易端口扫描器

  在比较早以前,我用过S扫描器, 以及大名鼎鼎的nmap扫描器, 可以快速扫描某个主机开放的端口, 今天使用C实现这样一个软件,

  编译环境为Mac, 系统版本10.11.6:

#include <stdio.h>  
#include <stdlib.h>  
#include <sys/socket.h>  
#include <unistd.h>
#include <time.h>  
#include <sys/types.h>  
#include <netinet/in.h>  
#include <netdb.h>
#include <arpa/inet.h>

void msg()  
{  
  printf("EP:scan ip startport endport\nEP:scan ip 127.0.0.1 20 2009\n");  
}  
int main(int argc,char** argv)  
{  
  char *ip;  
  int startport,endport,sockfd,i;  
  struct sockaddr_in to;  
  float costtime;  
  clock_t start,end;  
  if(4!=argc)  
  {  
    msg();  
    return 0;  
  }  
  ip=argv[1];  
  startport=atoi(argv[2]);  
  endport=atoi(argv[3]);  
  if(startport<1 || endport>65535 || endport<startport)  
  {  
    printf("端口范围出错/n");  
    return 0;   
  }  
  else{
    printf("IP:%s %d-%d\n",ip,startport,endport);  
  }
  to.sin_family=AF_INET;  
  to.sin_addr.s_addr=inet_addr(ip);  
  start=clock();  
  for(i=startport;i<=endport;i++)  
  {  
    sockfd=socket(AF_INET,SOCK_STREAM,0);  
    to.sin_port=htons(i);  
    if(connect(sockfd,(struct sockaddr *)&to,sizeof(struct sockaddr)) == 0) {
         printf("%s    %d\n",ip,i);
         close(sockfd);
    };
  }  
  end=clock();  
  costtime=(float)(end-start)/CLOCKS_PER_SEC;  
  printf("用时:%f秒\n",costtime);  
  return 0;  
}  

  亲测可行:

  以上的代码只能检测固定的ip, 通过更改源码, 软件可以支持一段的ip端口检测, 多加一个循环:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include <sys/select.h>
#include <string>
void msg()
{
    printf( "EP:scan ip startport endport\nEP:scan ip 127.0.0.1 20 2009\n" );
    printf( "EP:scan ip endip startport endport\nEP:scan ip 127.0.0. 250 20 2009\n" );
}
void runsock(int sockfd, struct sockaddr_in to, char *ipval, int i) {
    sockfd = socket( AF_INET, SOCK_STREAM, 0 );
    //fcntl(sockfd, F_SETFL, O_NONBLOCK);
    to.sin_port    = htons( i );
    //printf( "IP:%s  %d\n", ipval, i );
    if ( connect( sockfd, (struct sockaddr *) &to, sizeof(struct sockaddr) ) == 0 ){
        printf( "%s    %d\n", ipval, i );
        close( sockfd );
    }
}

int main( int argc, char* argv[] ){
    char            * ip;
    char            * endip;
    int            startport, endport, sockfd, i;
    struct sockaddr_in    to;
    float            costtime;
    clock_t            start, end;
    if ( 4 == argc ){
        ip        = argv[1];
        startport    = atoi( argv[2] );
        endport        = atoi( argv[3] );
        if ( startport < 1 || endport > 65535 || endport < startport ){
            printf( "端口范围出错/n" );
            return(0);
        }else{
            printf( "IP:%s %d-%d\n", ip, startport, endport );
        }
        to.sin_family        = AF_INET;
        to.sin_addr.s_addr    = inet_addr( ip );
        start            = clock();
        for ( i = startport; i <= endport; i++ ){
            runsock(sockfd, to, ip, i);
        }
        end        = clock();
        costtime    = (float) (end - start) / CLOCKS_PER_SEC;
        printf( "用时:%f秒\n", costtime );
        return(0);
    }else if ( 5 == argc ){
        ip        = argv[1];
        endip        = argv[2];
        startport    = atoi( argv[3] );
        endport        = atoi( argv[4] );
        char *tempip;
        if ( startport < 1 || endport > 65535 || endport < startport ){
            printf( "端口范围出错/n" );
            return(0);
        }else{
            /* 循环ip地址 */
            start = clock();
            char ipval[20];
            for ( int j = 1; j <= atoi( endip ); j++ ){
                sprintf( ipval, "%s%d", ip, j );
                printf( "IP:%s\n", ipval );
                to.sin_family        = AF_INET;
                to.sin_addr.s_addr    = inet_addr( ipval );
                for ( i = startport; i <= endport; i++ ){
                    runsock(sockfd, to, ipval, i);
                }
            }
            end        = clock();
            costtime    = (float) (end - start) / CLOCKS_PER_SEC;
            printf( "用时:%f秒\n", costtime );
        };/* 循环端口 */
        return(0);
    }
    msg();
    return(0);
}

  局域网网段IP端口扫描器

  看起来这个扫描器是实现了, 但是还有一个天大的问题, 那就是connect是同步的, 如果有些ip是不存在的, 那么connect函数就会阻塞在那边, 导致运行非常缓慢,那就需要异步的socket连接, 涉及select.h, 通过icmp判断存活主机:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <setjmp.h>
#include <errno.h>
#include <sys/select.h>
#include <fcntl.h>

#define PACKET_SIZE 4096

/* 计算校验和的算法 */
unsigned short cal_chksum(unsigned short *addr,int len)
{
    int sum=0;
    int nleft = len;
    unsigned short *w = addr;
    unsigned short answer = 0;
    /* 把ICMP报头二进制数据以2字节为单位累加起来 */
    while(nleft > 1){
        sum += *w++;
        nleft -= 2;
    }
    /*
     * 若ICMP报头为奇数个字节,会剩下最后一字节。
     * 把最后一个字节视为一个2字节数据的高字节,
     * 这2字节数据的低字节为0,继续累加
     */
    if(nleft == 1){
        *(unsigned char *)(&answer) = *(unsigned char *)w;
        sum += answer;    /* 这里将 answer 转换成 int 整数 */
    }
    sum = (sum >> 16) + (sum & 0xffff);        /* 高位低位相加 */
    sum += (sum >> 16);        /* 上一步溢出时,将溢出位也加到sum中 */
    answer = ~sum;             /* 注意类型转换,现在的校验和为16位 */
    return answer;
}
int livetest(char* ip) {

    char    sendpacket[PACKET_SIZE];    /* 发送的数据包 */
    char    recvpacket[PACKET_SIZE];    /* 接收的数据包 */
    pid_t    pid;
    int    datalen = 56;    /* icmp数据包中数据的长度 */
    struct protoent *protocol;
    protocol = getprotobyname("icmp");
    int sockfd;
    int size = 50*1024;
    if((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0) {
        perror("socket error");
    }
    setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size) );
    
    struct sockaddr_in dest_addr;
    bzero(&dest_addr, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_addr.s_addr = inet_addr(ip);
    //send packet;
    int packsize;
    struct icmp *icmp;
    struct timeval *tval;
    icmp = (struct icmp*)sendpacket;
    icmp->icmp_type = ICMP_ECHO;    /* icmp的类型 */
    icmp->icmp_code = 0;            /* icmp的编码 */
    icmp->icmp_cksum = 0;           /* icmp的校验和 */
    icmp->icmp_seq = 1;       /* icmp的顺序号 */
    icmp->icmp_id = pid;            /* icmp的标志符 */
    packsize = 8 + datalen;   /* icmp8字节的头 加上数据的长度(datalen=56), packsize = 64 */
    tval = (struct timeval *)icmp->icmp_data;    /* 获得icmp结构中最后的数据部分的指针 */
    gettimeofday(tval, NULL); /* 将发送的时间填入icmp结构中最后的数据部分 */
    icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, packsize);/*填充发送方的校验和*/

    if(sendto(sockfd, sendpacket, packsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0){
        perror("sendto error");
    }
    //printf("send %d, send done\n",1 );
    int n;
    struct sockaddr_in from;
    int fromlen = sizeof(from);
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
    struct timeval timeo = {1,0};
    fd_set set;
    FD_ZERO(&set);
    FD_SET(sockfd, &set);

    //read , write;
    int retval = select(sockfd+1, &set, NULL, NULL, &timeo);
    if(retval == -1) {
        printf("select error\n");
        return 0;
    }else if(retval == 0 ) {
        //printf("timeout\n");
        return 0;
    }else{
        if( FD_ISSET(sockfd, &set) ){
            //printf("host is live\n");
            return 1;
        }
        return 0;
    }
    // n = recvfrom(sockfd, recvpacket,sizeof(recvpacket), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);
    // if(n<0) {
    //     perror("recvfrom error");
    // }else{
    //     printf("%d\n",n);
    // }
    //return 0;
}

static void sleep_ms(unsigned int secs){
    struct timeval tval;
    tval.tv_sec=secs/1000;
    tval.tv_usec=(secs*1000)%1000000;
    select(0,NULL,NULL,NULL,&tval);
}

void msg()
{
    printf( "EP:scan ip startport endport\nEP:scan ip 127.0.0.1 20 2009\n" );
    printf( "EP:scan ip endip startport endport\nEP:scan ip 127.0.0. 1 250 20 2009\n" );
}
void runsock(int sockfd, struct sockaddr_in to, char *ipval, int i) {
    sockfd = socket( AF_INET, SOCK_STREAM, 0 );
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
    fd_set set,writeSet;
    int error; //错误代码  
    socklen_t len = sizeof(error); 
    //while(1){     
    FD_ZERO(&set);
    FD_ZERO(&writeSet);
    struct timeval timeo= {1,0};
    //socklen_t len = sizeof(timeo);
    FD_SET(sockfd,&set);
    FD_SET(sockfd,&set);
    //setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO,&timeo, len);
    to.sin_port    = htons( i );
    //printf( "test  %s    %d , sockfd value %d\n", ipval, i , sockfd);
    //printf( "IP:%s  %d\n", ipval, i );
    //printf("%d\n",i);
    int conn = connect( sockfd, (struct sockaddr *) &to, sizeof(struct sockaddr) );
    //等待
    int retval = select(sockfd+ 1, &set, &writeSet, NULL, &timeo);

    getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len );  
    if(error == 0)  
        printf("%s Port %d is opened\n", ipval, i);
    //printf("%d\n",sockfd);
       // printf("%s :%d\n",ipval, i);
    // if (retval== -1) {
    //     perror("select error\n");
    // } else if(retval == 0){
    //     //printf("timeout\n");
    // }else{
    //     //printf("find %s :%d\n",ipval, i);    
    //     if(FD_ISSET(sockfd,&set)) {
    //         printf("find %s :%d\n",ipval, i);    
    //     }
    // }
    //}
}

int main( int argc, char* argv[] ){
    char            * ip;
    int endip, startip;
    int            startport, endport, sockfd, i;
    struct sockaddr_in    to;
    float            costtime;
    clock_t            start, end;
    if ( 4 == argc ){
        ip        = argv[1];
        startport    = atoi( argv[2] );
        endport        = atoi( argv[3] );
        if ( startport < 1 || endport > 65535 || endport < startport ){
            printf( "端口范围出错/n" );
            return(0);
        }else{
            printf( "IP:%s %d-%d\n", ip, startport, endport );
        }
        to.sin_family        = AF_INET;
        to.sin_addr.s_addr    = inet_addr( ip );
        start            = clock();
        for ( i = startport; i <= endport; i++ ){
            //printf("%d\n",i);
            runsock(sockfd, to, ip, i);
        }
        end        = clock();
        costtime    = (float) (end - start) / CLOCKS_PER_SEC;
        printf( "用时:%f秒\n", costtime );
        return(0);
    }else if ( 6 == argc ){
        ip        = argv[1];
        startip = atoi(argv[2]);
        endip        = atoi(argv[3]);
        startport    = atoi( argv[4] );
        endport        = atoi( argv[5] );
        char *tempip;
        if ( startport < 1 || endport > 65535 || endport < startport ){
            printf( "端口范围出错/n" );
            return(0);
        }else{
            /* 循环ip地址 */
            start = clock();
            char ipval[20];
            for ( int j = startip; j <= endip ; j++ ){
                sprintf( ipval, "%s%d", ip, j );
                printf( "IP:%s\n", ipval );
                if(livetest(ipval) == 1){
                    to.sin_family = AF_INET;
                    //printf("okokok\n");
                    to.sin_addr.s_addr = inet_addr( ipval );
                    for ( i = startport; i <= endport; i++ ){
                        runsock(sockfd, to, ipval, i);
                        sleep_ms(1000);
                    }    
                }
            }
            end    = clock();
            costtime = (float) (end - start) / CLOCKS_PER_SEC;
            printf( "用时:%f秒\n", costtime );
        };
        //while(1){}
        /* 循环端口 */
        return(0);
    }
    msg();
    return(0);
}

 

   使用方式1: scan ip startport endport

sudo ./s 192.168.2.114 1  139

  使用方式2: scan ip start endip startport endport

sudo ./s 192.168.1. 108 110 1 200

  还有一个问题, 就是扫描的端口不怎么准确, 经常出现误报, 有些端口跟没开, 但是扫描器会显示目标端口有开, 应该是判断sock是否连接成功的逻辑有问题, 目前没有好的解决方案, 期待大神指点一下:

    getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len );  
    if(error == 0)  
        printf("%s Port %d is opened\n", ipval, i);

 

  参考链接

    Linux C语言写的超级简单端口扫描器    http://blog.csdn.net/kongjiajie/article/details/4799986

    Linux的SOCKET编程详解    http://blog.csdn.net/hguisu/article/details/7445768/

 

  EOF

作者: NONO
出处:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830 

TCP 端口访问和 C++

...通过防火墙的端口?例如,如果程序员想要创建一个聊天系统,但程序需要知道哪些端口没有被用户的防火墙拒绝访问传入连接,有没有办法在你的代码中检查这一点?即使防火墙阻止了同一端口的传入连接,程序也可能无法将... 查看详情

用python实现一个端口扫描,只需简单几步就好

一、常见端口扫描的原理0、秘密扫描秘密扫描是一种不被审计工具所检测的扫描技术。它通常用于在通过普通的防火墙或路由器的筛选(filtering)时隐藏自己。秘密扫描能躲避IDS、防火墙、包过滤器和日志审计,从而获取目标... 查看详情

什么系统是一种自动检测远程或本地主机安全性弱点的程序

...的回应类型表示是否在使用该端口并且可由此探寻弱点。扫描器是一种自动检测远程或本地主机安全性弱点的程序,通过使用扫描器你可以不留痕迹的发现远程服务器的各种TCP端口的分配及提供的服务和它们的软件版本!这就能... 查看详情

华为od机试真题c++实现静态扫描2023q1|100分

Python题库目录C++题库目录Java题库目录目录题目思路考点Code题目题目描述静态扫描可以快速识别源代码的缺陷,静态扫描的结果以扫描报告作为输出:1、文件扫描的成本和文件 查看详情

vb6.0扫描端口

...击或怀疑电脑被植入木马时,我们往往求助于防火墙,本系统即通过实时监控全部TCP连接的方法来实现防黑客攻击。同时网络管理人员在整个网络运行期间,能否实时监控联网计算机的运行状态和操作对网络安全具有极其重要的... 查看详情

java示例代码_实现Java";扫描仪#34;在C++中

java示例代码_实现Java";扫描仪#34;在C++中 查看详情

python实现端口扫描

一、常见端口扫描的原理0、秘密扫描秘密扫描是一种不被审计工具所检测的扫描技术。它通常用于在通过普通的防火墙或路由器的筛选(filtering参考技术A一、常见端口扫描的原理0、秘密扫描秘密扫描是一种不被审计工具所检测... 查看详情

渗透测试之端口扫描

...连接,如ACK包,SYN包,不会在应用层访问,僵尸扫描:不和目标系统产生交互,极为隐蔽全连接扫描:建立完整的三次握手所有的TCP扫描方式都是基于三次握手的变化来判断目标系统端口状态隐蔽扫描:发送SYN数据包,如果收到对方发来的ACK... 查看详情

c++实现“扫描检测硬件改动”(代码片段)

这里需要用到cfgmgr32.h,参考了网上好几篇博文。#include<windows.h>#include<stdio.h>#include<cfgmgr.h>#pragmacomment(lib,"setupapi.lib")intmain()DEVINSTdevInst;CONFIGRETstatus;status=CM_Locate_DevNode( 查看详情

用c++实现一个log系统

提要近期在写一些C++的图形代码,在调试和測试过程中都会须要在终端打印一些信息出来。之前的做法是直接用std::cout<<"SomeWord"<<std::endl;这样做事实上非常的麻烦,每次都要打非常多的字母还有特殊符号,除去... 查看详情

端口扫描器的几种代码实现方案(代码片段)

...,即目标信息收集。因此能否开发出一款高效稳定的端口扫描器,往往决定了漏洞扫描器的好坏。那么说到端口扫描器,我们往往会先想到nmap、masscan等神器,它们是这个领域的标杆。但本篇并不是为了介绍这几款工具,而是谈... 查看详情

nmap扫描信息收集

...或者多个源端口的数据流量妆发大奥某一个指定的端口来实现对网络的监听,指定端口成为镜像端口或目的端口。2、ARP攻击捕获数据包   当用户不具备上网功能的时候,可以通过发起ARP攻击,来捕获目标主机上的数... 查看详情

linux系统扫描nmap与tcpdump抓包

...为目的--检测潜在风险--查找可攻击目标--收集设备/主机/系统/软件信息--发现可利用的安全漏洞基本用法nmap[扫描类型][选项]<扫描目标...>常用的扫描类型常用选项-sSTCPSYN扫描(半开)该方式发送SYN到目标端口,如果收到SYN/ACK回... 查看详情

扫描与爆破

...口上所运行的服务。甚至可以进一步确定目标主机的操作系统类型和更详细的信息。 基础知识常见端口号21FTP 443HTTPS22SSH1433SQLServer23Telnet1521Oracle25SMTP3306MySQL80HTTP33 查看详情

编译原理之扫描器

...sp;  编译原理实验课要求自己实现一个简单一些的扫描器。我是用c++实现的。具体的原理的话,完全是按照书上的算法实现的,也没有什么特别难懂的地方,不过想要写的比较完备的话还是有一定难度的。   Go-... 查看详情

lyscript实现应用层钩子扫描器

Capstone是一个轻量级的多平台、多架构的反汇编框架,该模块支持目前所有通用操作系统,反汇编架构几乎全部支持,实现应用层钩子扫描,我们需要得到程序内存文件的机器码以及磁盘中的机器码,并通过capstone这个第三方反... 查看详情

nmap--网络探测和安全审核工具

...的应用程序类型与版本信息,最后利用它还能侦测出操作系统的类型和版本。一、nmap基本功能与结构主要有如下四个基本功能:如果希望了解目标主机更多的信息,可以通过完全扫描的方式实现,nmap命令内置了“-A”选项,可... 查看详情

求扫描指定ip的端口的扫描器?

比如我用普通扫描器扫描出了开了3389端口的IP,我要在这些IP中扫描开了21的端口,有这种扫描器吗?有,阿D工具包,不过那个会被很多杀毒软件报告病毒(其实杀毒软件把黑客软件都认为是病毒),但是不会对电脑造成危害参考技术A... 查看详情