实例探究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 序列化 pickle/cPickle模块使用介绍
Nov 30 Python
Python文件和流(实例讲解)
Sep 12 Python
python方法生成txt标签文件的实例代码
May 10 Python
pyqt5利用pyqtDesigner实现登录界面
Mar 28 Python
python实现代码统计程序
Sep 19 Python
基于Python实现签到脚本过程解析
Oct 25 Python
浅谈tensorflow模型保存为pb的各种姿势
May 25 Python
python 写函数在一定条件下需要调用自身时的写法说明
Jun 01 Python
Python3爬虫带上cookie的实例代码
Jul 28 Python
python中@property的作用和getter setter的解释
Dec 22 Python
python实现简单聊天功能
Jul 07 Python
利用Matlab绘制各类特殊图形的实例代码
Jul 16 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数据饼图效果实现代码
2011/11/23 PHP
PHP让网站移动访问更加友好方法
2019/02/14 PHP
Laravel如何实现适合Api的异常处理响应格式
2020/06/14 PHP
javascript multibox 全选
2009/03/22 Javascript
基于jQuery的消息提示插件 DivAlert之旅(二)
2010/04/01 Javascript
jQuery仿Excel表格编辑功能的实现代码
2013/05/01 Javascript
关于js数组去重的问题小结
2014/01/24 Javascript
jQuery-1.9.1源码分析系列(十)事件系统之事件包装
2015/11/20 Javascript
轻松学习jQuery插件EasyUI EasyUI表单验证
2015/12/01 Javascript
jQuery 常用代码集锦(必看篇)
2016/05/16 Javascript
BootStrap 超链接变按钮的实现方法
2016/09/25 Javascript
微信小程序 简单DEMO布局,逻辑,样式的练习
2016/11/30 Javascript
babel基本使用详解
2017/02/17 Javascript
Vue用v-for给src属性赋值的方法
2018/03/03 Javascript
Vue实现购物车的全选、单选、显示商品价格代码实例
2019/05/06 Javascript
微信小程序开发实现消息推送
2020/11/18 Javascript
javaScript把其它类型转换为Number类型
2019/10/13 Javascript
vue3.0生命周期的示例代码
2020/09/24 Javascript
vue动态合并单元格并添加小计合计功能示例
2020/11/26 Vue.js
[58:25]VP vs RNG 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
Python中的多行注释文档编写风格汇总
2016/06/16 Python
Python读写Json涉及到中文的处理方法
2016/09/12 Python
python中numpy包使用教程之数组和相关操作详解
2017/07/30 Python
Python基于滑动平均思想实现缺失数据填充的方法
2019/02/21 Python
Python从文件中读取数据的方法步骤
2020/11/18 Python
Python爬虫入门教程02之笔趣阁小说爬取
2021/01/24 Python
一款恶搞头像特效的制作过程 利用css3和jquery
2014/11/21 HTML / CSS
Clarks鞋法国官方网站:英国其乐鞋品牌
2018/02/11 全球购物
什么是继承
2013/12/07 面试题
区域总监的岗位职责
2013/11/21 职场文书
保护动物倡议书
2014/04/15 职场文书
爱国演讲稿400字
2014/05/07 职场文书
干部竞争上岗演讲稿
2014/09/11 职场文书
学习计划书怎么写
2014/09/15 职场文书
个人融资协议书范本两则
2014/10/15 职场文书
MySQL 1130异常,无法远程登录解决方案详解
2021/08/23 MySQL