mac/unix系统:C++实现一个端口扫描器
简易端口扫描器
在比较早以前,我用过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++实现一个端口扫描器的更多相关文章
- java编写一个端口扫描器
好久没写java了,学的时候,也没学习网络编程这一块,无意中看到了一本书,于是小小复习一下java,写个端口扫描器,玩玩吧,网上这种小公具有的是,就是自己无聊写着玩玩. 源代码如下: 共两个类,比较简 ...
- Golang 写一个端口扫描器
前话 最近痴迷于Golang这个新兴语言,因为它是强类型编译型语言,可以直接编译成三大平台的二进制执行文件,可以直接运行无需其他依赖环境.而且Golang独特的goroutine使得多线程任务执行如n ...
- 【技术分享】手把手教你使用PowerShell内置的端口扫描器
[技术分享]手把手教你使用PowerShell内置的端口扫描器 引言 想做端口扫描,NMAP是理想的选择,但是有时候NMAP并不可用.有的时候仅仅是想看一下某个端口是否开放.在这些情况下,PowerS ...
- MD5做为文件名。机器唯一码有电脑的CPU信息和MAC地址,这两个信息需要在linux或unix系统下才能获取吧。
可以采用机器(电脑)唯一码 + 上传IP + 当前时间戳 + GUID ( + 随机数),然后MD5做为文件名.机器唯一码有电脑的CPU信息和MAC地址,这两个信息需要在linux或unix系统下才能 ...
- Python与Hack之window下运行带参数的Python脚本,实现一个简单的端口扫描器
1.前提是:windows已经配置好Python的环境变量: 2.进入cmd命令行模式: **输入python命令,检测是否环境配置好:显示这样说明配置环境变量没问题 **用cd命令进入Python脚 ...
- 如何在unix系统中用别的用户运行一个程序?
1.问题的缘由 实际开发系统的时候,经常需要用别的用户运行一个程序.比如,有些系统为保证系统安全,不允许使用root来运行.这里,我们总结了unix系统下如何解决这个问题的一些方法.同时,我们还讨论如 ...
- 用Python实现一个端口扫描,只需简单几步就好
一.常见端口扫描的原理 0.秘密扫描 秘密扫描是一种不被审计工具所检测的扫描技术. 它通常用于在通过普通的防火墙或路由器的筛选(filtering)时隐藏自己. 秘密扫描能躲避IDS.防火墙.包过滤器 ...
- UNIX系统上的抓包工具tcpdump常用命令说明
tcpdump 介绍 tcpdump采用命令行方式对接口的数据包进行筛选抓取,其丰富特性表现在灵活的表达式上. 不带任何选项的tcpdump,默认会抓取第一个网络接口,且只有将tcpdump进程终止才 ...
- Nginx实现多个站点使用一个端口(配置Nginx的虚拟主机)
Nginx 是一个轻量级高性能的 Web 服务器, 并发处理能力强, 消耗资源小, 无论是静态服务器还是网站, Nginx 表现更加出色, 作为 Apache 的补充和替代使用率越来越高,目前很多大型 ...
随机推荐
- ubuntu12.04下同步cm10源码(个人记录,当作笔记)
环境:AMD N850,4G,ubuntu12.04 x64, 老规矩,先是各种依赖: sudo apt-get install git-core gnupg flex bison python ra ...
- 转:AM335x启动流程(BootRom->MLO->Uboot)
http://blog.chinaunix.net/uid-28458801-id-3486399.html 参考文件: 1,AM335x ARM Cortex-A8 Microprocessors ...
- Servlet 中的out.print()与out.writer()的区别
PrintWriter out = response.getWriter(); out.print(obj)其源码如下: public void print(Object obj) { write(S ...
- hdu 1385 Minimum Transport Cost
http://acm.hdu.edu.cn/showproblem.php?pid=1385 #include <cstdio> #include <cstring> #inc ...
- Keil C调试经验
我们使用Keil C调试某系统时积累的一些经验: 1.由于Keil C对中文支持不太好,因而会出现显示的光标与光标实际所在不一致的现象,这会对修改中文注释造成影响.在Windows2000下面 ...
- js深入研究之函数内的函数
第一种 function foo() { ; function bar() { a *= ; } bar(); return a; } 第二种 function foo() { ; function ...
- back_inserter的用法
1,代码如下: #include<iostream> #include<list> #include<algorithm> #include<iterator ...
- 页面onclick()中传值问题
html中onclick()里面传变量到javascript中的问题,小小的记录下: 传变量的话一定要加 '' <span onclick="sellGoods('${session ...
- <php>json小结
1.页面中如果即用到jquery包,又用到js文件,要先写jquery包,再加载js文件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...
- SQL中@@ROWCOUNT函数
返回受上一语句影响的行数.如果行数大于 20 亿,请使用 ROWCOUNT_BIG. 语法 @@ROWCOUNT 返回类型 int 注释 Transact-SQL 语句可以通过下列方 ...