简易端口扫描器

  在比较早以前,我用过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(!=argc)
{
msg();
return ;
}
ip=argv[];
startport=atoi(argv[]);
endport=atoi(argv[]);
if(startport< || endport> || endport<startport)
{
printf("端口范围出错/n");
return ;
}
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,);
to.sin_port=htons(i);
if(connect(sockfd,(struct sockaddr *)&to,sizeof(struct sockaddr)) == ) {
printf("%s %d\n",ip,i);
close(sockfd);
};
}
end=clock();
costtime=(float)(end-start)/CLOCKS_PER_SEC;
printf("用时:%f秒\n",costtime);
return ;
}

  亲测可行:

  以上的代码只能检测固定的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, );
//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) ) == ){
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 ( == argc ){
ip = argv[];
startport = atoi( argv[] );
endport = atoi( argv[] );
if ( startport < || endport > || endport < startport ){
printf( "端口范围出错/n" );
return();
}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();
}else if ( == argc ){
ip = argv[];
endip = argv[];
startport = atoi( argv[] );
endport = atoi( argv[] );
char *tempip;
if ( startport < || endport > || endport < startport ){
printf( "端口范围出错/n" );
return();
}else{
/* 循环ip地址 */
start = clock();
char ipval[];
for ( int j = ; 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();
}
msg();
return();
}

  局域网网段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=;
int nleft = len;
unsigned short *w = addr;
unsigned short answer = ;
/* 把ICMP报头二进制数据以2字节为单位累加起来 */
while(nleft > ){
sum += *w++;
nleft -= ;
}
/*
* 若ICMP报头为奇数个字节,会剩下最后一字节。
* 把最后一个字节视为一个2字节数据的高字节,
* 这2字节数据的低字节为0,继续累加
*/
if(nleft == ){
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer; /* 这里将 answer 转换成 int 整数 */
}
sum = (sum >> ) + (sum & 0xffff); /* 高位低位相加 */
sum += (sum >> ); /* 上一步溢出时,将溢出位也加到sum中 */
answer = ~sum; /* 注意类型转换,现在的校验和为16位 */
return answer;
}
int livetest(char* ip) { char sendpacket[PACKET_SIZE]; /* 发送的数据包 */
char recvpacket[PACKET_SIZE]; /* 接收的数据包 */
pid_t pid;
int datalen = ; /* icmp数据包中数据的长度 */
struct protoent *protocol;
protocol = getprotobyname("icmp");
int sockfd;
int size = *;
if((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < ) {
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 = ; /* icmp的编码 */
icmp->icmp_cksum = ; /* icmp的校验和 */
icmp->icmp_seq = ; /* icmp的顺序号 */
icmp->icmp_id = pid; /* icmp的标志符 */
packsize = + 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, , (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < ){
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 = {,};
fd_set set;
FD_ZERO(&set);
FD_SET(sockfd, &set); //read , write;
int retval = select(sockfd+, &set, NULL, NULL, &timeo);
if(retval == -) {
printf("select error\n");
return ;
}else if(retval == ) {
//printf("timeout\n");
return ;
}else{
if( FD_ISSET(sockfd, &set) ){
//printf("host is live\n");
return ;
}
return ;
}
// 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/;
tval.tv_usec=(secs*)%;
select(,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, );
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= {,};
//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+ , &set, &writeSet, NULL, &timeo); getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len );
if(error == )
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 ( == argc ){
ip = argv[];
startport = atoi( argv[] );
endport = atoi( argv[] );
if ( startport < || endport > || endport < startport ){
printf( "端口范围出错/n" );
return();
}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();
}else if ( == argc ){
ip = argv[];
startip = atoi(argv[]);
endip = atoi(argv[]);
startport = atoi( argv[] );
endport = atoi( argv[] );
char *tempip;
if ( startport < || endport > || endport < startport ){
printf( "端口范围出错/n" );
return();
}else{
/* 循环ip地址 */
start = clock();
char ipval[];
for ( int j = startip; j <= endip ; j++ ){
sprintf( ipval, "%s%d", ip, j );
printf( "IP:%s\n", ipval );
if(livetest(ipval) == ){
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();
}
}
}
end = clock();
costtime = (float) (end - start) / CLOCKS_PER_SEC;
printf( "用时:%f秒\n", costtime );
};
//while(1){}
/* 循环端口 */
return();
}
msg();
return();
}

  使用方式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 == )
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

mac/unix系统:C++实现一个端口扫描器的更多相关文章

  1. java编写一个端口扫描器

    好久没写java了,学的时候,也没学习网络编程这一块,无意中看到了一本书,于是小小复习一下java,写个端口扫描器,玩玩吧,网上这种小公具有的是,就是自己无聊写着玩玩. 源代码如下: 共两个类,比较简 ...

  2. Golang 写一个端口扫描器

    前话 最近痴迷于Golang这个新兴语言,因为它是强类型编译型语言,可以直接编译成三大平台的二进制执行文件,可以直接运行无需其他依赖环境.而且Golang独特的goroutine使得多线程任务执行如n ...

  3. 【技术分享】手把手教你使用PowerShell内置的端口扫描器

    [技术分享]手把手教你使用PowerShell内置的端口扫描器 引言 想做端口扫描,NMAP是理想的选择,但是有时候NMAP并不可用.有的时候仅仅是想看一下某个端口是否开放.在这些情况下,PowerS ...

  4. MD5做为文件名。机器唯一码有电脑的CPU信息和MAC地址,这两个信息需要在linux或unix系统下才能获取吧。

    可以采用机器(电脑)唯一码 + 上传IP + 当前时间戳 + GUID ( + 随机数),然后MD5做为文件名.机器唯一码有电脑的CPU信息和MAC地址,这两个信息需要在linux或unix系统下才能 ...

  5. Python与Hack之window下运行带参数的Python脚本,实现一个简单的端口扫描器

    1.前提是:windows已经配置好Python的环境变量: 2.进入cmd命令行模式: **输入python命令,检测是否环境配置好:显示这样说明配置环境变量没问题 **用cd命令进入Python脚 ...

  6. 如何在unix系统中用别的用户运行一个程序?

    1.问题的缘由 实际开发系统的时候,经常需要用别的用户运行一个程序.比如,有些系统为保证系统安全,不允许使用root来运行.这里,我们总结了unix系统下如何解决这个问题的一些方法.同时,我们还讨论如 ...

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

    一.常见端口扫描的原理 0.秘密扫描 秘密扫描是一种不被审计工具所检测的扫描技术. 它通常用于在通过普通的防火墙或路由器的筛选(filtering)时隐藏自己. 秘密扫描能躲避IDS.防火墙.包过滤器 ...

  8. UNIX系统上的抓包工具tcpdump常用命令说明

    tcpdump 介绍 tcpdump采用命令行方式对接口的数据包进行筛选抓取,其丰富特性表现在灵活的表达式上. 不带任何选项的tcpdump,默认会抓取第一个网络接口,且只有将tcpdump进程终止才 ...

  9. Nginx实现多个站点使用一个端口(配置Nginx的虚拟主机)

    Nginx 是一个轻量级高性能的 Web 服务器, 并发处理能力强, 消耗资源小, 无论是静态服务器还是网站, Nginx 表现更加出色, 作为 Apache 的补充和替代使用率越来越高,目前很多大型 ...

随机推荐

  1. 安装notepad++之后怎样在鼠标右键上加上Edit with notepad++

    在鼠标右键上加入使用notepad++编辑 我们在安装完notepad++文本编辑器之后,在一个文本文件上右键有时候并没有出现"使用notepad++编辑的选项",我们可以通过简单 ...

  2. subTree

    struct Tree() { int val; Tree *left, *right; Tree(int a): val(a), left(NULL), right(NULL){} } bool h ...

  3. struct2(二) struct2的hello world 程序

    在struct2 的web应用程序中,当你点击一个超链接或者提交一个HTML页面的时候,并不是直接的转向一个另一个的页面,而是转到你提供的一个Java 类.这个过程被称为一个action,一个acti ...

  4. hdu3849-By Recognizing These Guys, We Find Social Networks Useful:双连通分量

    By Recognizing These Guys, We Find Social Networks Useful Time Limit: 2000/1000 MS (Java/Others)     ...

  5. Codeforces Round #327 (Div. 1) D. Top Secret Task

    D. Top Secret Task time limit per test 3 seconds memory limit per test 256 megabytes input standard ...

  6. Git本地版本控制备忘

    首先git是一个版本控制工具,类似于SVN 笔记包括两部分,git本地版本控制和git远程协助 一.Git本地版本控制 以git windows版本msysgit为例,下载地址http://msysg ...

  7. Go--包引用介绍

    最近在学习Go编程,本文简单的叙述如何在Go编程中使用包(包管理). 和其他大多数语言一样,Go也存在包,并使用package关键字定义一个包.首先介绍在程序中如何引入包,引入包有以下几种方式: 1. ...

  8. 与Jquery Mobile的第一次亲密接触

    Jquery Mobile闻名已久,今天终于有亲密接触的机会. 通过动手写的demo,对它有了一个基本的认识: 自带的UI组件用起来简洁,方便:对旧版本的浏览器或移动设备能做到很好的优雅降级,而不影响 ...

  9. [WebStrom] Cannot detect file change to trigger webpack re-compile

    Working with editors/IDEs supporting “safe write” Note that many editors support “safe write” featur ...

  10. single-row function和muti-row function

    1.single-row function 指一行数据输入,返回一个值的函数. 常见的有 字符函数(如:substr) 日期函数(如:months_between) 数字函数(如:MOD) 转换函数( ...