告别脚本小子【编写端口扫描工具】
前言
Windows系统默认开放了很多端口,通常这些端口意味着该主机运行着大家都知道的服务,比如TCP端口21-FTP服务,TCP端口80-HTTP服务,有些服务就有可能存在公开的漏洞,因此可以说主机上每一个开放的端口都可能成为一条入侵的捷径。当然,网上存在很多端口扫描工具,但是我们总不能只知道使用别人的工具,一是这些工具别人编写的时候有没有加入后门,二是如果只会用别人的工具,最终也只能是一个脚本小子,所以我们自己来编写一款实用的端口扫描工具。
一、基础知识
1.1、线程
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
1.2、多线程
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
1.3、利用Windows API创建多线程程序
这里我们先介绍几个函数
1.3.1、CreateThread()函数
用于创建一个新的线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。
第二个参数是用于新线程的初始堆栈大小,默认值为0。在任何情况下,Windows根据需要动态延长堆栈的大小。
第三个参数是指向线程函数的地址。函数名称没有限制,但是必须以下列形式声明:
DWORD WINAPI ThreadProc (PVOID pParam) ;
第四个参数为传递给ThreadProc的参数。这样主线程和从属线程就可以共享数据。
第五个参数通常为0,但当建立的线程不马上执行时为旗标CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。
第六个参数是指向一个接受该线程ID值的指针。
二、编写实战
在常见的端口扫描方式中,完整端口扫描是最简单的情况,无需用到任何协议,我们只需要调用connect()函数连接服务器的每个端口,如果连接端口成功则说明该端口开放,否则就是关闭。
该技术最多的特点就是简单,而且不需要任何权限。
代码如下:
#include <stdio.h> #include <WinSock2.h> #pragma comment(lib,"ws2_32") #define START 80 //起始端口 #define END 1024 //终止端口 int main(int argc,char *argv[]) { int i; WSADATA ws; //ws用来存储系统传回的关于WINSOCK的资料 SOCKET sockfd; struct sockaddr_in their_addr; if (argc != 2 ) { printf("使用方法:scan.exe <ip>"); } WSAStartup(MAKEWORD(2,2),&ws); their_addr.sin_family = AF_INET; //设置协议簇,AF_INET表示TCP/IP协议 their_addr.sin_addr.S_un.S_addr = inet_addr(argv[1]); //根据命令行参数确定扫描IP for (i=START;i<=END;i++) //循环建立socket后连接 { sockfd = socket(AF_INET,SOCK_STREAM,0); their_addr.sin_port = htons(i); printf("[+]正在扫描端口 %d \n",i); if (connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr))==SOCKET_ERROR) { continue; //连接失败进行下一个端口扫描 } printf("\n\t [+]端口 %d 开放! \n\n",i); } closesocket(sockfd); WSACleanup(); return 0; }
这里我们就可以拿i春秋bbs.ichunqiu.com为例子来扫描一下。
那么大家就可以发现,扫描的速度非常慢啊,这样肯定不行的,所以这里我们就要运用多线程技术了。
当然我们也可以界面化
这里我们使用的是c#简单来编写了一下界面版的端口扫描工具,原理一样,不做介绍
Python
下面我就是用python来编写端口扫描,一步一步带大家进入编程的世界。
首先我们来个基础的框架
def scan(): print 'start scan' if __name__ == '__main__': scan()
python从命令行读取参数
我们需要用到模块sys
def scan(ip,port): #扫描函数 print '[+]The port is:' + str(port) if __name__ == '__main__': ip = sys.argv[1] #读取命令行的参数 print 'start scan' print 'scan IP is %s' % ip for i in range(1,100): scan(ip,i) #调用scan函数
大家看是不是很简单
怎么实现扫描,和上诉原理一样,这样我们调用内置的socket模块
def scan(ip,port): print '[+]Scan port is:' + str(port) tcpscan = socket(AF_INET,SOCK_STREAM) addr = (ip,port) try: tcpscan.connect(addr) print " [-]The port " + str(port) + " open \n" tcpscan.close() except: pass if __name__ == '__main__': ip = sys.argv[1] print 'start scan' print 'scan IP is %s' % ip for i in range(80,100): scan(ip,i)
有没有很简单,大家是不是在纠结速度啦。多线程技术哦,下篇文章再来讲解。
感谢您的阅读,如果您学到了,请点赞(码字不易)!欢迎热心园友补充!
作者:zusheng 文章首链:http://bbs.ichunqiu.com/thread-7051-1-2.html
最后祝大家七夕快乐!