Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80c9976832 | ||
|
|
b6326c25b9 | ||
|
|
35f591cf49 | ||
|
|
67e437bbd7 | ||
|
|
194dae8ce1 | ||
|
|
a89442ceb0 | ||
|
|
24debf6062 | ||
|
|
ff98846c99 | ||
|
|
e3f5a0bca1 | ||
|
|
7278e4f051 | ||
|
|
709a511c1d | ||
|
|
452027f085 | ||
|
|
8574802428 | ||
|
|
79fb044921 | ||
|
|
13ac634fea | ||
|
|
04a68bc262 | ||
|
|
47de15ab68 | ||
|
|
512af24a05 | ||
|
|
e5ff46d3bb | ||
|
|
c7a0979140 | ||
|
|
7fd7245df2 | ||
|
|
026301b011 | ||
|
|
5cc77f90ae | ||
|
|
0d4568ac8a | ||
|
|
a6ed491a39 | ||
|
|
90dfc3f1ae | ||
|
|
36388fb0d7 | ||
|
|
043166d0c8 | ||
|
|
b92d488851 | ||
|
|
d841a04a89 | ||
|
|
d8553baa78 | ||
|
|
8521d5aee3 | ||
|
|
ebe21d9c7f | ||
|
|
3b83abdbd3 | ||
|
|
aead2463c9 | ||
|
|
5cc5676672 | ||
|
|
ecc32ca18d | ||
|
|
661640de79 | ||
|
|
96dfdab7ed | ||
|
|
56bed5fd3b | ||
|
|
3fd3b18406 | ||
|
|
45bac97a16 | ||
|
|
2c6eca4a92 | ||
|
|
8109d59655 | ||
|
|
8a15571cd2 | ||
|
|
a15c8f2e60 | ||
|
|
cec3d6c04d | ||
|
|
d872604855 | ||
|
|
2b6fb933a4 | ||
|
|
1da892e863 | ||
|
|
19bfedd1a3 | ||
|
|
cb87392d9d | ||
|
|
7cd5bc9ec3 | ||
|
|
edcc1a30ab | ||
|
|
6f93f6ab06 | ||
|
|
9de1f44288 | ||
|
|
3d360b0881 | ||
|
|
cf1c415846 | ||
|
|
90133e2444 | ||
|
|
81246046b8 | ||
|
|
20e037ab9e | ||
|
|
bc27036ba7 | ||
|
|
1cd470b90b | ||
|
|
64471e34bc | ||
|
|
f4c5d3de09 | ||
|
|
0dc59405e3 | ||
|
|
7c775bdc9b | ||
|
|
61400ff4d4 | ||
|
|
e119e34ebe | ||
|
|
6f059747bf | ||
|
|
24d72641fa | ||
|
|
b91fca3aa6 | ||
|
|
a6f1947837 | ||
|
|
6919131cef | ||
|
|
9186d801fc | ||
|
|
76e2f200bd | ||
|
|
1400bbd9ce | ||
|
|
fd37628ac2 | ||
|
|
4396d5afc4 | ||
|
|
f5faf80883 | ||
|
|
ba32da97c8 | ||
|
|
ec2de73d3a | ||
|
|
f4d032e5c7 | ||
|
|
4114aac6c3 | ||
|
|
0c43c6bb5f | ||
|
|
7b3c965b88 | ||
|
|
d1035268f6 | ||
|
|
8968f30bf9 | ||
|
|
84c2c29600 | ||
|
|
b2a59f261c | ||
|
|
363a330f8e | ||
|
|
1d2aa059ee | ||
|
|
f2bfa908a1 | ||
|
|
02a8de4696 | ||
|
|
22fa3eff71 | ||
|
|
69361209c3 | ||
|
|
ff0aeaa4ef | ||
|
|
3c461dfd4b | ||
|
|
c6beda383e | ||
|
|
3d59f3befd | ||
|
|
e54f0db239 | ||
|
|
0f877f2749 | ||
|
|
b69ba165d9 | ||
|
|
965076fe52 | ||
|
|
f4d6cb0430 | ||
|
|
19d412188c | ||
|
|
798d85978a | ||
|
|
819742d753 | ||
|
|
9b32fa6219 | ||
|
|
2c93661726 | ||
|
|
bddaca2a1e | ||
|
|
2f06f2e051 | ||
|
|
1d26e46bd9 | ||
|
|
fa0ff826a3 | ||
|
|
e1b3114c83 | ||
|
|
a655f075db | ||
|
|
13d4ebbf6b | ||
|
|
913d10b1ee | ||
|
|
eb541a0104 | ||
|
|
2063186994 | ||
|
|
860d571039 | ||
|
|
c6d79adf9a | ||
|
|
5453900ba1 | ||
|
|
5029ff1e37 | ||
|
|
47fb691339 | ||
|
|
dff9dd17bf | ||
|
|
5bc4a16b90 | ||
|
|
b1918953b0 | ||
|
|
6e89ed7007 | ||
|
|
744e8bc941 | ||
|
|
0b98ce7ffd | ||
|
|
ca8b293b06 | ||
|
|
e28d99a257 | ||
|
|
488feb6108 | ||
|
|
6cdb106f07 | ||
|
|
a7c5ccd27a | ||
|
|
136f9dbf0a | ||
|
|
16b45c3450 | ||
|
|
68330fad52 | ||
|
|
dca167518d |
No files matched your search
@@ -1,11 +1,9 @@
|
||||
import os, gc, sys, time, json, psutil
|
||||
import hashlib, pefile, socket, msvcrt
|
||||
import xml.etree.ElementTree as xmlet
|
||||
import os, gc, sys, time, json, yara
|
||||
import pefile, socket, msvcrt, psutil
|
||||
import requests, pyperclip, win32file
|
||||
import win32gui, win32api, win32con
|
||||
from PYAS_Function import model_dict
|
||||
from PYAS_Engine import ListSimHash
|
||||
from PYAS_Extension import slist, alist
|
||||
from PYAS_Compress import ListCompressor
|
||||
from PYAS_Language import translate_dict
|
||||
from PYAS_Interface import Ui_MainWindow
|
||||
from threading import Thread
|
||||
@@ -18,12 +16,14 @@ class MainWindow_Controller(QMainWindow):
|
||||
super(MainWindow_Controller, self).__init__()
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||
self.pyas = str(sys.argv[0]).replace("\\", "/")
|
||||
self.pyae_version = "2024-01-23"
|
||||
self.pyas_version = "3.0.1"
|
||||
self.pyas = sys.argv[0].replace("\\", "/")
|
||||
self.dir = os.path.dirname(self.pyas)
|
||||
self.pyae_version = "Fusion Engine"
|
||||
self.pyas_version = "3.1.0"
|
||||
self.ui = Ui_MainWindow()
|
||||
self.ui.setupUi(self)
|
||||
self.init_show_pyas()
|
||||
self.init_startup()
|
||||
self.init_rule_dict()
|
||||
self.init_data_base()
|
||||
self.init_read_json()
|
||||
self.init_tray_icon()
|
||||
@@ -36,10 +36,6 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.init_control()
|
||||
self.init_threads()
|
||||
|
||||
def init_data_base(self):
|
||||
self.pe = ListCompressor()
|
||||
self.pe.load_model(model_dict)
|
||||
|
||||
def init_threads(self):
|
||||
self.protect_proc_init()
|
||||
self.protect_file_init()
|
||||
@@ -54,6 +50,50 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.tray_icon.activated.connect(self.init_show_pyas)
|
||||
self.tray_icon.show()
|
||||
|
||||
def init_startup(self):
|
||||
try:
|
||||
if len(sys.argv) > 1:
|
||||
param = sys.argv[1]
|
||||
if "h" in param or "hid" in param:
|
||||
pass
|
||||
else:
|
||||
self.init_show_pyas()
|
||||
elif len(sys.argv) <= 1:
|
||||
self.init_show_pyas()
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
def init_data_base(self):
|
||||
try:
|
||||
file_path = os.path.join(self.dir, "Model")
|
||||
for root, dirs, files in os.walk(file_path):
|
||||
for file in files:
|
||||
model_path = os.path.join(root, file)
|
||||
ftype = str(f".{file.split('.')[-1]}").lower()
|
||||
if ftype in [".json", ".txt"]:
|
||||
self.pe = ListSimHash()
|
||||
self.pe.load_model(model_path)
|
||||
elif ftype in [".h5", ".pkl"]:
|
||||
pass # Not Sp Yet !
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
def init_rule_dict(self):
|
||||
try:
|
||||
self.compiled_rules = {}
|
||||
file_path = os.path.join(self.dir, "Rules")
|
||||
for root, dirs, files in os.walk(file_path):
|
||||
for file in files:
|
||||
yara_path = os.path.join(root, file)
|
||||
ftype = str(f".{file.split('.')[-1]}").lower()
|
||||
if ftype in [".yara", ".yar"]:
|
||||
rules = yara.compile(yara_path)
|
||||
elif ftype in [".yc", ".yrc"]:
|
||||
rules = yara.load(yara_path)
|
||||
self.compiled_rules[yara_path] = rules
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
def init_config_boot(self):
|
||||
try:
|
||||
with open(r"\\.\PhysicalDrive0", "r+b") as f:
|
||||
@@ -88,19 +128,19 @@ class MainWindow_Controller(QMainWindow):
|
||||
if not os.path.exists("C:/ProgramData/PYAS"):
|
||||
os.makedirs("C:/ProgramData/PYAS")
|
||||
if not os.path.exists("C:/ProgramData/PYAS/PYAS.json"):
|
||||
self.write_config({"high_sensitive":0,"cloud_services":1,
|
||||
self.write_config({"high_sensitive":0,"cloud_services":"http://127.0.0.1/",
|
||||
"language":"en_US","theme_color":"White","theme_custom":""})
|
||||
with open("C:/ProgramData/PYAS/PYAS.json", "r") as f:
|
||||
self.json = json.load(f)
|
||||
except:
|
||||
self.write_config({"high_sensitive":0,"cloud_services":1,
|
||||
self.write_config({"high_sensitive":0,"cloud_services":"http://127.0.0.1/",
|
||||
"language":"en_US","theme_color":"White","theme_custom":""})
|
||||
with open("C:/ProgramData/PYAS/PYAS.json", "r") as f:
|
||||
self.json = json.load(f)
|
||||
|
||||
def init_config_json(self):
|
||||
self.json["high_sensitive"] = self.json.get("high_sensitive", 0)
|
||||
self.json["cloud_services"] = self.json.get("cloud_services", 1)
|
||||
self.json["cloud_services"] = self.json.get("cloud_services", "http://127.0.0.1/")
|
||||
self.json["language"] = self.json.get("language", "en_US")
|
||||
self.json["theme_color"] = self.json.get("theme_color", "White")
|
||||
self.json["theme_custom"] = self.json.get("theme_custom", "")
|
||||
@@ -109,11 +149,6 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ui.high_sensitivity_switch_Button.setStyleSheet("""
|
||||
QPushButton{border:none;background-color:rgb(200, 250, 200);border-radius: 10px;}
|
||||
QPushButton:hover{background-color:rgb(210, 250, 210);}""")
|
||||
if self.json["cloud_services"] == 1:
|
||||
self.ui.cloud_services_switch_Button.setText(self.trans("已開啟"))
|
||||
self.ui.cloud_services_switch_Button.setStyleSheet("""
|
||||
QPushButton{border:none;background-color:rgb(200, 250, 200);border-radius: 10px;}
|
||||
QPushButton:hover{background-color:rgb(210, 250, 210);}""")
|
||||
if self.json["language"] == "zh_TW":
|
||||
self.ui.Language_Traditional_Chinese.setChecked(True)
|
||||
elif self.json["language"] == "zh_CN":
|
||||
@@ -165,7 +200,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ui.Protection_switch_Button_4.clicked.connect(self.protect_reg_init)
|
||||
self.ui.Protection_switch_Button_5.clicked.connect(self.protect_net_init)
|
||||
self.ui.high_sensitivity_switch_Button.clicked.connect(self.change_sensitive)
|
||||
self.ui.cloud_services_switch_Button.clicked.connect(self.change_cloud_service)
|
||||
self.ui.cloud_services_switch_Button.clicked.connect(self.cloud_service)
|
||||
self.ui.Add_White_list_Button.clicked.connect(self.add_white_list)
|
||||
self.ui.Language_Traditional_Chinese.clicked.connect(self.init_change_lang)
|
||||
self.ui.Language_Simplified_Chinese.clicked.connect(self.init_change_lang)
|
||||
@@ -287,9 +322,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ui.high_sensitivity_title.setText(self.trans("高靈敏度模式"))
|
||||
self.ui.high_sensitivity_illustrate.setText(self.trans("啟用此選項可以提高引擎的靈敏度"))
|
||||
self.ui.high_sensitivity_switch_Button.setText(self.trans(self.ui.high_sensitivity_switch_Button.text()))
|
||||
self.ui.cloud_services_title.setText(self.trans("雲端掃描服務"))
|
||||
self.ui.cloud_services_illustrate.setText(self.trans("啟用此選項可以連接雲端掃描服務"))
|
||||
self.ui.cloud_services_switch_Button.setText(self.trans(self.ui.cloud_services_switch_Button.text()))
|
||||
self.ui.cloud_services_title.setText(self.trans("上報雲端分析"))
|
||||
self.ui.cloud_services_illustrate.setText(self.trans("此選項可以選擇檔案並上報雲端分析"))
|
||||
self.ui.cloud_services_switch_Button.setText(self.trans("選擇"))
|
||||
self.ui.Add_White_list_title.setText(self.trans("增加到白名單"))
|
||||
self.ui.Add_White_list_illustrate.setText(self.trans("此選項可以選擇檔案並增加到白名單"))
|
||||
self.ui.Add_White_list_Button.setText(self.trans(self.ui.Add_White_list_Button.text()))
|
||||
@@ -559,8 +594,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.setWindowOpacity(self.pyas_opacity/100)
|
||||
else:
|
||||
self.timer.stop()
|
||||
self.show()
|
||||
self.pyas_opacity = 0
|
||||
self.show()
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(update_opacity)
|
||||
self.timer.start(2)
|
||||
@@ -583,7 +618,13 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.send_notify(self.trans("PYAS 已最小化到系統托盤圖標"))
|
||||
|
||||
def closeEvent(self, event):
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("您確定要退出 PYAS 和所有防護嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("您確定要退出 PYAS 和所有防護嗎?"):
|
||||
self.block_window = False
|
||||
self.proc_protect = False
|
||||
self.file_protect = False
|
||||
self.mbr_protect = False
|
||||
self.reg_protect = False
|
||||
self.net_protect = False
|
||||
self.virus_scan_break()
|
||||
event.accept()
|
||||
else:
|
||||
@@ -591,14 +632,29 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def bug_event(self, text):
|
||||
try:
|
||||
print(f"[Error] {text}")
|
||||
QMessageBox.critical(self,self.trans("錯誤"), text, QMessageBox.Ok)
|
||||
print(f"[Error] > {text}")
|
||||
QMessageBox.critical(self, "Error", str(text), QMessageBox.Ok)
|
||||
except:
|
||||
pass
|
||||
|
||||
def info_event(self, text):
|
||||
try:
|
||||
print(f"[Info] > {text}")
|
||||
QMessageBox.information(self, "Info", self.trans(str(text)), QMessageBox.Ok)
|
||||
except:
|
||||
pass
|
||||
|
||||
def question_event(self, text):
|
||||
try:
|
||||
print(f"[Quest] > {text}")
|
||||
return QMessageBox.question(self, "Quest", self.trans(str(text)),QMessageBox.Yes|QMessageBox.No) == 16384
|
||||
except:
|
||||
return False
|
||||
|
||||
def send_notify(self, text):
|
||||
try:
|
||||
now_time = time.strftime('%Y/%m/%d %H:%M:%S')
|
||||
now_time = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
print(f"[Notify] > [{now_time}] {text}")
|
||||
self.tray_icon.showMessage(now_time, text, 5000)
|
||||
QMetaObject.invokeMethod(self.ui.State_output, "append", Qt.QueuedConnection, Q_ARG(str, f"[{now_time}] {text}"))
|
||||
except:
|
||||
@@ -607,8 +663,10 @@ class MainWindow_Controller(QMainWindow):
|
||||
def show_menu(self):
|
||||
self.WindowMenu = QMenu()
|
||||
Main_Settings = QAction(self.trans("設定"),self)
|
||||
#Main_Update = QAction(self.trans("更新"),self)
|
||||
Main_About = QAction(self.trans("關於"),self)
|
||||
self.WindowMenu.addAction(Main_Settings)
|
||||
#self.WindowMenu.addAction(Main_Update)
|
||||
self.WindowMenu.addAction(Main_About)
|
||||
Qusetion = self.WindowMenu.exec_(self.ui.Menu_Button.mapToGlobal(QPoint(0, 30)))
|
||||
if Qusetion == Main_About and self.ui.About_widget.isHidden():
|
||||
@@ -626,6 +684,15 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ui.Window_widget.raise_()
|
||||
self.change_animation_3(self.ui.Setting_widget,0.5)
|
||||
self.change_animation_5(self.ui.Setting_widget,10,50,831,481)
|
||||
#if Qusetion == Main_Update:
|
||||
#self.update_database()
|
||||
|
||||
def update_database(self):
|
||||
try:
|
||||
if self.question_event("您確定要更新數據庫嗎?"):
|
||||
pass
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
def change_sensitive(self):
|
||||
sw_state = self.ui.high_sensitivity_switch_Button.text()
|
||||
@@ -635,7 +702,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ui.high_sensitivity_switch_Button.setStyleSheet("""
|
||||
QPushButton{border:none;background-color:rgb(230,230,230);border-radius: 10px;}
|
||||
QPushButton:hover{background-color:rgb(220,220,220);}""")
|
||||
elif QMessageBox.warning(self,self.trans("警告"),self.trans("此選項可能會誤報檔案,您確定要開啟嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
elif self.question_event("此選項可能會誤報檔案,您確定要開啟嗎?"):
|
||||
self.json["high_sensitive"] = 1
|
||||
self.ui.high_sensitivity_switch_Button.setText(self.trans("已開啟"))
|
||||
self.ui.high_sensitivity_switch_Button.setStyleSheet("""
|
||||
@@ -643,21 +710,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
QPushButton:hover{background-color:rgb(210,250,210);}""")
|
||||
self.write_config(self.json)
|
||||
|
||||
def change_cloud_service(self):
|
||||
sw_state = self.ui.cloud_services_switch_Button.text()
|
||||
if sw_state == self.trans("已關閉"):
|
||||
self.json["cloud_services"] = 1
|
||||
self.ui.cloud_services_switch_Button.setText(self.trans("已開啟"))
|
||||
self.ui.cloud_services_switch_Button.setStyleSheet("""
|
||||
QPushButton{border:none;background-color:rgb(200,250,200);border-radius: 10px;}
|
||||
QPushButton:hover{background-color:rgb(210,250,210);}""")
|
||||
elif sw_state == self.trans("已開啟"):
|
||||
self.json["cloud_services"] = 0
|
||||
self.ui.cloud_services_switch_Button.setText(self.trans("已關閉"))
|
||||
self.ui.cloud_services_switch_Button.setStyleSheet("""
|
||||
QPushButton{border:none;background-color:rgb(230,230,230);border-radius: 10px;}
|
||||
QPushButton:hover{background-color:rgb(220,220,220);}""")
|
||||
self.write_config(self.json)
|
||||
def cloud_service(self):
|
||||
try:
|
||||
file = str(QFileDialog.getOpenFileName(self,self.trans("上報雲端分析"),"C:/")[0]).replace("\\", "/")
|
||||
if file and self.question_event("您確定要上報雲端分析嗎?"):
|
||||
response = requests.post(self.json["cloud_services"], files={'file': open(file, 'rb')})
|
||||
if response.status_code == 200:
|
||||
self.info_event(f"成功上報雲端分析: "+file)
|
||||
else:
|
||||
self.bug_event(response.status_code)
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
def init_scan(self):
|
||||
try:
|
||||
@@ -665,7 +728,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
QApplication.processEvents()
|
||||
try:
|
||||
for file in self.virus_list:
|
||||
self.unlock_file(file)
|
||||
self.lock_file(file, False)
|
||||
except:
|
||||
pass
|
||||
self.scan_file = True
|
||||
@@ -692,17 +755,14 @@ class MainWindow_Controller(QMainWindow):
|
||||
copyPath.triggered.connect(lambda: copyPathFunc())
|
||||
menu.exec_(self.ui.Virus_Scan_output.mapToGlobal(point))
|
||||
|
||||
def lock_file(self, file):
|
||||
def lock_file(self, file, lock):
|
||||
try:
|
||||
self.virus_lock[file] = os.open(file, os.O_RDWR)
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_NBLCK, 0)
|
||||
except:
|
||||
pass
|
||||
|
||||
def unlock_file(self, file):
|
||||
try:
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_UNLCK, 0)
|
||||
os.close(self.virus_lock[file])
|
||||
if lock:
|
||||
self.virus_lock[file] = os.open(file, os.O_RDWR)
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_NBLCK, 0)
|
||||
else:
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_UNLCK, 0)
|
||||
os.close(self.virus_lock[file])
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -714,10 +774,10 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.ui.Virus_Scan_output.findItems(file, Qt.MatchContains)[0].checkState() == Qt.Checked:
|
||||
self.ui.Virus_Scan_text.setText(self.trans("正在刪除: ")+file)
|
||||
QApplication.processEvents()
|
||||
self.unlock_file(file)
|
||||
self.lock_file(file, False)
|
||||
os.remove(file)
|
||||
else:
|
||||
self.unlock_file(file)
|
||||
self.lock_file(file, False)
|
||||
except:
|
||||
continue
|
||||
self.virus_lock = {}
|
||||
@@ -730,14 +790,15 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def write_scan(self, state, file):
|
||||
try:
|
||||
self.lock_file(file)
|
||||
self.virus_list.append(file)
|
||||
self.virus_list_ui.append(f"[{state}] {file}")
|
||||
item = QListWidgetItem()
|
||||
item.setText(f"[{state}] {file}")
|
||||
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||
item.setCheckState(Qt.Checked)
|
||||
self.ui.Virus_Scan_output.addItem(item)
|
||||
if state and file:
|
||||
self.lock_file(file, True)
|
||||
self.virus_list.append(file)
|
||||
self.virus_list_ui.append(f"[{state}] {file}")
|
||||
item = QListWidgetItem()
|
||||
item.setText(f"[{state}] {file}")
|
||||
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||
item.setCheckState(Qt.Checked)
|
||||
self.ui.Virus_Scan_output.addItem(item)
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -773,9 +834,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
def file_scan(self):
|
||||
try:
|
||||
file = str(QFileDialog.getOpenFileName(self,self.trans("病毒掃描"),"C:/")[0])
|
||||
if file and file not in self.whitelist:
|
||||
if file and file not in self.whitelist and file != self.pyas:
|
||||
self.init_scan()
|
||||
self.start_scan(file)
|
||||
self.write_scan(self.start_scan(file),file)
|
||||
self.answer_scan()
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
@@ -803,60 +864,41 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.bug_event(e)
|
||||
self.virus_scan_break()
|
||||
|
||||
def start_scan(self, file):
|
||||
try:
|
||||
file_type = str(f".{file.split('.')[-1]}").lower()
|
||||
if self.json["high_sensitive"] and file != self.pyas:
|
||||
if self.api_scan(file):
|
||||
self.write_scan(self.trans("惡意"),file)
|
||||
elif self.pe_scan(file):
|
||||
self.write_scan(self.trans("可疑"),file)
|
||||
elif file_type in slist and file != self.pyas:
|
||||
if self.api_scan(file):
|
||||
self.write_scan(self.trans("惡意"),file)
|
||||
elif self.pe_scan(file):
|
||||
self.write_scan(self.trans("可疑"),file)
|
||||
except:
|
||||
pass
|
||||
|
||||
def traverse_path(self,path):
|
||||
for fd in os.listdir(path):
|
||||
try:
|
||||
file = str(os.path.join(path,fd)).replace("\\", "/")
|
||||
if self.scan_file == False:
|
||||
break
|
||||
elif ":/Windows" in file:
|
||||
continue
|
||||
elif os.path.isdir(file):
|
||||
self.traverse_path(file)
|
||||
elif file not in self.whitelist:
|
||||
elif file not in self.whitelist and file != self.pyas:
|
||||
self.ui.Virus_Scan_text.setText(self.trans(f"正在掃描: ")+file)
|
||||
QApplication.processEvents()
|
||||
self.start_scan(file)
|
||||
self.write_scan(self.start_scan(file),file)
|
||||
gc.collect()
|
||||
except:
|
||||
pass
|
||||
|
||||
def api_scan(self, file):
|
||||
def start_scan(self, file):
|
||||
try:
|
||||
if self.json["cloud_services"] == 1:
|
||||
with open(file, "rb") as f:
|
||||
text = str(hashlib.md5(f.read()).hexdigest())
|
||||
QApplication.processEvents()
|
||||
strBody = f'-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="md5s"\r\n\r\n{text}\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="format"\r\n\r\nXML\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="product"\r\n\r\n360zip\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="combo"\r\n\r\n360zip_main\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="v"\r\n\r\n2\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="osver"\r\n\r\n5.1\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="vk"\r\n\r\na03bc211\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="mid"\r\n\r\n8a40d9eff408a78fe9ec10a0e7e60f62\r\n-------------------------------7d83e2d7a141e--'
|
||||
response = requests.post('http://qup.f.360.cn/file_health_info.php', data=strBody, timeout=3)
|
||||
return response.status_code == 200 and float(xmlet.fromstring(response.text).find('.//e_level').text) > 50
|
||||
ftype = str(f".{file.split('.')[-1]}").lower()
|
||||
label, level = self.pe_scan(file)
|
||||
if label and "Unknown" in label:
|
||||
if self.rule_scan(file):
|
||||
return "Rules"
|
||||
elif label and "White" not in label:
|
||||
if self.json["high_sensitive"]:
|
||||
return label
|
||||
elif level and level >= 0.9:
|
||||
return label
|
||||
elif self.json["high_sensitive"]:
|
||||
if self.rule_scan(file):
|
||||
return "Rules"
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def sign_scan(self, file):
|
||||
try:
|
||||
with pefile.PE(file, fast_load=True) as pe:
|
||||
return pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]].VirtualAddress == 0
|
||||
except:
|
||||
return True
|
||||
|
||||
def pe_scan(self, file):
|
||||
try:
|
||||
fn = []
|
||||
@@ -867,20 +909,36 @@ class MainWindow_Controller(QMainWindow):
|
||||
fn.append(str(func.name, "utf-8"))
|
||||
except:
|
||||
pass
|
||||
if self.json["high_sensitive"]:
|
||||
return self.pe.predict(fn)
|
||||
elif "_CorExeMain" not in fn:
|
||||
return self.pe.predict(fn)
|
||||
QApplication.processEvents()
|
||||
return self.pe.predict(fn)
|
||||
except:
|
||||
return False, False
|
||||
|
||||
def sign_scan(self, file):
|
||||
try:
|
||||
with pefile.PE(file, fast_load=True) as pe:
|
||||
return pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]].VirtualAddress == 0
|
||||
except:
|
||||
return True
|
||||
|
||||
def rule_scan(self, file):
|
||||
try:
|
||||
with open(file, "rb") as f:
|
||||
data = f.read()
|
||||
for name, rules in self.compiled_rules.items():
|
||||
QApplication.processEvents()
|
||||
if rules.match(data=data):
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def proc_scan(self, pid):
|
||||
def proc_scan(self, p):
|
||||
try:
|
||||
for entry in psutil.Process(pid).memory_maps():
|
||||
for entry in p.memory_maps():
|
||||
file = entry.path.replace("\\", "/")
|
||||
if ":/Windows" not in file and ":/Program" not in file:
|
||||
if self.api_scan(file) or self.pe_scan(file):
|
||||
if self.start_scan(file):
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
@@ -888,13 +946,13 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def repair_system(self):
|
||||
try:
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("您確定要修復系統檔案嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("您確定要修復系統檔案嗎?"):
|
||||
self.repair_system_wallpaper()
|
||||
self.repair_system_restrict()
|
||||
self.repair_system_file_type()
|
||||
self.repair_system_file_icon()
|
||||
self.repair_system_image()
|
||||
QMessageBox.information(self,self.trans("成功"),self.trans("修復系統檔案成功"),QMessageBox.Ok)
|
||||
self.info_event("修復系統檔案成功")
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
@@ -987,9 +1045,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
for i in Permission:
|
||||
try:
|
||||
win32api.RegDeleteValue(key,i)
|
||||
self.proc.kill()
|
||||
file = self.proc.exe().replace("\\", "/")
|
||||
self.send_notify(self.trans("惡意行為攔截: ")+file)
|
||||
self.kill_process(self.proc, "惡意行為攔截")
|
||||
except:
|
||||
pass
|
||||
win32api.RegCloseKey(key)
|
||||
@@ -1007,12 +1063,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def clean_system(self):
|
||||
try:
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("您確定要清理系統垃圾嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("您確定要清理系統垃圾嗎?"):
|
||||
self.total_deleted_size = 0
|
||||
self.traverse_temp(f"C:/Users/{os.getlogin()}/AppData/Local/Temp/")
|
||||
self.traverse_temp(f"C:/Windows/Temp/")
|
||||
self.traverse_temp(f"C:/$Recycle.Bin/")
|
||||
QMessageBox.information(self,self.trans("成功"),self.trans(f"成功清理了 {self.total_deleted_size} 位元的系統垃圾"),QMessageBox.Ok)
|
||||
self.info_event(f"成功清理了 {self.total_deleted_size} 位元的系統垃圾")
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
@@ -1034,44 +1090,44 @@ class MainWindow_Controller(QMainWindow):
|
||||
try:
|
||||
file = str(QFileDialog.getOpenFileName(self,self.trans("增加到白名單"),"C:/")[0]).replace("\\", "/")
|
||||
if file and file not in self.whitelist:
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("您確定要增加到白名單嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("您確定要增加到白名單嗎?"):
|
||||
self.whitelist.append(file)
|
||||
with open("C:/ProgramData/PYAS/Whitelist.ini", "a+") as f:
|
||||
f.write(f"{file}\n")
|
||||
QMessageBox.information(self,self.trans("成功"),self.trans(f"成功增加到白名單: ")+file,QMessageBox.Ok)
|
||||
elif file and QMessageBox.warning(self,self.trans("警告"),self.trans("您確定要取消增加到白名單嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
self.info_event(f"成功增加到白名單: "+file)
|
||||
elif file and self.question_event("您確定要取消增加到白名單嗎?"):
|
||||
self.whitelist.remove(file)
|
||||
with open("C:/ProgramData/PYAS/Whitelist.ini", "w") as f:
|
||||
for white_file in self.whitelist:
|
||||
f.write(f'{white_file}\n')
|
||||
QMessageBox.information(self,self.trans("成功"),self.trans(f"成功取消增加到白名單: ")+file,QMessageBox.Ok)
|
||||
self.info_event(f"成功取消增加到白名單: "+file)
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
|
||||
def get_software_window(self):
|
||||
try:
|
||||
self.block_window = False
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("請選擇要攔截的軟體彈窗"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("請選擇要攔截的軟體彈窗"):
|
||||
while not self.block_window:
|
||||
window_name = str(win32gui.GetWindowText(win32gui.GetForegroundWindow()))
|
||||
QApplication.processEvents()
|
||||
if window_name not in ["","PYAS",self.trans("警告")]:
|
||||
if window_name not in self.blocklist:
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans(f"您確定要攔截 {window_name} 嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event(f"您確定要攔截 {window_name} 嗎?"):
|
||||
self.blocklist.append(window_name)
|
||||
with open("C:/ProgramData/PYAS/Blocklist.ini", "a+") as f:
|
||||
f.write(f'{window_name}\n')
|
||||
QMessageBox.information(self,self.trans("成功"),self.trans(f"成功增加到彈窗攔截: ")+window_name,QMessageBox.Ok)
|
||||
elif QMessageBox.warning(self,self.trans("警告"),self.trans(f"您確定要取消攔截 {window_name} 嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
self.info_event(f"成功增加到彈窗攔截: "+window_name)
|
||||
elif self.question_event(f"您確定要取消攔截 {window_name} 嗎?"):
|
||||
self.blocklist.remove(window_name)
|
||||
with open("C:/ProgramData/PYAS/Blocklist.ini", "w") as f:
|
||||
for block_name in self.blocklist:
|
||||
f.write(f'{block_name}\n')
|
||||
QMessageBox.information(self,self.trans("成功"),self.trans(f"成功取消彈窗攔截: ")+window_name,QMessageBox.Ok)
|
||||
self.info_event(f"成功取消彈窗攔截: "+window_name)
|
||||
self.block_window = True
|
||||
self.block_window_init()
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
self.bug_event(e)
|
||||
|
||||
def block_window_init(self):
|
||||
self.block_window = True
|
||||
@@ -1080,7 +1136,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def block_software_window(self):
|
||||
while self.block_window:
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
time.sleep(0.2)
|
||||
for window_name in self.blocklist:
|
||||
win32gui.PostMessage(win32gui.FindWindow(None, window_name), 274, 61536, 0)
|
||||
except:
|
||||
@@ -1088,9 +1144,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def repair_network(self):
|
||||
try:
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("您確定要修復系統網路嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("您確定要修復系統網路嗎?"):
|
||||
os.system("netsh winsock reset")
|
||||
if QMessageBox.warning(self,self.trans("警告"),self.trans("使用此選項需要重啟,您確定要重啟嗎?"),QMessageBox.Yes|QMessageBox.No) == 16384:
|
||||
if self.question_event("使用此選項需要重啟,您確定要重啟嗎?"):
|
||||
os.system("shutdown -r -t 0")
|
||||
except Exception as e:
|
||||
self.bug_event(e)
|
||||
@@ -1129,7 +1185,11 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.Process_popMenu.exec_(self.ui.Process_list.mapToGlobal(pos)) == self.kill_Process:
|
||||
for p in psutil.process_iter():
|
||||
if p.pid == self.pid:
|
||||
p.kill()
|
||||
file = p.exe().replace("\\", "/")
|
||||
if file == self.pyas:
|
||||
self.close()
|
||||
else:
|
||||
p.kill()
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -1209,77 +1269,111 @@ class MainWindow_Controller(QMainWindow):
|
||||
Thread(target=self.protect_net_thread, daemon=True).start()
|
||||
|
||||
def protect_proc_thread(self):
|
||||
existing_processes = set()
|
||||
self.proc = None
|
||||
existing_process = set()
|
||||
for p in psutil.process_iter():
|
||||
if p.pid not in existing_processes:
|
||||
existing_processes.add(p.pid)
|
||||
existing_process.add(p.pid)
|
||||
while self.proc_protect:
|
||||
time.sleep(0.05)
|
||||
time.sleep(0.01)
|
||||
new_process = set()
|
||||
for p in psutil.process_iter():
|
||||
new_process.add(p.pid)
|
||||
for pid in new_process - existing_process:
|
||||
try:
|
||||
if p.pid not in existing_processes:
|
||||
existing_processes.add(p.pid)
|
||||
name, file, cmd = p.name(), p.exe().replace("\\", "/"), p.cmdline()
|
||||
if file != self.pyas and file not in self.whitelist:
|
||||
psutil.Process(p.pid).suspend()
|
||||
if "powershell" in name and self.api_scan(cmd[-1].split("'")[-2]):
|
||||
p.kill()
|
||||
file = cmd[-1].split("'")[-2].replace("\\", "/")
|
||||
self.send_notify(self.trans("惡意腳本攔截: ")+file)
|
||||
elif "cmd.exe" in name and self.api_scan(" ".join(cmd[2:])):
|
||||
p.kill()
|
||||
file = " ".join(cmd[2:]).replace("\\", "/")
|
||||
self.send_notify(self.trans("惡意腳本攔截: ")+file)
|
||||
elif ":/Windows" in file and self.api_scan(cmd[-1]):
|
||||
p.kill()
|
||||
file = cmd[-1].replace("\\", "/")
|
||||
self.send_notify(self.trans("惡意軟體攔截: ")+file)
|
||||
elif ":/Windows" not in file and self.proc_scan(p.pid):
|
||||
p.kill()
|
||||
self.send_notify(self.trans("惡意軟體攔截: ")+file)
|
||||
elif ":/Windows" not in file and self.sign_scan(file):
|
||||
self.proc = p
|
||||
psutil.Process(p.pid).resume()
|
||||
gc.collect()
|
||||
p = psutil.Process(pid)
|
||||
self.handle_new_process(p)
|
||||
except:
|
||||
pass
|
||||
existing_process = new_process
|
||||
|
||||
def handle_new_process(self, p):
|
||||
try:
|
||||
name, file, cmd = p.name(), p.exe().replace("\\", "/"), p.cmdline()
|
||||
if file != self.pyas and file not in self.whitelist:
|
||||
self.lock_process(p, True)
|
||||
if "powershell" in name and self.start_scan(cmd[-1].split("'")[-2]):
|
||||
self.kill_process(p, "惡意腳本攔截")
|
||||
elif "cmd.exe" in name and self.start_scan(" ".join(cmd[2:])):
|
||||
self.kill_process(p, "惡意腳本攔截")
|
||||
elif ":/Windows" in file and self.start_scan(cmd[-1]):
|
||||
self.kill_process(p, "惡意軟體攔截")
|
||||
elif ":/Windows" not in file and self.proc_scan(p):
|
||||
self.kill_process(p, "惡意軟體攔截")
|
||||
elif ":/Windows" not in file and self.sign_scan(file):
|
||||
self.proc = p
|
||||
self.lock_process(p, False)
|
||||
gc.collect()
|
||||
except:
|
||||
self.lock_process(p, False)
|
||||
|
||||
def lock_process(self, p, lock):
|
||||
try:
|
||||
if lock:
|
||||
p.suspend()
|
||||
else:
|
||||
p.resume()
|
||||
except:
|
||||
pass
|
||||
|
||||
def kill_process(self, p, info):
|
||||
try:
|
||||
if p.is_running():
|
||||
file = p.exe().replace("\\", "/")
|
||||
self.send_notify(self.trans(f"{info}: ")+file)
|
||||
for child in p.children(recursive=True):
|
||||
child.kill()
|
||||
p.kill()
|
||||
self.proc = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def protect_file_thread(self):
|
||||
hDir = win32file.CreateFile("C:/Users/",win32con.GENERIC_READ,win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE|win32con.FILE_SHARE_DELETE,None,win32con.OPEN_EXISTING,win32con.FILE_FLAG_BACKUP_SEMANTICS,None)
|
||||
hDir = win32file.CreateFile("C:/",win32con.GENERIC_READ,win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE|win32con.FILE_SHARE_DELETE,None,win32con.OPEN_EXISTING,win32con.FILE_FLAG_BACKUP_SEMANTICS,None)
|
||||
self.ransom_counts = 0
|
||||
while self.file_protect:
|
||||
for action, file in win32file.ReadDirectoryChangesW(hDir,10485760,True,win32con.FILE_NOTIFY_CHANGE_FILE_NAME|win32con.FILE_NOTIFY_CHANGE_DIR_NAME|win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES|win32con.FILE_NOTIFY_CHANGE_SIZE|win32con.FILE_NOTIFY_CHANGE_LAST_WRITE|win32con.FILE_NOTIFY_CHANGE_SECURITY,None,None):
|
||||
try:
|
||||
fpath = str(f"C:/Users/{file}").replace("\\", "/")
|
||||
fpath = str(f"C:/{file}").replace("\\", "/")
|
||||
ftype = str(f".{fpath.split('.')[-1]}").lower()
|
||||
if action == 3 and ftype in slist and os.path.getsize(fpath) <= 20971520:
|
||||
if self.api_scan(fpath) or self.pe_scan(fpath):
|
||||
os.remove(fpath)
|
||||
self.send_notify(self.trans("惡意軟體刪除: ")+fpath)
|
||||
if self.ransom_counts >= 3 and self.proc:
|
||||
self.ransom_counts = 0
|
||||
self.kill_process(self.proc, "勒索行為攔截")
|
||||
elif ":/Windows" in fpath and "/Temp/" not in fpath:
|
||||
if action == 2 and ftype in alist:
|
||||
self.ransom_counts += 1
|
||||
elif action == 4 and ftype in alist:
|
||||
self.ransom_counts += 1
|
||||
elif ":/Users" in fpath and "/AppData/" not in fpath:
|
||||
if action == 2 and ftype in alist:
|
||||
self.ransom_counts += 1
|
||||
elif action == 4 and ftype in alist:
|
||||
self.ransom_counts += 1
|
||||
elif action == 3 and ftype in slist and os.path.getsize(fpath) <= 52428800:
|
||||
self.send_notify(self.trans(f"正在掃描: ")+fpath)
|
||||
if self.sign_scan(file) and self.start_scan(fpath):
|
||||
os.remove(fpath)
|
||||
self.send_notify(self.trans("惡意軟體刪除: ")+fpath)
|
||||
except:
|
||||
pass
|
||||
|
||||
def protect_boot_thread(self):
|
||||
while self.mbr_protect and self.mbr_value:
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
time.sleep(0.2)
|
||||
with open(r"\\.\PhysicalDrive0", "r+b") as f:
|
||||
if self.mbr_value[510:512] != b'\x55\xAA':
|
||||
self.proc.kill()
|
||||
file = self.proc.exe().replace("\\", "/")
|
||||
self.send_notify(self.trans("惡意行為攔截: ")+file)
|
||||
self.kill_process(self.proc, "惡意行為攔截")
|
||||
elif f.read(512) != self.mbr_value:
|
||||
f.seek(0)
|
||||
f.write(self.mbr_value)
|
||||
self.proc.kill()
|
||||
file = self.proc.exe().replace("\\", "/")
|
||||
self.send_notify(self.trans("惡意行為攔截: ")+file)
|
||||
self.kill_process(self.proc, "惡意行為攔截")
|
||||
except:
|
||||
pass
|
||||
self.kill_process(self.proc, "惡意行為攔截")
|
||||
|
||||
def protect_reg_thread(self):
|
||||
while self.reg_protect:
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
time.sleep(0.2)
|
||||
self.repair_system_image()
|
||||
self.repair_system_restrict()
|
||||
self.repair_system_file_type()
|
||||
@@ -1290,14 +1384,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
def protect_net_thread(self):
|
||||
while self.net_protect:
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
time.sleep(0.2)
|
||||
local = socket.gethostbyname(socket.gethostname())
|
||||
for conn in self.proc.connections():
|
||||
file = self.proc.exe().replace("\\", "/")
|
||||
if ":/Windows" not in file and ":/Program" not in file:
|
||||
if conn.laddr.ip == local:
|
||||
self.proc.kill()
|
||||
self.send_notify(self.trans("網路通訊攔截: ")+file)
|
||||
self.kill_process(self.proc, "網路通訊攔截")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import json
|
||||
|
||||
class ListCompressor:
|
||||
def __init__(self):
|
||||
self.model = {}
|
||||
|
||||
def save_model(self, file_name):
|
||||
with open(file_name, 'w') as f:
|
||||
json.dump(self.model, f)
|
||||
print(f"\nModel Is Saved In {file_name}")
|
||||
|
||||
def load_model(self, model_data):
|
||||
if isinstance(model_data, str):
|
||||
with open(model_data, 'r') as f:
|
||||
model_data = json.load(f)
|
||||
self.model['word'] = model_data['word']
|
||||
self.model.update(model_data)
|
||||
|
||||
def train_model(self, label, data):
|
||||
if label not in self.model:
|
||||
self.model[label] = []
|
||||
unique_words = set([item for sublist in data for item in sublist])
|
||||
self.model['word'] = {word: idx for idx, word in enumerate(unique_words)}
|
||||
self.model[label].extend([[self.model['word'][item] for item in lst] for lst in data])
|
||||
|
||||
def predict(self, new_list):
|
||||
new_list = [self.model['word'].get(item, 1) for item in new_list if item in self.model['word']]
|
||||
for label, items in self.model.items():
|
||||
if label != 'word' and new_list in items:
|
||||
return label
|
||||
return None
|
||||
@@ -0,0 +1,84 @@
|
||||
import time, json, hashlib, numpy
|
||||
from collections import Counter
|
||||
|
||||
class ListSimHash:
|
||||
def __init__(self):
|
||||
self.model = {}
|
||||
|
||||
def save_model(self, file_name):
|
||||
with open(file_name, 'w') as f:
|
||||
json.dump(self.model, f)
|
||||
print(f"\nModel Is Saved In {file_name}")
|
||||
|
||||
def load_model(self, model_data):
|
||||
if isinstance(model_data, str):
|
||||
with open(model_data, 'r') as f:
|
||||
model_data = json.load(f)
|
||||
self.model = model_data
|
||||
|
||||
def get_model(self, label):
|
||||
return self.model[label]
|
||||
|
||||
def train_model(self, label, data):
|
||||
start = time.time()
|
||||
print(f"Convert {label}")
|
||||
if label not in self.model:
|
||||
self.model[label] = []
|
||||
for i, x in enumerate(data, 1):
|
||||
self.model[label].append(self.build_text(x))
|
||||
used = "{0:.2f}".format(time.time()-start)
|
||||
prefix, suffix = f'{i}/{len(data)}:', f'{used}s'
|
||||
self.progress_bar(i, len(data), prefix, suffix)
|
||||
|
||||
def build_text(self, content):
|
||||
sums, batch, count = [], [], 0
|
||||
features = dict(Counter(sorted(content)))
|
||||
for f, w in features.items():
|
||||
count += w
|
||||
batch.append(hashlib.sha256(json.dumps(f).encode('utf-8')).digest() * w)
|
||||
if len(batch) >= 10000:
|
||||
sums.append(self.sum_hashes(batch))
|
||||
batch = []
|
||||
if batch:
|
||||
sums.append(self.sum_hashes(batch))
|
||||
combined_sums = numpy.sum(sums, 0)
|
||||
v = numpy.packbits(combined_sums > count / 2).tobytes()
|
||||
return int.from_bytes(v, 'big')
|
||||
|
||||
def sum_hashes(self, digests):
|
||||
bitarray = numpy.unpackbits(numpy.frombuffer(b''.join(digests), dtype='>B'))
|
||||
return numpy.sum(numpy.reshape(bitarray, (-1, 256)), 0)
|
||||
|
||||
def progress_bar(self, iteration, total, prefix='', suffix='', length=50, fill='█'):
|
||||
percent = min(100.0, max(0.0, 100 * (iteration / float(total))))
|
||||
end_char = '\n' if percent >= 100 else '\r'
|
||||
percent_string = "{0:.2f}".format(percent)
|
||||
filled_length = int(length * iteration // total)
|
||||
bar = fill * filled_length + ' ' * (length - filled_length)
|
||||
print(f'\r {prefix: <10} |{bar}| {percent_string}% {suffix}', end=end_char)
|
||||
|
||||
def predict_all(self, query):
|
||||
label_similarities = {}
|
||||
query_hash = self.build_text(query)
|
||||
for label in self.model:
|
||||
max_similarity = 0
|
||||
for data_point in self.model[label]:
|
||||
similarity = self.similar(data_point, query_hash)
|
||||
if similarity > max_similarity:
|
||||
max_similarity = similarity
|
||||
label_similarities[label] = max_similarity
|
||||
return label_similarities
|
||||
|
||||
def predict(self, query):
|
||||
max_similarity, max_label = 0, None
|
||||
query_hash = self.build_text(query)
|
||||
for label in self.model:
|
||||
for data_point in self.model[label]:
|
||||
similarity = self.similar(data_point, query_hash)
|
||||
if similarity > max_similarity:
|
||||
max_similarity, max_label = similarity, label
|
||||
return max_label, max_similarity
|
||||
|
||||
def similar(self, x, y):
|
||||
hamming_distance = bin(x ^ y).count('1')
|
||||
return 1 - hamming_distance / 256
|
||||
+1
-2
@@ -5,5 +5,4 @@ alist = [".ppt",".pptx",".wps",".txt",".rtf",
|
||||
".pdf",".xls",".xlsx",".doc",".docx",
|
||||
".jpg",".jpeg",".png",".webp",".gif",
|
||||
".mp3",".wav",".aac",".ogg",".flac",
|
||||
".mp4",".avi",".mov",".wmv",".mkv",
|
||||
".zip",".7z",".rar",".tar",".gz"]
|
||||
".mp4",".avi",".mov",".wmv",".mkv"]
|
||||
+8
-7
@@ -4,7 +4,7 @@ translate_dict = {
|
||||
"軟體":"软件","檔案":"文件","確定":"确认","設定":"设置","關於":"关于",
|
||||
"錯誤":"错误","實時":"实时","發現":"发现","關閉":"关闭","裝置":"设备",
|
||||
"總數":"总数","註冊":"注册","圖標":"图标","路徑":"路径","網路":"网络",
|
||||
"位元":"字节","彈窗":"弹窗","靈敏":"敏感","誤報":"误报","複製":"复制",
|
||||
"位元":"字节","彈窗":"弹窗","靈敏":"敏感","複製":"复制","誤":"误","報":"报",
|
||||
"訊":"讯","項":"项","掃":"扫","試":"试","黃":"黄","嗎":"吗","防":"保",
|
||||
"鑰":"钥","當":"当","後":"后","護":"护","導":"导","區":"区","復":"复",
|
||||
"攔":"拦","請":"请","統":"统","刪":"删","進":"进","個":"个","選":"选",
|
||||
@@ -15,17 +15,17 @@ translate_dict = {
|
||||
"詢":"询","製":"制","員":"员","許":"许","條":"条","藍":"蓝","紅":"红",
|
||||
"綠":"绿","態":"态","務":"务","連":"连","檢":"检","測":"测","關":"关",
|
||||
"強":"强","驅":"驱","為":"为","腳":"脚","增":"增","單":"单","開":"开",
|
||||
"惡":"恶"},
|
||||
"惡":"恶","據":"据","庫":"库","經":"经","敗":"败"},
|
||||
"en_US": {"已開啟":"On","已關閉":"Off","已开启":"On","已关闭":"Off",",":", ",
|
||||
"立即刪除":"Delete Now","安全模式":"Safe Mode","官方網站":"Website",
|
||||
"惡意檔案":"Virus ","惡意軟體":"Malware ","勒索軟體":"Ransom ",
|
||||
"惡意檔案":"Virus ","惡意軟體":"Malware ","勒索行為":"Ransom ",
|
||||
"惡意腳本":"Script ","惡意行為":"Behavior ","可疑軟體":"Suspicious ",
|
||||
"許可條款":"License Terms","惡意":"Malicious","可疑":"Suspicious",
|
||||
"啟用":"Enable ","禁用":"Disable ","正在初始化中":"Initializing ",
|
||||
"正在掃描":"Scanning ","正在刪除":"Deleting ","最小化到":"Minimize To ",
|
||||
"確定要":"Sure Want To ","靈敏度":"Sensitivity ","更新":"Update",
|
||||
"確定要":"Sure Want To ","靈敏度":"Sensitivity ","更新":"Update ",
|
||||
"白色主題":"White","黃色主題":"Yellow","紅色主題":"Red","語言":"Language ",
|
||||
"藍色主題":"Blue","綠色主題":"Green","自定主題":"Custom",
|
||||
"藍色主題":"Blue","綠色主題":"Green","自定主題":"Custom","最新":"Latest ",
|
||||
"測試人員":"Testers","版本資訊":"Information","通訊":"Communication ",
|
||||
"註冊表":"Registry ","退出":"Quit ","檔案":"File ","當前":"Currently ",
|
||||
"狀態":"State ","掃描":"Scan ","工具":"Tools ","防護":"Protect ",
|
||||
@@ -47,8 +47,9 @@ translate_dict = {
|
||||
"增加到":"Add File To ","白名單":"White List ","軟體":"Software ",
|
||||
"增加":"Add","移除":"Delete","項目":"Items ","生成":"Create ","稱":"",
|
||||
"複製":"Copy ","開啟":"Open ","此":"This ","並":"And ","全盤":"Full ",
|
||||
"已經在":"Already ","數據庫":"Database ","誤報":"False Positive ",
|
||||
"高":"High ","新":"New ","和":"And ","可能會":"Possible ","會":"Can ",
|
||||
"日誌":"Log","已":"Has ","彈窗":"Window ","要":"To ","驅動":"Drive ",
|
||||
"的":"","顯示":"","個":"","嗎":"","中":"","了":"","行":"","立即":"",
|
||||
"密鑰":"Key ","檢測":"Detect ","誤報":"False Positive ","製作":"Make",
|
||||
" ":" "}}
|
||||
"密鑰":"Key ","檢測":"Detect ","製作":"Make","運行":"Running",
|
||||
"自動":"Auto ","上報":"Report ","分析":"Analysis "," ":" "}}
|
||||
Binary file not shown.
+2
-1
@@ -1,7 +1,8 @@
|
||||
import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.0.1',
|
||||
version='3.0.9',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
# PYAS (Python Antivirus Software)
|
||||
|
||||

|
||||

|
||||
|
||||
## Readme Language
|
||||
|
||||
[繁體中文](https://github.com/zackzheng1121/PYAS/blob/main/README_zh.md)
|
||||
|
||||
## File Information
|
||||
|
||||
PYAS.py -> Main PYAS Program (including animation, scanning, protection functions, etc.)
|
||||
|
||||
PYAS_Compress.py -> Conversion Database (used to convert database dict and list to and from each other)
|
||||
PYAS_Engine.py -> Conversion Database (used to convert database dict and list to and from each other)
|
||||
|
||||
PYAS_Extension.py -> File Extension (including scanned file extensions and common file extensions)
|
||||
|
||||
PYAS_Function.* -> Compress Database (need to use zip to decompress and put it in the same directory)
|
||||
PYAS_Model.* -> Virus Database (the virus database must be placed in the same directory)
|
||||
|
||||
PYAS_Interface.py -> PyQt5 Interface (designed by QT designer, must be matched with the main program)
|
||||
|
||||
@@ -33,35 +37,6 @@ pywin32==306
|
||||
PyQt5==5.15.9
|
||||
```
|
||||
|
||||
## Hash Scanning
|
||||
|
||||
Use Qihoo 360 cloud service to scan known malicious files
|
||||
|
||||
```
|
||||
import hashlib, requests
|
||||
import xml.etree.ElementTree as xmlet
|
||||
|
||||
def hash_scan(file):
|
||||
try:
|
||||
with open(file, "rb") as f:
|
||||
text = str(hashlib.md5(f.read()).hexdigest())
|
||||
strBody = f'-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="md5s"\r\n\r\n{text}\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="format"\r\n\r\nXML\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="product"\r\n\r\n360zip\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="combo"\r\n\r\n360zip_main\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="v"\r\n\r\n2\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="osver"\r\n\r\n5.1\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="vk"\r\n\r\na03bc211\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="mid"\r\n\r\n8a40d9eff408a78fe9ec10a0e7e60f62\r\n-------------------------------7d83e2d7a141e--'
|
||||
response = requests.post('http://qup.f.360.cn/file_health_info.php', data=strBody, timeout=3)
|
||||
if response.status_code == 200:
|
||||
level = float(xmlet.fromstring(response.text).find('.//e_level').text)
|
||||
if level > 50:
|
||||
return "Virus"
|
||||
elif level <= 10:
|
||||
return "Safe"
|
||||
return "Unknown"
|
||||
return f"Error: {response.status_code}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
info = hash_scan("path to file")
|
||||
print(info)
|
||||
```
|
||||
|
||||
## Pefile Scanning
|
||||
|
||||
Get the pefile file function import table for scanning
|
||||
@@ -113,6 +88,8 @@ def proc_detect():
|
||||
print(f"File: {file}")
|
||||
print(f"Pid: {p.pid}")
|
||||
print(f"Cmd: {cmd}")
|
||||
elif not psutil.pid_exists(p.pid):
|
||||
existing_processes.remove(p.pid)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
# PYAS (Python 防毒軟體)
|
||||
|
||||

|
||||
|
||||
## 文件訊息
|
||||
|
||||
PYAS.py -> PYAS主程式(包括動畫、掃描、保護功能等)
|
||||
|
||||
PYAS_Engine.py -> 轉換資料庫(用於相互轉換資料庫字典和清單)
|
||||
|
||||
PYAS_Extension.py -> 檔案副檔名(包括掃描檔案副檔名和常用檔案副檔名)
|
||||
|
||||
PYAS_Model.* -> 病毒庫(病毒庫必須放在同一目錄下)
|
||||
|
||||
PYAS_Interface.py -> PyQt5 Interface(由QT設計師設計,必須與主程式相符)
|
||||
|
||||
PYAS_Resource.py -> PyQt5資源(狀態圖片、圖示包、按鈕圖示等)
|
||||
|
||||
PYAS_Language.py -> 翻譯字典(繁體中文、簡體中文、英文)
|
||||
|
||||
PYAS_Version.py -> Pyinstaller Info(檔案資訊、版本資訊、原名等)
|
||||
|
||||
## 要求
|
||||
|
||||
使用 pip install requirements 來安裝需要的模組
|
||||
|
||||
```
|
||||
psutil==5.9.5
|
||||
pefile==2023.2.7
|
||||
requests==2.31.0
|
||||
pyperclip==1.8.2
|
||||
pywin32==306
|
||||
PyQt5==5.15.9
|
||||
```
|
||||
|
||||
## 哈希掃描
|
||||
|
||||
使用奇虎360雲端服務掃描已知惡意文件
|
||||
|
||||
```
|
||||
import hashlib, requests
|
||||
import xml.etree.ElementTree as xmlet
|
||||
|
||||
def hash_scan(file):
|
||||
try:
|
||||
with open(file, "rb") as f:
|
||||
text = str(hashlib.md5(f.read()).hexdigest())
|
||||
strBody = f'-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="md5s"\r\n\r\n{text}\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="format"\r\n\r\nXML\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="product"\r\n\r\n360zip\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="combo"\r\n\r\n360zip_main\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="v"\r\n\r\n2\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="osver"\r\n\r\n5.1\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="vk"\r\n\r\na03bc211\r\n-------------------------------7d83e2d7a141e\r\nContent-Disposition: form-data; name="mid"\r\n\r\n8a40d9eff408a78fe9ec10a0e7e60f62\r\n-------------------------------7d83e2d7a141e--'
|
||||
response = requests.post('http://qup.f.360.cn/file_health_info.php', data=strBody, timeout=3)
|
||||
if response.status_code == 200:
|
||||
level = float(xmlet.fromstring(response.text).find('.//e_level').text)
|
||||
if level > 50:
|
||||
return "Virus"
|
||||
elif level <= 10:
|
||||
return "Safe"
|
||||
return "Unknown"
|
||||
return f"Error: {response.status_code}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
info = hash_scan("path to file")
|
||||
print(info)
|
||||
```
|
||||
|
||||
## 文件掃描
|
||||
|
||||
取得pefile檔案函數導入表進行掃描
|
||||
|
||||
```
|
||||
import pefile
|
||||
|
||||
def pe_scan(file):
|
||||
try:
|
||||
fn = []
|
||||
with pefile.PE(file) as pe:
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
for func in entry.imports:
|
||||
try:
|
||||
fn.append(str(func.name, "utf-8"))
|
||||
except:
|
||||
pass
|
||||
if fn in known_malicious_list:
|
||||
return "Virus"
|
||||
return "Safe"
|
||||
return "Unknown"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
info = pe_scan("path to file")
|
||||
print(info)
|
||||
```
|
||||
|
||||
## 進程偵測
|
||||
|
||||
顯示新進程名稱、檔案路徑、cmd 行、pid
|
||||
```
|
||||
import psutil, time
|
||||
|
||||
def proc_detect():
|
||||
existing_processes = set()
|
||||
for p in psutil.process_iter():
|
||||
if p.pid not in existing_processes:
|
||||
existing_processes.add(p.pid)
|
||||
while True:
|
||||
time.sleep(0.1)
|
||||
for p in psutil.process_iter():
|
||||
try:
|
||||
if p.pid not in existing_processes:
|
||||
existing_processes.add(p.pid)
|
||||
name, file, cmd = p.name(), p.exe(), p.cmdline()
|
||||
print(f"Name: {name}")
|
||||
print(f"File: {file}")
|
||||
print(f"Pid: {p.pid}")
|
||||
print(f"Cmd: {cmd}")
|
||||
except:
|
||||
pass
|
||||
|
||||
proc_detect()
|
||||
```
|
||||
|
||||
## 檔案偵測
|
||||
|
||||
監控指定路徑下的檔案變化
|
||||
```
|
||||
import os, win32file, win32con
|
||||
|
||||
def file_detect(path):
|
||||
hDir = win32file.CreateFile(path,win32con.GENERIC_READ,win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE|win32con.FILE_SHARE_DELETE,None,win32con.OPEN_EXISTING,win32con.FILE_FLAG_BACKUP_SEMANTICS,None)
|
||||
while True:
|
||||
for action, file in win32file.ReadDirectoryChangesW(hDir,10485760,True,win32con.FILE_NOTIFY_CHANGE_FILE_NAME|win32con.FILE_NOTIFY_CHANGE_DIR_NAME|win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES|win32con.FILE_NOTIFY_CHANGE_SIZE|win32con.FILE_NOTIFY_CHANGE_LAST_WRITE|win32con.FILE_NOTIFY_CHANGE_SECURITY,None,None):
|
||||
try:
|
||||
fpath = os.path.join(path, file)
|
||||
if action == 1:
|
||||
print(f"File Create: {fpath}")
|
||||
elif action == 2:
|
||||
print(f"File Delete: {fpath}")
|
||||
elif action == 3:
|
||||
print(f"File Modify: {fpath}")
|
||||
elif action == 4:
|
||||
print(f"File Rename: {fpath}")
|
||||
elif action == 5:
|
||||
print(f"File Rename: {fpath}")
|
||||
except:
|
||||
pass
|
||||
|
||||
file_detect("path")
|
||||
```
|
||||
|
||||
## 官方網頁
|
||||
|
||||
https://pyantivirus.wixsite.com/pyas
|
||||
|
||||
https://github.com/87owo/PYAS
|
||||
|
||||
## 開源許可證
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.md
|
||||
|
||||
## 支援的系統
|
||||
|
||||
Windows 8.1, 10, 11 (64-bit),記憶體 1GB, 磁碟容量 1GB 或更高
|
||||
Other systems may experience software malfunctions or crash
|
||||
|
||||
## Special Thanks
|
||||
360, Wix, VirusShare, mtkiao129, AV-T Team of LisectGroup
|
||||
|
||||
Copyright© 2020~2024 PYAS Security By 87owo
|
||||
Reference in new issue
Block a user