实例探究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 相关文章推荐
如何利用Fabric自动化你的任务
Oct 20 Python
Python输出各行命令详解
Feb 01 Python
TensorFlow入门使用 tf.train.Saver()保存模型
Apr 24 Python
pytorch 数据集图片显示方法
Jul 26 Python
Pycharm新手教程(只需要看这篇就够了)
Jun 18 Python
Python3多线程版TCP端口扫描器
Aug 31 Python
使用Python对Dicom文件进行读取与写入的实现
Apr 20 Python
keras中的History对象用法
Jun 19 Python
利用Vscode进行Python开发环境配置的步骤
Jun 22 Python
Python中的With语句的使用及原理
Jul 29 Python
calendar在python3时间中常用函数举例详解
Nov 18 Python
python 中 .py文件 转 .pyd文件的操作
Mar 04 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中数组定义的几种方法
2013/09/01 PHP
PHP+jQuery 注册模块开发详解
2014/10/14 PHP
codeigniter显示所有脚本执行时间的方法
2015/03/21 PHP
zen cart实现订单中增加paypal中预留电话的方法
2016/07/12 PHP
php实现的网页版剪刀石头布游戏示例
2016/11/25 PHP
php实现数组纵向转横向并过滤重复值的方法分析
2017/05/29 PHP
Laravel5.4框架中视图共享数据的方法详解
2019/09/05 PHP
js 处理URL实用技巧
2010/11/23 Javascript
jQuery EasyUI API 中文文档 - TreeGrid 树表格使用介绍
2011/11/21 Javascript
JavaScript中的变量作用域介绍
2014/12/31 Javascript
JavaScript实现添加、查找、删除元素
2015/07/02 Javascript
JS实现状态栏跑马灯文字效果代码
2015/10/24 Javascript
深入解析JavaScript中函数的Currying柯里化
2016/03/19 Javascript
JS中传递参数的几种不同方法比较
2017/01/20 Javascript
jQuery获取所有父级元素及同级元素及子元素的方法(推荐)
2018/01/21 jQuery
百度地图去掉marker覆盖物或者去掉maker的label文字方法
2018/01/26 Javascript
vue项目添加多页面配置的步骤详解
2019/05/22 Javascript
在Vue中使用Select选择器拼接label的操作
2020/10/22 Javascript
原生JavaScript实现留言板
2021/01/10 Javascript
python连接MySQL、MongoDB、Redis、memcache等数据库的方法
2013/11/15 Python
python共享引用(多个变量引用)示例代码
2013/12/04 Python
Python标准库之sqlite3使用实例
2014/11/25 Python
Python实现大文件排序的方法
2015/07/10 Python
简单谈谈Python中的json与pickle
2017/07/19 Python
Python爬取数据并写入MySQL数据库的实例
2018/06/21 Python
Python实现的IP端口扫描工具类示例
2019/02/15 Python
Python函数的参数常见分类与用法实例详解
2019/03/30 Python
详解Python中pandas的安装操作说明(傻瓜版)
2019/04/08 Python
解决Python正则表达式匹配反斜杠''\''问题
2019/07/17 Python
python中property属性的介绍及其应用详解
2019/08/29 Python
详解python如何引用包package
2020/06/07 Python
CSS3的文字阴影—text-shadow的使用方法
2012/12/25 HTML / CSS
学徒工职责
2014/03/06 职场文书
怎样写离婚协议书
2014/09/10 职场文书
结婚仪式主持词
2015/06/29 职场文书
Win11开始菜单添加休眠选项
2022/04/19 数码科技