实例探究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中的闭包详细介绍和实例
Nov 21 Python
Python中用format函数格式化字符串的用法
Apr 08 Python
Python中的defaultdict与__missing__()使用介绍
Feb 03 Python
Python内置模块ConfigParser实现配置读写功能的方法
Feb 12 Python
Php多进程实现代码
May 07 Python
python矩阵的转置和逆转实例
Dec 12 Python
详解Python中pandas的安装操作说明(傻瓜版)
Apr 08 Python
Python使用LDAP做用户认证的方法
Jun 20 Python
Python hashlib加密模块常用方法解析
Dec 18 Python
Python openpyxl模块原理及用法解析
Jan 19 Python
python实现打砖块游戏
Feb 25 Python
Python bytes string相互转换过程解析
Mar 05 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 动态多文件上传
2009/01/18 PHP
解析PHP中常见的mongodb查询操作
2013/06/20 PHP
php的SimpleXML方法读写XML接口文件实例解析
2014/06/16 PHP
Cygwin中安装PHP方法步骤
2015/07/04 PHP
PHP中常见的缓存技术实例分析
2015/09/23 PHP
PHP中FTP相关函数小结
2016/07/15 PHP
使用vs code编辑调试php配置的方法
2019/01/29 PHP
15条JavaScript最佳实践小结
2013/08/09 Javascript
浅谈JavaScript函数参数的可修改性问题
2013/12/05 Javascript
jquery自动切换tabs选项卡的具体实现
2013/12/24 Javascript
js父窗口关闭时子窗口随之关闭完美解决方案
2014/04/29 Javascript
一个奇葩的最短的 IE 版本判断JS脚本
2014/05/28 Javascript
字段太多jquey快速清空表单内容方法
2014/08/21 Javascript
jQuery实现首页图片淡入淡出效果的方法
2015/06/10 Javascript
利用C/C++编写node.js原生模块的方法教程
2017/07/07 Javascript
JS中关于正则的巧妙操作
2017/08/31 Javascript
JavaScript中工厂函数与构造函数示例详解
2019/05/06 Javascript
微信小程序 button样式设置为图片的方法
2020/06/19 Javascript
封装 axios+promise通用请求函数操作
2020/08/11 Javascript
python基础教程之实现石头剪刀布游戏示例
2014/02/11 Python
python抓取网站的图片并下载到本地的方法
2018/05/22 Python
Python一句代码实现找出所有水仙花数的方法
2018/11/13 Python
Python XML转Json之XML2Dict的使用方法
2019/01/15 Python
OpenCV+Python--RGB转HSI的实现
2019/11/27 Python
基于 HTML5 的 WebGL 3D 版俄罗斯方块的示例代码
2018/05/28 HTML / CSS
美国市场上最实惠的送餐服务:Dinnerly
2018/03/18 全球购物
彪马土耳其官网:PUMA土耳其
2019/07/14 全球购物
数控技术学生的自我评价
2014/02/15 职场文书
聘任书的写作格式及范文
2014/03/29 职场文书
2014年新农村建设工作总结
2014/12/01 职场文书
2015年建党94周年演讲稿
2015/03/19 职场文书
寻衅滋事罪辩护词
2015/05/21 职场文书
Redis读写分离搭建的完整步骤
2021/09/14 Redis
Python办公自动化解决world文件批量转换
2021/09/15 Python
Python安装及建立虚拟环境的完整步骤
2022/06/25 Servers
详解flex:1什么意思
2022/07/23 HTML / CSS