实例探究Python以并发方式编写高性能端口扫描器的方法


Posted in Python onJune 14, 2016

关于端口扫描器
端口扫描工具(Port Scanner)指用于探测服务器或主机开放端口情况的工具。常被计算机管理员用于确认安全策略,同时被攻击者用于识别目标主机上的可运作的网络服务。

端口扫描定义是客户端向一定范围的服务器端口发送对应请求,以此确认可使用的端口。虽然其本身并不是恶意的网络活动,但也是网络攻击者探测目标主机服务,以利用该服务的已知漏洞的重要手段。端口扫描的主要用途仍然只是确认远程机器某个服务的可用性。

扫描多个主机以获取特定的某个端口被称为端口清扫(Portsweep),以此获取特定的服务。例如,基于SQL服务的计算机蠕虫就会清扫大量主机的同一端口以在 1433 端口上建立TCP连接。

Python实现

端口扫描器原理很简单,无非就是操作socket,能connect就认定这个端口开放着。

import socket 
def scan(port): 
  s = socket.socket() 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
if __name__ == '__main__': 
  map(scan,range(1,65536))

这样一个最简单的端口扫描器出来了。
等等喂,半天都没反应,那是因为socket是阻塞的,每次连接要等很久才超时。
我们自己给它加上的超时。

s.settimeout(0.1)

再跑一遍,感觉快多了。

多线程版本

import socket 
import threading 
def scan(port): 
  s = socket.socket() 
  s.settimeout(0.1) 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
 
if __name__ == '__main__': 
  threads = [threading.Thread(target=scan, args=(i,)) for i in xrange(1,65536)] 
  map(lambda x:x.start(),threads)

运行一下,哇,好快,快到抛出错误了。thread.error: can't start new thread。
想一下,这个进程开启了65535个线程,有两种可能,一种是超过最大线程数了,一种是超过最大socket句柄数了。在linux可以通过ulimit来修改。
如果不修改最大限制,怎么用多线程不报错呢?
加个queue,变成生产者-消费者模式,开固定线程。

多线程+队列版本

import socket 
import threading 
from Queue import Queue 
def scan(port): 
  s = socket.socket() 
  s.settimeout(0.1) 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
 
def worker(): 
  while not q.empty(): 
    port = q.get() 
    try: 
      scan(port) 
    finally: 
      q.task_done() 
 
if __name__ == '__main__': 
  q = Queue() 
  map(q.put,xrange(1,65535)) 
  threads = [threading.Thread(target=worker) for i in xrange(500)] 
  map(lambda x:x.start(),threads) 
  q.join()

这里开500个线程,不停的从队列取任务来做。

multiprocessing+队列版本
总不能开65535个进程吧?还是用生产者消费者模式

import multiprocessing 
def scan(port): 
  s = socket.socket() 
  s.settimeout(0.1) 
  if s.connect_ex(('localhost', port)) == 0: 
    print port, 'open' 
  s.close() 
 
def worker(q): 
  while not q.empty(): 
    port = q.get() 
    try: 
      scan(port) 
    finally: 
      q.task_done() 
 
if __name__ == '__main__': 
  q = multiprocessing.JoinableQueue() 
  map(q.put,xrange(1,65535)) 
  jobs = [multiprocessing.Process(target=worker, args=(q,)) for i in xrange(100)] 
  map(lambda x:x.start(),jobs)

注意这里把队列作为一个参数传入到worker中去,因为是process safe的queue,不然会报错。
还有用的是JoinableQueue(),顾名思义就是可以join()的。

gevent的spawn版本

from gevent import monkey; monkey.patch_all(); 
import gevent 
import socket 
... 
if __name__ == '__main__': 
  threads = [gevent.spawn(scan, i) for i in xrange(1,65536)] 
  gevent.joinall(threads)

注意monkey patch必须在被patch的东西之前import,不然会Exception KeyError.比如不能先import threading,再monkey patch.

gevent的Pool版本

from gevent import monkey; monkey.patch_all(); 
import socket 
from gevent.pool import Pool 
... 
if __name__ == '__main__': 
  pool = Pool(500) 
  pool.map(scan,xrange(1,65536)) 
  pool.join()

concurrent.futures版本

