基于PyQt5的端口扫描器

和黑猫之家聊聊?

由于课设的关系,选择用PyQt5制作一个端口扫描器。期间遇到了许多问题,在此回忆总结一下

操作系统:Windows 10 1709

python版本:python 3.5.3

注:本文所述方法大部分为摸索制作过程中搜索总结而来,本文只是进行归纳汇总

一,Python-socket

判断一个端口是否开放的方法有很多,最简单的自然是建立TCP三次握手,也就是简单的connect

[python] view plain copy
  1. s = socket(AF_INET,SOCK_STREAM)  

  2. s.connect((self.host,self.port))  

  3. s.close()  


用try包裹一下,做一下错误判定

[python] view plain copy
  1. except timeout:#超时,一般就认为端口为关闭,如果超时时间设置过小可能会造成遗漏  

  2.       s.close()  

  3.       return  

  4. except ConnectionRefusedError:#连接被拒绝  

  5.       s.close()  

  6.       return  

  7. except OSError as e:#其它的一些错误,当端口为0的时候会触发,还有就是linux系统中连接有上限,过了上限会报【OSERROR ERROR 24】  

  8.       if self.port!=0:  

  9.            print("OSError",self.port,self.host,e)  

  10.       return  

  11. except Exception as e:#防患于未然  

  12.       print(repr(e),self.port,self.host)  

  13.       return  

本来还想尝试一下raw_socket,只建立TCP第一次握手,发SYN包,但是遇到了一些问题


raw_socket需要手动制作TCP头部和IP头部

[python] view plain copy
  1. tcp_header=Create_TCP_SYN_Header("10.241.65.141","10.241.65.1",22)  

  2. ip_header=CreateIPHeaders("10.241.65.141","10.241.65.1")  

具体实现可以百度,有很多


然后就是建立这个RAW_SOCKET,一定要用管理员权限

网上有两种写法

[python] view plain copy
  1. s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)  

  2. s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)  

前者发包的时候会把我写好的头当作数据发出去

基于PyQt5的端口扫描器 渗透测试 第1张

基于PyQt5的端口扫描器 渗透测试 第2张

后者直接报错

基于PyQt5的端口扫描器 渗透测试 第3张

弄了好多小时也无解,故放弃

后来学长说python的socket库不完整。。可能是某些原因吧


二,PyQt5

这是第三个用pyqt写的程序了,然而才发现designer这个好东西,直接画界面就好了

界面的第一版不怎么好看,第二版算是正式版

基于PyQt5的端口扫描器 渗透测试 第4张

想想遇到的坑吧。。

将滚动条直接滚到底部,这么一个小功能还是找了好久

[python] view plain copy
  1. self.listView.verticalScrollBar().setValue(self.listView.verticalScrollBar().maximum())  

设置文本输入格式和范围,网上有其它类型的


[python] view plain copy
  1. self.lineEdit.setValidator(QtGui.QIntValidator(1,50000))  

一组按钮可以设置号每个按钮的id,然后获取选择id来实时获取当前选项


[python] view plain copy
  1. self.buttonGroup.setId(self.ip_radio,1)  

  2. self.buttonGroup.setId(self.port_radio,0)  

  3. if self.buttonGroup.checkedId():  

  4.     self.list_by_ip()  

  5. else:  

  6.     self.list_by_port()  

设置QTreeWidget宽度


[python] view plain copy
  1. self.result_tree.setColumnWidth(0,250)#第一列设成250  

从QlistWidget移除选中的一行(self.ippool为QlistWidget)


[python] view plain copy
  1. self.ippool.removeItemWidget(self.ippool.takeItem(self.ippool.row(self.ippool.selectedItems()[0])))  

QTreeWidgetItem设置复选框

[python] view plain copy
  1. temp=QtWidgets.QTreeWidgetItem(self.result_tree.topLevelItem(item))  

  2. temp.setFlags(temp.flags() | Qt.ItemIsUserCheckable)  

  3. temp.setCheckState(0, Qt.Unchecked)  

隐藏一个QTreeWidgetItem

self.item.setHidden(True)

差不多就这些


另外 推荐

Iconfont-阿里巴巴矢量图标库

图标很多,颜色随心

最后,pyqt5的打包使用python 3.6.4 pyinstaller在windows10 32位 的虚拟机中直接打包的,直接

[python] view plain copy
  1. pyinstaller -F -w -i scan.ico scanner.py  

就可以,python3.5下的pyinstaller打包不全PyQt5,32位则是希望打包后的文件可以在32和64位系统中运行


图片要转成py文件,经过引用后自动打包,图片打包成py的方法可以看我转的另一篇博客


三,线程

端口扫描器肯定是要多线程的啦

这次设计的是主界面按下按钮后,开启一个Qthread进行创建子线程操作,循环创建,设定同时运行的线程数上限,利用一些信号支持用户的手动停止

期间的问题就是,如果Qthread在执行的过程中更改了主线程的属性或内容,会造成python的不稳定,很容易直接崩溃并且无任何明显报错,唯一出现的可能是

QWidget::repaint: Recursive repaint detected

这句报错只有在控制台窗口下才会出现,IDLE并不会打印


可以利用信号(pysignal)或者其它机制避免Qthread更改主线程的属性或内容

PS:这里的不稳定十分的玄学,在某些时候直接崩溃,IDLE无任何报错,即使全部用try包裹也没用。甚至一些print语句也会影响到其崩溃是否发生,总之感觉程序处于一种极其不稳定的状态,所以据说好像QT作者并不推荐Qthread

pps:好像在IDLE下不那么容易崩溃


另外,python自带的threading不会有这个问题,不过得用好线程锁,这里就不细说了


四,总结

总之,花了总共近50个小时写出这么个东西,编程收获是要大于实际使用意义的

各种线程直接的处理,信息传递,python的内存释放机制,raw_socket尝试,啧啧

以后写个小程序出来用也会变得容易许多

文件代码扔到百度云了,有空再去传github吧

链接:https://pan.baidu.com/s/1mhl-wgoe031e0kZg6gJZPg 密码:5sx0

来黑猫之家看看呗

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Copyright © 2015-2018 黑猫吧(www.heimaoba.cn) 赣ICP备18005425号-1