zoukankan      html  css  js  c++  java
  • 构建一个高速的扫描器

    0x00 前言

    近期工作须要做大规模的扫描,须要自己依据指纹写一个扫描引擎,其中碰到了无数的坑。最后勉强算的过去,特地写写博客总结一下各种坑和思路。

    0x01 任务队列+多线程模型

    一開始自然而然想到的就是这个扫描模型,利用Python中的同步队列结合多线程发包、收包、写操作。

    大致模型例如以下:

    class Worker(Thread):
        def __init__(self, queue):
            self.queue = queue
        def run(self):
            while True:
                ip = self.queue.get()
                sock.send()
                recv = sock.recv(buf_size)
                ....
                do sth....
                ....
                self.queue.task_done()
    def main():
        ...
        worker = Worker(queue)
        work.deamon = True
        work.start()
        queue.join() # 队列同步
        ...

    然而事实证明效率真的很低。仅仅能发出可怜的几百pps。4M带宽都占不满。各位假设想做大规模扫描。我说的大规模扫描是指上亿的IP扫描,就别考虑这样的方法了。


    总结一下原因,主要是线程太多,切换时间很耗费CPU,而且因为GIL的存在。效率可想而知。

    其次就是因为IP太多,不能所有放入队列中,必须在main函数中做个缓冲区,然后放入同步队列,频繁queue.join()的结果是状态为done的线程在等待慢线程的情景增多,效率自然就不高了,因为以上原因导致的效率低下,网络的IO延迟根本不算什么。


    后来又想用celery做异步的任务。分离网络IO和本地IO,把线程中本地耗时长的IO操作分离出来。放到celery其中异步运行。这样能够加快发包收包线程的速度。结果发现更慢,建议不要用celery处理很耗时的后台任务,感觉好坑….

    0x01 基于无状态扫描模型

    简单来说,就是不关心数据包的交互状态,这里不存在同步队列的情况。而是开辟两个线程就可以。一个线程负责send。还有一个线程负责recv。因为我业务上须要发送UDP协议为基础探測包。
    这样做的优点是不同在一个线程中同一时候发送和接收报文,因为不关心网络通信的交互过程,因此。能够让发包线程和收包线程分别满负荷工作,不存在线程相互等待的情况,也基本不存在同步的情况,因此效率大大提高。模型大致例如以下:

    class Sender(Thread):
        def __init__(self, sock):
            self.sock = sock
        def run(self):
            for ip in list:
                pack = package_create()
                self.sock.sendto(pack, server)
                ....
                do sth ...
                ....
    class Receiver(Thread):
        def __init__(self, sock):
            self.sock = sock
        def run(self):
            while True:
                recv = self.sock.recvfrom(buf_size)
                handle_recv_package(recv)
                ....
                do sth ...
                ....
    def main():
        sender = Sender(sock)
        recver = Receiver(sock)
        ....
        do sth ...
        ....
        sock.close()

    比較麻烦的是。这样的模式须要自己对报文进行组装、发送、解析接收的报文等工作。回报是,效率大大提高,轻松3k~4kpps(4M/1 CPU/2G mem),带宽基本占满。

    0x03 性能提升

    主要提升的空间在下面方面:

    • 加快报文组装的速度
    • 加快接收报文的组装速度
    • 利用缓冲区进行批量IO。一定程度降低IO操作
    • 处理好socket error,建议使用事件模型,设置任务回滚功能,遇到socket error就结束线程,然后在主线程中再新建socket并启动线程开工

    优化做好了。现有的带宽占满了,这时候,瓶颈就是带宽,就要换个更好的带宽。添加速度。
    另外,在測试的过程中。鄙人发现网卡发送UDP包的速度比TCP慢多了,用了两台机器測试,结论一致。

  • 相关阅读:
    android ContentObserver
    3 个简单、优秀的 Linux 网络监视器
    使用 sar 和 kSar 来发现 Linux 性能瓶颈
    4 个拥有绝佳命令行界面的终端程序
    4 个用于构建优秀的命令行用户界面的 Python 库
    理解 Linux 的平均负载和性能监控
    安装matplotlib
    面向系统管理员的网络管理指南
    使用 Nmon 监控 Linux 的系统性能
    linux smem 查看各进程使用memory情况
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7295154.html
Copyright © 2011-2022 走看看