用Python做一个安全攻防工具:端口嗅探器(2)

6 篇文章 1 订阅
3 篇文章 0 订阅

传送门

本系列原创博文传送门:

用Python做一个安全攻防工具:端口嗅探器(1)

用Python做一个安全攻防工具:端口嗅探器(2)

用Python做一个安全攻防工具:端口嗅探器(3)

用Python做一个安全攻防工具:端口嗅探器(4)

用Python做一个安全攻防工具:端口嗅探器(5)

用Python做一个安全攻防工具:端口嗅探器(6)

用Python做一个安全攻防工具:端口嗅探器(7)

用Python做一个安全攻防工具:端口嗅探器(8)

用Python做一个安全攻防工具:端口嗅探器(9)

 

正文

这一篇主要实现端口的自动遍历。

根据端口的百科资料,一个ip的可用端口数是 2^16 = 65536 。

那么遍历一个ip的端口,就需要调用那么多次端口访问。因为经常要调用端口访问的代码块,我们先把这部分代码提取成一个函数。

# coding=utf-8
import socket


def port_search(ip, port):
    address = (ip, port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    err = client.connect_ex(address)
    if err == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
        print(client.recv(1024))
    client.close()  # 关闭连接
    print(err)


port_search(ip='110.242.68.4', port=22)

考虑到端口数量众多,我们看一下运行一次的时间,引入time模块:

# coding=utf-8
import socket
import time


def port_search(ip, port):
    address = (ip, port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    err = client.connect_ex(address)
    if err == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
        print(client.recv(1024))
    client.close()  # 关闭连接
    print(err)


start_time = time.time()
port_search(ip='110.242.68.4', port=22)
print('消耗时间为: ', time.time()-start_time)

试了下,不开放的端口耗时:

将port值修改为80:

发现每一个端口都耗时很久,假设按20秒计算,遍历完所有端口耗时=65536*20秒……好像有点久!

为了加快遍历速度,我们引入线程threading模块(教程见此),线程的意义就是并发,而非串行。

第一阶段,加入端口遍历后,对于port_search函数通过实现线程的创建和运行来调用。

# coding=utf-8
import socket
import threading


def port_search(_ip, _port):
    address = (_ip, _port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    err = client.connect_ex(address)
    if err == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
        print(client.recv(1024))
    client.close()  # 关闭连接
    print(err)


ip = '110.242.68.4'
for i in range(65536):  # 0~65535
    thread = threading.Thread(target=port_search, args=(ip, i))
    thread.start()

这个程序还有问题,若是运行完,有很多个输出,输出很杂乱,需要将信息归整,修改之后如下:

import socket
import threading


global_list = []  # 收集开着的端口信息
global_err_list = []  # 收集关着的端口信息


def port_search(_ip, _port):
    address = (_ip, _port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    code = client.connect_ex(address)
    if code == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
        global_list.append([_ip, _port, code, client.recv(1024)])
    else:
        global_err_list.append([_ip, _port, code])  # 此处不能调用 recv api,会抛出异常
    client.close()  # 关闭连接


ip = '110.242.68.4'
for i in range(65536):  # 0~65535
    thread = threading.Thread(target=port_search, args=(ip, i))
    thread.start()

print(global_list)
print(global_err_list)

到这里之后,还有两个潜在的问题:

1、这两个全局列表被并发访问,需要加一个锁;

2、打印的时候,有些线程没有结束,所以会错过一些信息,需要使用join阻塞来等待所有线程结束。

再次修改如下:

# coding=utf-8
import socket
import threading


global_list = []  # 收集开着的端口信息
global_err_list = []  # 收集关着的端口信息
global_thread_list = []  # 线程收集列表
lock = threading.Lock()  # 创建一个锁


def port_search(_ip, _port):
    address = (_ip, _port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    code = client.connect_ex(address)
    lock.acquire()
    if code == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
        global_list.append([_ip, _port, code, client.recv(1024)])
    else:
        global_err_list.append([_ip, _port, code])  # 此处不能调用 recv api,会抛出异常
    lock.release()
    client.close()  # 关闭连接


ip = '110.242.68.4'
for i in range(65536):  # 0~65535
    thread = threading.Thread(target=port_search, args=(ip, i))
    global_thread_list.append(thread)
    thread.start()

for t in global_thread_list:  # 遍历,等待所有进程运行完毕
    t.join()

print(global_list)
print(global_err_list)

走读代码时发现有个风险点,如果在加锁和解锁之前的代码段抛出异常,一个线程崩溃,会导致锁无法解开,修改如下:

# coding=utf-8
import socket
import threading


global_list = []  # 收集开着的端口信息
global_err_list = []  # 收集关着的端口信息
global_thread_list = []  # 线程收集列表
lock = threading.Lock()  # 创建一个锁


def port_search(_ip, _port):
    address = (_ip, _port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    code = client.connect_ex(address)
    lock.acquire()
    try:
        if code == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
            global_list.append([_ip, _port, code, client.recv(1024)])
        else:
            global_err_list.append([_ip, _port, code])  # 此处不能调用 recv api,会抛出异常
    except BaseException:
        print('进行列表操作时报错')
    lock.release()
    client.close()  # 关闭连接


ip = '110.242.68.4'
for i in range(65536):  # 0~65535
    thread = threading.Thread(target=port_search, args=(ip, i))
    global_thread_list.append(thread)
    thread.start()

for t in global_thread_list:  # 遍历,等待所有进程运行完毕
    t.join()

print(global_list)
print(global_err_list)

初步试运行了一下,卡的不行。加一个最大线程限制:

# coding=utf-8
import socket
import threading


global_list = []  # 收集开着的端口信息
global_err_list = []  # 收集关着的端口信息
global_thread_list = []  # 线程收集列表
lock = threading.Lock()  # 创建一个锁
sem = threading.Semaphore(500)  # 并发500


def port_search(_ip, _port):
    address = (_ip, _port)  # 地址必须是一个元祖,第一个是str,第二个是int
    client = socket.socket()  # 创建套接字
    code = client.connect_ex(address)
    lock.acquire()  # 锁定
    try:
        if code == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
            global_list.append([_ip, _port, code, client.recv(1024)])
        else:
            global_err_list.append([_ip, _port, code])  # 此处不能调用 recv api,会抛出异常
    except BaseException:
        print('进行列表操作时报错')
    lock.release()  # 释放锁
    client.close()  # 关闭连接


ip = '110.242.68.4'
for i in range(65536):  # 0~65535
    with sem:  # 并发限制
        thread = threading.Thread(target=port_search, args=(ip, i))
        global_thread_list.append(thread)
        thread.start()

for t in global_thread_list:  # 遍历,等待所有进程运行完毕
    t.join()

print(global_list)
# print(global_err_list)

遍历下来后,输出结果为:

[['110.242.68.4', 80, 0, b''], ['110.242.68.4', 443, 0, b'']]

就是web服务http和https两大协议使用的端口。

 

好了,这个核心逻辑应该就没啥问题了。

但是这种用户界面就太LOW了吧?所以后面我们将使用PySide2来制作工具界面。

下一篇将开始接触PySide2。

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值