import socket 
from Queue import Queue 
from concurrent.futures import ThreadPoolExecutor 
... 
if __name__ == '__main__': 
  q = Queue() 
  map(q.put,xrange(1,65536)) 
  with ThreadPoolExecutor(max_workers=500) as executor: 
    for i in range(500): 
      executor.submit(worker,q)
Python 相关文章推荐
Python实现求最大公约数及判断素数的方法
May 26 Python
基于使用paramiko执行远程linux主机命令(详解)
Oct 16 Python
用python 批量更改图像尺寸到统一大小的方法
Mar 31 Python
Python2和Python3之间的str处理方式导致乱码的讲解
Jan 03 Python
PyQt编程之如何在屏幕中央显示窗体的实例
Jun 18 Python
python Django的web开发实例(入门)
Jul 31 Python
Python中list循环遍历删除数据的正确方法
Sep 02 Python
使用opencv识别图像红色区域,并输出红色区域中心点坐标
Jun 02 Python
使用openCV去除文字中乱入的线条实例
Jun 02 Python
Python使用windows设置定时执行脚本
Nov 12 Python
Python字符串的15个基本操作(小结)
Feb 03 Python
python爬虫请求库httpx和parsel解析库的使用测评
May 10 Python
Python使用dis模块把Python反编译为字节码的用法详解
Jun 14 #Python
Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
Jun 14 #Python
Python的Flask框架中使用Flask-SQLAlchemy管理数据库的教程
Jun 14 #Python
全面了解Python的getattr(),setattr(),delattr(),hasattr()
Jun 14 #Python
浅谈python中的getattr函数 hasattr函数
Jun 14 #Python
深入解析Python中的线程同步方法
Jun 14 #Python
详解Python中的Descriptor描述符类
Jun 14 #Python
You might like
PHP Squid中可缓存的动态网页设计
2008/09/17 PHP
PHP使用strtotime获取上个月、下个月、本月的日期
2015/12/30 PHP
thinkphp在低版本Nginx 下支持PATHINFO的方法分享
2016/05/27 PHP
Laravel模型事件的实现原理详解
2018/03/14 PHP
简单的js分页脚本
2009/05/21 Javascript
JQuery中的ready函数冲突的解决方法
2010/05/17 Javascript
中国地区三级联动下拉菜单效果分析
2012/11/15 Javascript
window.open关于浏览器拦截问题分析及解决方法
2013/02/05 Javascript
jquery改变tr背景色的示例代码
2013/12/28 Javascript
jQuery中hover方法和toggle方法使用指南
2015/02/27 Javascript
简单纯js实现点击切换TAB标签实例
2015/08/23 Javascript
自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框
2015/12/12 Javascript
JavaScript数组去重的两种方法推荐
2016/04/05 Javascript
NodeJs中express框架的send()方法简介
2017/06/20 NodeJs
vue.js实现格式化时间并每秒更新显示功能示例
2018/07/07 Javascript
vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
2018/11/29 Javascript
jQuery zTree插件使用简单教程
2019/08/16 jQuery
js实现轮播图效果 z-index实现轮播图
2020/01/17 Javascript
vue单文件组件无法获取$refs的问题
2020/06/24 Javascript
python中bisect模块用法实例
2014/09/25 Python
Python全局变量操作详解
2015/04/14 Python
Python字符串处理之count()方法的使用
2015/05/18 Python
解决python中遇到字典里key值为None的情况,取不出来的问题
2018/10/17 Python
python将字符串list写入excel和txt的实例
2019/07/20 Python
django2.2安装错误最全的解决方案(小结)
2019/09/24 Python
python matplotlib.pyplot.plot()参数用法
2020/04/14 Python
k-means 聚类算法与Python实现代码
2020/06/01 Python
夜大毕业自我鉴定
2013/10/11 职场文书
小学英语课后反思
2014/04/26 职场文书
农村党支部书记党群众路线四风问题整改措施
2014/09/26 职场文书
房屋买卖协议书范本
2014/09/27 职场文书
热情服务标语
2014/10/07 职场文书
开平碉楼导游词
2015/02/06 职场文书
公司中层管理培训心得体会
2016/01/11 职场文书
2016年乡镇七一建党节活动总结
2016/04/05 职场文书
Java 通过手写分布式雪花SnowFlake生成ID方法详解
2022/04/07 Java/Android