Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
949f74ab04 | ||
|
|
ef68a89b45 | ||
|
|
10ecb35cca | ||
|
|
696ac22f77 | ||
|
|
8d13f98030 | ||
|
|
1dddf13a9d | ||
|
|
855472239a | ||
|
|
dc5d317623 | ||
|
|
91011e9e2a | ||
|
|
bb09b75d3b | ||
|
|
b2f9adad66 | ||
|
|
0c2c84d2a9 | ||
|
|
1733e7cc86 | ||
|
|
831d9a9895 | ||
|
|
70ba3a629d | ||
|
|
29e9702d21 | ||
|
|
f8ef756bd4 | ||
|
|
233b28e8a5 | ||
|
|
da77c17be5 | ||
|
|
8dfa76822e | ||
|
|
4163991b66 | ||
|
|
78323c9a08 | ||
|
|
eb517e86f3 | ||
|
|
e24f3823cb | ||
|
|
fd3e8ee28a | ||
|
|
a980ea7bd6 | ||
|
|
20ce73c54f | ||
|
|
d8f22f7f28 | ||
|
|
591055edaf | ||
|
|
fbf0e06c04 | ||
|
|
9daf327e80 | ||
|
|
4441978e10 | ||
|
|
d8eb770735 | ||
|
|
cefbf79c6e | ||
|
|
fc8762094e | ||
|
|
801c3aadcc | ||
|
|
4296f1ec43 | ||
|
|
a7f469747c | ||
|
|
68d0352343 | ||
|
|
01e6d09f7c | ||
|
|
6582abfd4a | ||
|
|
f8f4b303d7 | ||
|
|
79f05a23a8 | ||
|
|
95845c5c85 | ||
|
|
cd1a24290e | ||
|
|
21261bfb17 | ||
|
|
79abf51cb8 |
No files matched your search
+5
-10
@@ -9,11 +9,7 @@ def preprocess_image(file_data, size, channels=1):
|
||||
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
|
||||
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
|
||||
img[:len(file_data)] = arr
|
||||
|
||||
if channels == 1:
|
||||
image = Image.fromarray(img.reshape((wah, wah)), 'L')
|
||||
else:
|
||||
image = Image.fromarray(img.reshape((wah, wah, channels)), 'RGB')
|
||||
image = Image.fromarray(img.reshape((wah, wah)), 'L')
|
||||
return image.resize((width, height), Image.Resampling.NEAREST)
|
||||
|
||||
####################################################################################################
|
||||
@@ -27,9 +23,7 @@ def is_text_file(content, sample_size=1024):
|
||||
return nontext / len(raw) < 0.15
|
||||
|
||||
def get_type(file_path):
|
||||
suffix = {
|
||||
".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys",
|
||||
".bat", ".cmd", ".js", ".php", ".ps1", ".vbs", ".wsf"}
|
||||
suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui", ".cpl"}
|
||||
|
||||
match_data = {}
|
||||
if not os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
@@ -38,7 +32,8 @@ def get_type(file_path):
|
||||
with pefile.PE(file_path, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
if section.Characteristics & 0x00000020:
|
||||
data = section.get_data()
|
||||
if data:
|
||||
match_data[name] = section.get_data()
|
||||
except pefile.PEFormatError:
|
||||
with open(file_path, 'rb') as f:
|
||||
@@ -68,6 +63,6 @@ def file_to_images(input_dir, output_dir):
|
||||
####################################################################################################
|
||||
|
||||
input_directory = input('Enter the folder path: ')
|
||||
output_directory = "./Image_File"
|
||||
output_directory = "./Image_Output"
|
||||
file_to_images(input_directory, output_directory)
|
||||
input('Conversion Complete')
|
||||
@@ -30,5 +30,5 @@ def dedup_realtime(root):
|
||||
####################################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
dedup_realtime(r".\Image_File")
|
||||
dedup_realtime(input("Input file path: "))
|
||||
input('Deduplicate Complete')
|
||||
+81
-50
@@ -2,6 +2,7 @@ import tensorflow as tf
|
||||
import os, tf2onnx, numpy, random
|
||||
from PIL import Image
|
||||
from tensorflow.keras import *
|
||||
from collections import Counter
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -10,36 +11,10 @@ tf.keras.mixed_precision.set_global_policy('mixed_float16')
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def get_file_list(directory):
|
||||
file_paths, labels, class_indices = [], [], {}
|
||||
for i, cls in enumerate(sorted([d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d))])):
|
||||
class_indices[cls] = i
|
||||
cls_dir = os.path.join(directory, cls)
|
||||
for root, _, files in os.walk(cls_dir):
|
||||
for file in files:
|
||||
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
|
||||
file_paths.append(os.path.join(root, file))
|
||||
labels.append(i)
|
||||
return file_paths, labels, class_indices
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def parse_fn(filename, label, image_size, channels, num_classes):
|
||||
img = tf.io.read_file(filename)
|
||||
img = tf.image.decode_image(img, channels=channels, expand_animations=False)
|
||||
img = tf.cond(
|
||||
tf.reduce_all(tf.equal(tf.shape(img)[:2], image_size)),
|
||||
lambda: tf.cast(img, tf.float32),
|
||||
lambda: tf.image.resize(img, image_size))
|
||||
img = tf.cast(img, tf.float32) / 255.0
|
||||
label = tf.one_hot(label, depth=num_classes)
|
||||
return img, label
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=128, color_mode="grayscale"):
|
||||
def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=256, color_mode="grayscale"):
|
||||
channels = 1 if color_mode == "grayscale" else 3
|
||||
file_paths, labels, class_indices = get_file_list(data_dir)
|
||||
|
||||
combined = list(zip(file_paths, labels))
|
||||
random.shuffle(combined)
|
||||
file_paths[:], labels[:] = zip(*combined)
|
||||
@@ -64,11 +39,35 @@ def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def parse_fn(filename, label, image_size, channels, num_classes):
|
||||
img = tf.io.read_file(filename)
|
||||
img = tf.image.decode_image(img, channels=channels, expand_animations=False)
|
||||
img = tf.cond(tf.reduce_all(tf.equal(tf.shape(img)[:2], image_size)), lambda: tf.cast(img, tf.float32), lambda: tf.image.resize(img, image_size))
|
||||
img = tf.cast(img, tf.float32) / 255.0
|
||||
label = tf.one_hot(label, depth=num_classes)
|
||||
return img, label
|
||||
|
||||
def get_file_list(directory):
|
||||
file_paths, labels, class_indices = [], [], {}
|
||||
for i, cls in enumerate(sorted([d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d))])):
|
||||
class_indices[cls] = i
|
||||
cls_dir = os.path.join(directory, cls)
|
||||
|
||||
for root, _, files in os.walk(cls_dir):
|
||||
for file in files:
|
||||
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
|
||||
file_paths.append(os.path.join(root, file))
|
||||
labels.append(i)
|
||||
return file_paths, labels, class_indices
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def multi_conv(x, strides):
|
||||
convs = []
|
||||
for k in [1, 3, 5, 7]:
|
||||
for k in [(1, 7), (1, 1), (3, 3), (5, 5)]:
|
||||
c = layers.DepthwiseConv2D(kernel_size=k, strides=strides, padding='same', activation='gelu')(x)
|
||||
convs.append(c)
|
||||
|
||||
x = layers.Concatenate()(convs)
|
||||
x = layers.BatchNormalization()(x)
|
||||
return layers.Activation('gelu')(x)
|
||||
@@ -78,12 +77,27 @@ def gated_conv(x, strides):
|
||||
g = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='sigmoid')(x)
|
||||
return layers.Multiply()([c, g])
|
||||
|
||||
def se_block(x, reduction):
|
||||
def coordinate_attention(x, reduction):
|
||||
input_shape = tf.shape(x)
|
||||
h, w = input_shape[1], input_shape[2]
|
||||
filters = x.shape[-1]
|
||||
s = layers.Conv2D(filters // reduction, 1, padding='same', activation='gelu')(x)
|
||||
s = layers.BatchNormalization()(s)
|
||||
s = layers.Conv2D(filters, 1, padding='same', activation='sigmoid')(s)
|
||||
return layers.multiply([x, s])
|
||||
mip = max(8, filters // reduction)
|
||||
|
||||
x_h = layers.Lambda(lambda t: tf.reduce_mean(t, axis=2, keepdims=True))(x)
|
||||
x_w = layers.Lambda(lambda t: tf.reduce_mean(t, axis=1, keepdims=True))(x)
|
||||
x_w_t = layers.Permute((2, 1, 3))(x_w)
|
||||
|
||||
x_cat = layers.Concatenate(axis=1)([x_h, x_w_t])
|
||||
x_cat = layers.Conv2D(mip, 1, padding='valid', use_bias=False)(x_cat)
|
||||
x_cat = layers.BatchNormalization()(x_cat)
|
||||
x_cat = layers.Activation('gelu')(x_cat)
|
||||
|
||||
x_cat_h, x_cat_w = layers.Lambda(lambda t: tf.split(t[0], [t[1], t[2]], axis=1))([x_cat, h, w])
|
||||
x_h_sigmoid = layers.Conv2D(filters, 1, padding='valid', activation='sigmoid')(x_cat_h)
|
||||
x_cat_w = layers.Permute((2, 1, 3))(x_cat_w)
|
||||
x_w_sigmoid = layers.Conv2D(filters, 1, padding='valid', activation='sigmoid')(x_cat_w)
|
||||
|
||||
return layers.Multiply()([x, x_h_sigmoid, x_w_sigmoid])
|
||||
|
||||
def build_model(input_shape, num_classes):
|
||||
i = layers.Input(shape=input_shape)
|
||||
@@ -93,40 +107,57 @@ def build_model(input_shape, num_classes):
|
||||
x = multi_conv(x, strides=2)
|
||||
x = multi_conv(x, strides=2)
|
||||
x = multi_conv(x, strides=2)
|
||||
x = se_block(x, reduction=2)
|
||||
|
||||
x = coordinate_attention(x, reduction=4)
|
||||
x = layers.GlobalMaxPooling2D()(x)
|
||||
|
||||
x = layers.Dense(x.shape[-1] // 2, activation='gelu', dtype='float32')(x)
|
||||
o = layers.Dense(num_classes, activation='softmax', dtype='float32')(x)
|
||||
return models.Model(i, o)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def lr_scheduler(epoch, lr):
|
||||
return 1e-4 * (0.95 ** epoch)
|
||||
def calculate_lr_cosine_sq(epoch, total_epochs=25, lr_start=5e-4, lr_end=1e-6):
|
||||
cos_val = 0.5 * (1 + numpy.cos(epoch / total_epochs * numpy.pi))
|
||||
return lr_end + (lr_start - lr_end) * (cos_val ** 2)
|
||||
|
||||
def lr_scheduler(epoch):
|
||||
return calculate_lr_cosine_sq(epoch)
|
||||
|
||||
def categorical_focal_loss(gamma=2.0, alpha=0.25):
|
||||
def focal_loss(y_true, y_pred):
|
||||
y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1.0 - tf.keras.backend.epsilon())
|
||||
cross_entropy = -y_true * tf.math.log(y_pred)
|
||||
loss = alpha * tf.math.pow(1.0 - y_pred, gamma) * cross_entropy
|
||||
return tf.reduce_sum(loss, axis=-1)
|
||||
return focal_loss
|
||||
|
||||
class CustomModelCheckpoint(callbacks.Callback):
|
||||
def on_epoch_end(self, epoch, logs=False):
|
||||
models.save_model(self.model, f"PYAS_Model_Epoch_{epoch + 1}.h5")
|
||||
spec = (tf.TensorSpec((None,) + train_ds.image_shape, tf.float32),)
|
||||
model_proto, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=18)
|
||||
model_proto, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=17)
|
||||
with open(f"PYAS_Model_Epoch_{epoch + 1}.onnx", "wb") as f:
|
||||
f.write(model_proto.SerializeToString())
|
||||
|
||||
####################################################################################################
|
||||
|
||||
train_ds, val_ds = load_dataset(r'.\Image_File')
|
||||
train_ds, val_ds = load_dataset(r'.\Image_File_Pefile')
|
||||
for imgs, _ in train_ds.take(1):
|
||||
train_ds.image_shape = imgs.shape[1:]
|
||||
break
|
||||
|
||||
strategy = tf.distribute.MirroredStrategy(cross_device_ops=tf.distribute.HierarchicalCopyAllReduce())
|
||||
with strategy.scope():
|
||||
try:
|
||||
model = models.load_model('PYAS_Model_Epoch_0.h5')
|
||||
print("Load model from disk")
|
||||
except:
|
||||
print("Creating a new model")
|
||||
model = build_model(train_ds.image_shape, len(train_ds.class_indices))
|
||||
print(f"Total parameters: {model.count_params()}")
|
||||
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
|
||||
model.fit(train_ds, epochs=30, callbacks=[CustomModelCheckpoint(), callbacks.LearningRateScheduler(lr_scheduler)], validation_data=val_ds)
|
||||
try:
|
||||
model = models.load_model('PYAS_Model_Epoch_0.h5')
|
||||
print("Load model from disk")
|
||||
except:
|
||||
print("Creating a new model")
|
||||
model = build_model(train_ds.image_shape, len(train_ds.class_indices))
|
||||
|
||||
print(f"Total parameters: {model.count_params()}")
|
||||
model.compile(optimizer='adam', loss=categorical_focal_loss(gamma=2.0, alpha=0.25),
|
||||
metrics=['accuracy', metrics.Precision(name='precision'), metrics.Recall(name='recall'),
|
||||
metrics.FalsePositives(name='fp'), metrics.TrueNegatives(name='tn')])
|
||||
|
||||
model.fit(train_ds, epochs=25, callbacks=[CustomModelCheckpoint(), callbacks.LearningRateScheduler(lr_scheduler)], validation_data=val_ds)
|
||||
input('Training Complete')
|
||||
+24
-15
@@ -1,22 +1,31 @@
|
||||
87owo software and source code Development License Agreement
|
||||
Last updated: 2025-09-09
|
||||
87owo Publicly Available Source Code License Agreement
|
||||
Last updated: 2025-12-19
|
||||
|
||||
1. License and Ownership
|
||||
– A single device key may be required to activate the software or source code.
|
||||
– 87owo reserves all rights. Intellectual property rights are not transferred.
|
||||
– All project script archives and executable files are proprietary.
|
||||
- This software and its source code are source available for public view.
|
||||
- Access or execution of certain features may require an activation key.
|
||||
- 87owo reserves all legal rights, no intellectual property is transferred.
|
||||
- All project archives, source code, and binaries remain fully proprietary.
|
||||
|
||||
2. Use and Prohibitions
|
||||
– Reverse engineering, removal of licenses, or use for illegal purposes is prohibited.
|
||||
– Personal internal use or modification of the source code is permitted.
|
||||
– Redistribution, etc. requires prior authorization from the original author.
|
||||
- You may view, download, and modify the code for local and personal use.
|
||||
- Redistribution of any original or modified code requires prior consent.
|
||||
- Commercial hosting or SaaS deployment is prohibited without permission.
|
||||
- Reverse engineering, license removal, or illegal use is strictly barred.
|
||||
|
||||
3. Updates and Terms
|
||||
– New versions and updates may include patches, enhancements, or modifications.
|
||||
– Continued use constitutes your acceptance of these features or license.
|
||||
3. Data and Privacy
|
||||
- This project may collect operational data for optimization and analysis.
|
||||
- All collected data is strictly anonymized to ensure the user's privacy.
|
||||
- Transmission of information is encrypted to prevent unauthorized access.
|
||||
|
||||
4. Warranty and Liability
|
||||
– This software or code is provided "as is" without warranty of any kind.
|
||||
– In no event will we be liable for any direct or indirect damages.
|
||||
4. Updates and Terms
|
||||
- New releases may include critical patches, enhancements, or UI changes.
|
||||
- Continued use of these services constitutes acceptance of updated terms.
|
||||
- The author reserves the right to modify these license terms at any time.
|
||||
|
||||
© 2020–2025 87owo (PYAS Security). All rights reserved.
|
||||
5. Warranty and Liability
|
||||
- All services and assets are provided "as is" without any form of warranty.
|
||||
- 87owo is not liable for any direct or indirect damages from software use.
|
||||
- Users assume all risks regarding performance, data, and system integrity.
|
||||
|
||||
© 2020-2026 87owo (PYAS Security). All rights reserved.
|
||||
@@ -129,11 +129,11 @@ class MainWindow_Controller(QMainWindow):
|
||||
"traditional_switch", "simplified_switch", "english_switch",
|
||||
]
|
||||
self.pyas_default = {
|
||||
"version": "3.3.6",
|
||||
"version": "3.3.7",
|
||||
"product": "00000-00000-00000-00000-00000",
|
||||
"language": "english_switch",
|
||||
"theme": "white_switch",
|
||||
"sensitive": 95,
|
||||
"sensitive": 90,
|
||||
"process_switch": True,
|
||||
"document_switch": True,
|
||||
"system_switch": True,
|
||||
@@ -297,7 +297,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def create_shadow(self):
|
||||
shadow = QGraphicsDropShadowEffect(self)
|
||||
shadow.setOffset(0, 0)
|
||||
shadow.setBlurRadius(20)
|
||||
shadow.setBlurRadius(15)
|
||||
shadow.setColor(QColor(175, 175, 175))
|
||||
return shadow
|
||||
|
||||
@@ -357,7 +357,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def save_state(self, name, checked):
|
||||
self.pyas_config[name] = bool(checked)
|
||||
if name == "sensitive_switch":
|
||||
self.pyas_config["sensitive"] = 0 if checked else 95
|
||||
self.pyas_config["sensitive"] = 0 if checked else 90
|
||||
elif name == "extension_switch":
|
||||
pass
|
||||
elif name == "process_switch":
|
||||
@@ -833,7 +833,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
return False
|
||||
|
||||
label, level = self.model.model_scan(file_path)
|
||||
if label and level >= self.pyas_config.get("sensitive", 95):
|
||||
if label and level >= self.pyas_config.get("sensitive", 90):
|
||||
return f"{label}.{level}"
|
||||
|
||||
if self.pyas_config.get("extension_switch", False):
|
||||
@@ -1521,7 +1521,15 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def website_button(self):
|
||||
try:
|
||||
webbrowser.open("https://github.com/87owo/PYAS")
|
||||
webbrowser.open("https://pyas-security.com/antivirus")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
return False
|
||||
|
||||
def cloud_button(self):
|
||||
try:
|
||||
webbrowser.open("https://pyas-security.com/analyze")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
@@ -1760,17 +1768,20 @@ class MainWindow_Controller(QMainWindow):
|
||||
bytes_returned = ctypes.wintypes.DWORD()
|
||||
res = self.kernel32.ReadDirectoryChangesW(hDir, buffer, ctypes.sizeof(buffer), True,
|
||||
0x0000001F, ctypes.byref(bytes_returned), None, None)
|
||||
if not res:
|
||||
continue
|
||||
|
||||
if res:
|
||||
notify = FILE_NOTIFY_INFORMATION.from_buffer_copy(buffer)
|
||||
raw_filename = notify.FileName[:notify.FileNameLength // 2]
|
||||
file_path = self.norm_path(os.path.join(self.path_user, raw_filename))
|
||||
notify = FILE_NOTIFY_INFORMATION.from_buffer_copy(buffer)
|
||||
raw_filename = notify.FileName[:notify.FileNameLength // 2]
|
||||
if not raw_filename:
|
||||
continue
|
||||
|
||||
if notify.Action in [2, 3, 4] and not self.is_in_whitelist(file_path):
|
||||
state = self.scan_engine(file_path)
|
||||
if state:
|
||||
self.add_to_quarantine([file_path])
|
||||
self.send_message(f"檔案防護 | 靜態掃描攔截 | None | {file_path} | None", "notify", True)
|
||||
file_path = self.norm_path(os.path.join(self.path_user, raw_filename), must_exist=True)
|
||||
if file_path and notify.Action in [2, 3, 4] and not self.is_in_whitelist(file_path):
|
||||
state = self.scan_engine(file_path)
|
||||
if state:
|
||||
self.add_to_quarantine([file_path])
|
||||
self.send_message(f"檔案防護 | 靜態掃描攔截 | None | {file_path} | None", "notify", True)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
self.kernel32.CloseHandle(hDir)
|
||||
|
||||
+7
-7
@@ -117,13 +117,13 @@ translate_dict = {
|
||||
"_white": "_black"
|
||||
},
|
||||
"black_switch": {
|
||||
"255, 255, 255": "55, 55, 55",
|
||||
"245, 245, 245": "75, 75, 75",
|
||||
"235, 235, 235": "65, 65, 65",
|
||||
"240, 240, 240": "80, 80, 80",
|
||||
"250, 250, 250": "90, 90, 90",
|
||||
"220, 255, 220": "80, 80, 80",
|
||||
"210, 255, 210": "90, 90, 90",
|
||||
"255, 255, 255": "20, 20, 20",
|
||||
"245, 245, 245": "40, 40, 40",
|
||||
"235, 235, 235": "30, 30, 30",
|
||||
"240, 240, 240": "70, 70, 70",
|
||||
"250, 250, 250": "80, 80, 80",
|
||||
"220, 255, 220": "70, 70, 70",
|
||||
"210, 255, 210": "80, 80, 80",
|
||||
"0, 0, 0": "255, 255, 255",
|
||||
"_black": "_white"
|
||||
}
|
||||
|
||||
+43
-40
@@ -109,9 +109,9 @@ class rule_scanner:
|
||||
class model_scanner:
|
||||
def __init__(self):
|
||||
self.model = None
|
||||
self.suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".bat", ".cmd", ".js", ".php", ".ps1", ".vbs", ".wsf"}
|
||||
self.labels = ["Pefile/White", "Script/White", "Pefile/General", "Script/General"]
|
||||
self.detect_set = {"Pefile/General", "Script/General"}
|
||||
self.suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys"}
|
||||
self.labels = ["Pefile/White", "Pefile/General"]
|
||||
self.detect_set = {"Pefile/General"}
|
||||
self.resize = (224, 224)
|
||||
|
||||
def load_path(self, path):
|
||||
@@ -127,44 +127,55 @@ class model_scanner:
|
||||
return False
|
||||
|
||||
def model_scan(self, file_path, full_output=False):
|
||||
if not self.model or not file_path.exists():
|
||||
if not self.model:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
sections = self.extract_sections(file_path)
|
||||
if not sections:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
images = [self.preprocess_image(data, self.resize) for data in sections.values()]
|
||||
if not images:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
arr = numpy.stack([numpy.asarray(img).astype('float32') / 255.0 for img in images])[:, :, :, None]
|
||||
input_shape = self.model.get_inputs()[0].shape
|
||||
if len(input_shape) == 4 and input_shape[1] in (1, 3):
|
||||
arr = arr.transpose(0, 3, 1, 2)
|
||||
arr = numpy.stack([numpy.asarray(img).astype('float32') / 255.0 for img in images])
|
||||
if arr.ndim == 3:
|
||||
arr = numpy.expand_dims(arr, axis=-1)
|
||||
|
||||
input_name = self.model.get_inputs()[0].name
|
||||
preds = self.model.run(None, {input_name: arr})[0]
|
||||
exp = numpy.exp(preds - numpy.max(preds, axis=1, keepdims=True))
|
||||
probs = exp / numpy.sum(exp, axis=1, keepdims=True)
|
||||
input_meta = self.model.get_inputs()[0]
|
||||
input_name = input_meta.name
|
||||
input_shape = input_meta.shape
|
||||
|
||||
max_overall = max(numpy.max(row) for row in probs)
|
||||
if len(input_shape) == 4:
|
||||
if input_shape[1] in (1, 3) and input_shape[3] not in (1, 3):
|
||||
arr = arr.transpose(0, 3, 1, 2)
|
||||
try:
|
||||
probs = self.model.run(None, {input_name: arr})[0]
|
||||
except Exception:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
results = []
|
||||
best_malicious = None
|
||||
|
||||
for name, pred, img in zip(sections.keys(), probs, images):
|
||||
idx = int(numpy.argmax(pred))
|
||||
label = self.labels[idx]
|
||||
conf = round((pred[idx] / max_overall) * 100, 2)
|
||||
label = self.labels[idx] if idx < len(self.labels) else f"Class_{idx}"
|
||||
conf = round(float(pred[idx]) * 100, 2)
|
||||
print(f"Section: {name} | Label: {label} | Confidence: {conf}%")
|
||||
|
||||
if full_output:
|
||||
results.append((name, label, conf, self.pil_to_base64(img)))
|
||||
elif label in self.detect_set:
|
||||
return label, int(conf)
|
||||
if label in self.detect_set:
|
||||
if best_malicious is None or conf > best_malicious[1]:
|
||||
best_malicious = (label, int(conf))
|
||||
|
||||
if full_output:
|
||||
results.sort(key=lambda x: (x[1] not in self.detect_set, -x[2]))
|
||||
malicious_count = sum(1 for _, lbl, _, _ in results if lbl in self.detect_set)
|
||||
total = len(results)
|
||||
status = f"{malicious_count}/{total}"
|
||||
return results, str(file_path), status
|
||||
return False, False
|
||||
return results, str(file_path), f"{malicious_count}/{total}"
|
||||
|
||||
return best_malicious if best_malicious else (False, False)
|
||||
|
||||
def pil_to_base64(self, img):
|
||||
buf = BytesIO()
|
||||
@@ -180,26 +191,18 @@ class model_scanner:
|
||||
image = Image.fromarray(imgbuf.reshape((wah, wah)), 'L')
|
||||
return image.resize((width, height), Image.Resampling.NEAREST)
|
||||
|
||||
def is_text_file(self, content, sample_size=1024):
|
||||
raw = content[:sample_size]
|
||||
if not raw:
|
||||
return False
|
||||
text_char = set(range(32, 127)) | {9, 10, 13}
|
||||
nontext = sum(b not in text_char for b in raw)
|
||||
return nontext / len(raw) < 0.15
|
||||
|
||||
def extract_sections(self, file_path):
|
||||
match_data = {}
|
||||
try:
|
||||
with pefile.PE(file_path, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
if section.Characteristics & 0x00000020:
|
||||
match_data[name] = section.get_data()
|
||||
except Exception:
|
||||
with open(file_path, 'rb') as f:
|
||||
content = f.read()
|
||||
ext = file_path.suffix.lower()
|
||||
if ext in self.suffix or self.is_text_file(content):
|
||||
match_data[ext] = content
|
||||
fpath_str = str(file_path)
|
||||
ext = os.path.splitext(fpath_str)[-1].lower()
|
||||
if ext in self.suffix:
|
||||
try:
|
||||
with pefile.PE(fpath_str, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1', errors='ignore').lower()
|
||||
data = section.get_data()
|
||||
if data:
|
||||
match_data[name] = data
|
||||
except Exception:
|
||||
pass
|
||||
return match_data
|
||||
+33
-30
@@ -1437,8 +1437,8 @@ class Ui_main_window(object):
|
||||
self.info_text.setHtml(QCoreApplication.translate("main_window", u"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>", None))
|
||||
"</style></head><body style=\" font-family:'PMingLiU'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';\"><br /></p></body></html>", None))
|
||||
self.sensitive_title.setText(QCoreApplication.translate("main_window", u"\u589e\u5f37\u6a21\u5f0f", None))
|
||||
self.sensitive_text.setText(QCoreApplication.translate("main_window", u"\u6b64\u9078\u9805\u53ef\u4ee5\u589e\u5f37\u6383\u63cf\u5f15\u64ce", None))
|
||||
self.sensitive_switch.setText(QCoreApplication.translate("main_window", u"\u95dc\u9589", None))
|
||||
@@ -1477,39 +1477,42 @@ class Ui_main_window(object):
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:'Microsoft YaHei'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">PYAS Security Software Development License Agreement</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Last updated: 2025-08-15</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">87owo Publicly Available Source Code License Agreement</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Last updated: 2025-12-19</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">1. License & Ownership</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">1. License and Ownership</p>\n"
|
||||
"<p style=\" margin-top:0px; ma"
|
||||
"rgin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 A single device key may be required to activate the software or source code.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Non exclusive, non transferable, and for personal, internal use only.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 87owo reserves all rights. Intellectual property rights are not transferred.</p>\n"
|
||||
"rgin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - This software and its source code are source available for public view.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Access or execution of certain features may require an activation key.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - 87owo reserves all legal rights, no intellectual property is transferred.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - All project archives, source code, and binaries remain fully proprietary.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">2. Use & Prohibitions</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent"
|
||||
":0; text-indent:0px;\"> \u2013 Private redistribution, sublicensing, or rental is prohibited.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Private reverse engineering or removal of notices is prohibited.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Any of the above requires prior authorization from the original author.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Only personal internal use or modification of the source code is permitted.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Use of the software or source code for any illegal purpose is prohibited.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin"
|
||||
"-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">3. License Components</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 All project script archives and executable files are proprietary.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; mar"
|
||||
"gin-right:0px; -qt-block-indent:0; text-indent:0px;\">2. Use and Prohibitions</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - You may view, download, and modify the code for local and personal use.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Redistribution of any original or modified code requires prior consent.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Commercial hosting or SaaS deployment is prohibited without permission.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Reverse engineering, license removal, or illegal use is strictly barred.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-blo"
|
||||
"ck-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">3. Data and Privacy</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - This project may collect operational data for optimization and analysis.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - All collected data is strictly anonymized to ensure the user's privacy.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Transmission of information is encrypted to prevent unauthorized access.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">4. Updates & Terms</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Updates to new versions may include patches, enhancements, or modifications.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-lef"
|
||||
"t:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Continued use constitutes your acceptance of these features or terms.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; m"
|
||||
"argin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">4. Updates and Terms</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - New releases may include critical patches, enhancements, or UI changes.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Continued use of these services constitutes acceptance of updated terms.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - The author reserves the right to modify these license terms at any time.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">5. Warranty & Liability</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 This software is provided "as is," without warranty of any kind.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 In no event shall there be any liability for consequential damages.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-in"
|
||||
"dent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">\u00a9 2020\u20132025 87owo (PYAS Security). All rights reserved.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>", None))
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">5. Warranty and Liabi"
|
||||
"lity</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - All services and assets are provided "as is" without any form of warranty.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - 87owo is not liable for any direct or indirect damages from software use.</p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> - Users assume all risks regarding performance, data, and system integrity.</p>\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">\u00a9 2020-2026 87owo (PYAS Security). All rights reserved.</p></body></html>", None))
|
||||
self.process_title.setText(QCoreApplication.translate("main_window", u"\u9032\u7a0b\u9632\u8b77", None))
|
||||
self.process_text.setText(QCoreApplication.translate("main_window", u"\u6b64\u9078\u9805\u53ef\u4ee5\u6514\u622a\u9032\u7a0b\u75c5\u6bd2", None))
|
||||
self.process_switch.setText(QCoreApplication.translate("main_window", u"\u95dc\u9589", None))
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.3.6',
|
||||
version='3.3.7',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -12,6 +12,7 @@ BOOLEAN g_Unloading = FALSE;
|
||||
BOOLEAN g_CmRegActive = FALSE;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
PDEVICE_OBJECT g_GuardDevice = NULL;
|
||||
PDRIVER_OBJECT g_DriverObject = NULL;
|
||||
|
||||
PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS] = { 0 };
|
||||
ULONG g_MbrFilterCount = 0;
|
||||
@@ -37,22 +38,22 @@ static NTSTATUS CombinedDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
{
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (s->MajorFunction == IRP_MJ_PNP) {
|
||||
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
|
||||
if (s->MinorFunction == IRP_MN_REMOVE_DEVICE) {
|
||||
NTSTATUS st;
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
st = lower ? IoCallDriver(lower, Irp) : STATUS_SUCCESS;
|
||||
if (lower) IoDetachDevice(lower);
|
||||
IoDeleteDevice(DeviceObject);
|
||||
return st;
|
||||
}
|
||||
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (s->MajorFunction == IRP_MJ_PNP) {
|
||||
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
|
||||
if (s->MinorFunction == IRP_MN_REMOVE_DEVICE) {
|
||||
NTSTATUS st;
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp);
|
||||
st = lower ? IoCallDriver(lower, Irp) : STATUS_SUCCESS;
|
||||
if (lower) IoDetachDevice(lower);
|
||||
IoDeleteDevice(DeviceObject);
|
||||
return st;
|
||||
}
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
|
||||
return BootProtectDispatch(DeviceObject, Irp);
|
||||
}
|
||||
|
||||
@@ -76,12 +77,14 @@ static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
DiskDrvObj = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
PDEVICE_OBJECT* list = ExAllocatePool2(POOL_FLAG_NON_PAGED, done * sizeof(PDEVICE_OBJECT), 'dskT');
|
||||
if (!list) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
status = IoEnumerateDeviceObjectList(DiskDrvObj, list, done * sizeof(PDEVICE_OBJECT), &done);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
ExFreePool2(list, 'dskT', NULL, 0);
|
||||
@@ -89,91 +92,41 @@ static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
DiskDrvObj = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < done && g_MbrFilterCount < MAX_MBR_TARGETS; ++i) {
|
||||
PDEVICE_OBJECT lower = list[i], filter, attached = NULL;
|
||||
|
||||
status = IoCreateDevice(DriverObject, 0, NULL, lower->DeviceType, lower->Characteristics, FALSE, &filter);
|
||||
if (!NT_SUCCESS(status))
|
||||
continue;
|
||||
|
||||
filter->Flags |= (lower->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE));
|
||||
|
||||
status = IoAttachDeviceToDeviceStackSafe(filter, lower, &attached);
|
||||
if (!NT_SUCCESS(status) || !attached) {
|
||||
IoDeleteDevice(filter);
|
||||
continue;
|
||||
}
|
||||
|
||||
filter->StackSize = lower->StackSize + 1;
|
||||
filter->AlignmentRequirement = lower->AlignmentRequirement;
|
||||
filter->DeviceExtension = attached;
|
||||
filter->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
g_MbrFilterTargets[g_MbrFilterCount++] = filter;
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < done; ++i)
|
||||
ObDereferenceObject(list[i]);
|
||||
ExFreePool2(list, 'dskT', NULL, 0);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS AttachFsFilters(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNICODE_STRING symDev = RTL_CONSTANT_STRING(L"IoDeviceObjectType");
|
||||
POBJECT_TYPE* pDevType = (POBJECT_TYPE*)MmGetSystemRoutineAddress(&symDev);
|
||||
if (!pDevType || !*pDevType)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
for (ULONG i = 0; g_AttachDisk[i] && g_MbrFilterCount < MAX_MBR_TARGETS; ++i) {
|
||||
UNICODE_STRING name;
|
||||
RtlInitUnicodeString(&name, g_AttachDisk[i]);
|
||||
PDEVICE_OBJECT fsCtl = NULL;
|
||||
|
||||
status = ObReferenceObjectByName(&name, OBJ_CASE_INSENSITIVE, NULL, 0, *pDevType, KernelMode, NULL, (PVOID*)&fsCtl);
|
||||
if (!NT_SUCCESS(status) || !fsCtl)
|
||||
continue;
|
||||
|
||||
PDRIVER_OBJECT fsDrv = fsCtl->DriverObject;
|
||||
if (fsDrv) {
|
||||
ULONG need = 0;
|
||||
|
||||
status = IoEnumerateDeviceObjectList(fsDrv, NULL, 0, &need);
|
||||
if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_SUCCESS) {
|
||||
PDEVICE_OBJECT* list = (PDEVICE_OBJECT*)ExAllocatePool2(POOL_FLAG_NON_PAGED, need * sizeof(PDEVICE_OBJECT), 'fsTA');
|
||||
if (list) {
|
||||
|
||||
status = IoEnumerateDeviceObjectList(fsDrv, list, need * sizeof(PDEVICE_OBJECT), &need);
|
||||
if (NT_SUCCESS(status)) {
|
||||
for (ULONG j = 0; j < need && g_MbrFilterCount < MAX_MBR_TARGETS; ++j) {
|
||||
|
||||
PDEVICE_OBJECT lower = list[j], filter, attached = NULL;
|
||||
NTSTATUS s2 = IoCreateDevice(DriverObject, 0, NULL, lower->DeviceType, lower->Characteristics, FALSE, &filter);
|
||||
if (!NT_SUCCESS(s2))
|
||||
continue;
|
||||
|
||||
filter->Flags |= (lower->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE));
|
||||
s2 = IoAttachDeviceToDeviceStackSafe(filter, lower, &attached);
|
||||
if (!NT_SUCCESS(s2) || !attached) {
|
||||
IoDeleteDevice(filter);
|
||||
continue;
|
||||
}
|
||||
filter->StackSize = lower->StackSize + 1;
|
||||
filter->AlignmentRequirement = lower->AlignmentRequirement;
|
||||
filter->DeviceExtension = attached;
|
||||
filter->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
g_MbrFilterTargets[g_MbrFilterCount++] = filter;
|
||||
}
|
||||
}
|
||||
ExFreePool2(list, 'fsTA', NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
ObDereferenceObject(fsCtl);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
g_DriverObject = DriverObject;
|
||||
|
||||
KeInitializeSpinLock(&g_ProtectLock);
|
||||
ExInitializeFastMutex(&HookMutex);
|
||||
KeInitializeEvent(&g_LogDrainEvent, NotificationEvent, TRUE);
|
||||
@@ -182,12 +135,14 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
|
||||
InterlockedExchange(&g_LogWorkCount, 0);
|
||||
ExInitializeRundownProtection(&g_Rundown);
|
||||
|
||||
{
|
||||
UNICODE_STRING guardName = RTL_CONSTANT_STRING(L"\\Device\\PYAS_Driver_Guard");
|
||||
NTSTATUS gs = IoCreateDevice(DriverObject, 0, &guardName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_GuardDevice);
|
||||
if (!NT_SUCCESS(gs))
|
||||
return STATUS_DEVICE_BUSY;
|
||||
}
|
||||
|
||||
{
|
||||
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
|
||||
g_DeviceName.Buffer = g_DeviceNameBuf;
|
||||
@@ -195,6 +150,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
RtlStringCchPrintfW(g_DeviceName.Buffer, RTL_NUMBER_OF(g_DeviceNameBuf), L"\\Device\\PYAS_Driver_%p_%I64x", DriverObject, t.QuadPart);
|
||||
g_DeviceName.Length = (USHORT)(wcslen(g_DeviceName.Buffer) * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
NTSTATUS status = IoCreateDevice(DriverObject, 0, &g_DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_ControlDeviceObject);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
@@ -204,8 +160,8 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
DriverObject->MajorFunction[i] = CombinedDispatch;
|
||||
|
||||
DriverObject->Flags |= 0x20;
|
||||
|
||||
g_ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
{
|
||||
UINT i = 0;
|
||||
@@ -213,7 +169,6 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
status = IoCreateSymbolicLink(&g_SymLink, &g_DeviceName);
|
||||
if (NT_SUCCESS(status))
|
||||
break;
|
||||
|
||||
LARGE_INTEGER d = { 0 };
|
||||
d.QuadPart = -10LL * 1000LL * 10LL;
|
||||
KeDelayExecutionThread(KernelMode, FALSE, &d);
|
||||
@@ -227,6 +182,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
}
|
||||
}
|
||||
g_SymLinkCreated = TRUE;
|
||||
|
||||
{
|
||||
static WCHAR regAltBuf[32];
|
||||
static UNICODE_STRING regAlt;
|
||||
@@ -238,13 +194,16 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
if (NT_SUCCESS(status))
|
||||
g_CmRegActive = TRUE;
|
||||
}
|
||||
|
||||
InitImageProtect();
|
||||
InitInjectProtect();
|
||||
InitRemoteProtect();
|
||||
InitScreenProtect();
|
||||
InitProcessProtect();
|
||||
AttachFsFilters(DriverObject);
|
||||
|
||||
InitFileProtect();
|
||||
AttachDiskFilters(DriverObject);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -253,14 +212,18 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
g_Unloading = TRUE;
|
||||
|
||||
if (g_SymLinkCreated) {
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
g_SymLinkCreated = FALSE;
|
||||
}
|
||||
|
||||
if (g_CmRegActive) {
|
||||
CmUnRegisterCallback(g_CmRegHandle);
|
||||
g_CmRegActive = FALSE;
|
||||
}
|
||||
|
||||
UninitFileProtect();
|
||||
UninitInjectProtect();
|
||||
UninitImageProtect();
|
||||
UninitRemoteProtect();
|
||||
@@ -268,6 +231,7 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
UninitProcessProtect();
|
||||
|
||||
ExWaitForRundownProtectionRelease(&g_Rundown);
|
||||
|
||||
if (InterlockedCompareExchange((volatile LONG*)&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
@@ -276,6 +240,7 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
while (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0)
|
||||
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < g_MbrFilterCount; ++i) {
|
||||
if (g_MbrFilterTargets[i]) {
|
||||
IoDetachDevice((PDEVICE_OBJECT)g_MbrFilterTargets[i]->DeviceExtension);
|
||||
@@ -284,6 +249,7 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
}
|
||||
}
|
||||
g_MbrFilterCount = 0;
|
||||
|
||||
if (DiskDrvObj) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
#define PROTECTED_BOOT_AREA_SIZE (34 * 512)
|
||||
|
||||
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || exe->MaximumLength == 0)
|
||||
@@ -24,6 +26,9 @@ static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
|
||||
static BOOLEAN QueryDevName(PDEVICE_OBJECT dev, PUNICODE_STRING out)
|
||||
{
|
||||
if (KeGetCurrentIrql() > APC_LEVEL)
|
||||
return FALSE;
|
||||
|
||||
if (!dev || !out || !out->Buffer || out->MaximumLength == 0)
|
||||
return FALSE;
|
||||
|
||||
@@ -48,37 +53,30 @@ static BOOLEAN QueryDevName(PDEVICE_OBJECT dev, PUNICODE_STRING out)
|
||||
return ok;
|
||||
}
|
||||
|
||||
static BOOLEAN IsDASDRoot(PFILE_OBJECT fo)
|
||||
static BOOLEAN IsDangerousDiskIo(PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (!fo)
|
||||
return TRUE;
|
||||
if (fo->FileName.Length == 0 && fo->RelatedFileObject == NULL)
|
||||
return TRUE;
|
||||
if (!s) return FALSE;
|
||||
|
||||
if (s->MajorFunction == IRP_MJ_DEVICE_CONTROL || s->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) {
|
||||
ULONG code = s->Parameters.DeviceIoControl.IoControlCode;
|
||||
if (code == IOCTL_DISK_SET_DRIVE_LAYOUT || code == IOCTL_DISK_SET_DRIVE_LAYOUT_EX) return TRUE;
|
||||
if (code == IOCTL_DISK_DELETE_DRIVE_LAYOUT) return TRUE;
|
||||
if (code == IOCTL_DISK_FORMAT_TRACKS || code == IOCTL_DISK_FORMAT_TRACKS_EX) return TRUE;
|
||||
if (code == IOCTL_DISK_SET_PARTITION_INFO || code == IOCTL_DISK_SET_PARTITION_INFO_EX) return TRUE;
|
||||
if (code == IOCTL_SCSI_PASS_THROUGH || code == IOCTL_SCSI_PASS_THROUGH_DIRECT) return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsDangerousDiskIo(PIO_STACK_LOCATION s)
|
||||
static BOOLEAN IsBootSectorWrite(PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (!s)
|
||||
return FALSE;
|
||||
if (s->MajorFunction == IRP_MJ_DEVICE_CONTROL || s->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) {
|
||||
ULONG code = s->Parameters.DeviceIoControl.IoControlCode;
|
||||
if (code == IOCTL_SCSI_PASS_THROUGH || code == IOCTL_SCSI_PASS_THROUGH_DIRECT)
|
||||
if (s->MajorFunction == IRP_MJ_WRITE) {
|
||||
LARGE_INTEGER offset = s->Parameters.Write.ByteOffset;
|
||||
if (offset.QuadPart < PROTECTED_BOOT_AREA_SIZE) {
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_SET_DRIVE_LAYOUT || code == IOCTL_DISK_SET_DRIVE_LAYOUT_EX)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_DELETE_DRIVE_LAYOUT)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_FORMAT_TRACKS || code == IOCTL_DISK_FORMAT_TRACKS_EX)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_SET_PARTITION_INFO || code == IOCTL_DISK_SET_PARTITION_INFO_EX)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (s->MajorFunction == IRP_MJ_SCSI)
|
||||
return TRUE;
|
||||
if (s->MajorFunction == IRP_MJ_WRITE || s->MajorFunction == IRP_MJ_FLUSH_BUFFERS)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -91,17 +89,21 @@ NTSTATUS BootProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
if (!ExAcquireRundownProtection(&g_Rundown)) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver(lower, Irp);
|
||||
}
|
||||
|
||||
if (g_Unloading) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS rs = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return rs;
|
||||
}
|
||||
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
if (s->MajorFunction == IRP_MJ_POWER) {
|
||||
PoStartNextPowerIrp(Irp);
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
@@ -109,14 +111,17 @@ NTSTATUS BootProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return ps;
|
||||
}
|
||||
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
ULONG upid = (ULONG)(ULONG_PTR)pid;
|
||||
|
||||
if (upid == 0 || upid == 4) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS ns = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return ns;
|
||||
}
|
||||
|
||||
BOOLEAN blocked = FALSE;
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
@@ -132,28 +137,24 @@ NTSTATUS BootProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
return ns2;
|
||||
}
|
||||
}
|
||||
|
||||
if (lower->DeviceType == FILE_DEVICE_DISK) {
|
||||
if (IsDangerousDiskIo(s)) {
|
||||
BOOLEAN hit = FALSE;
|
||||
WCHAR db[512] = { 0 };
|
||||
UNICODE_STRING dev = { 0 };
|
||||
dev.Buffer = db;
|
||||
dev.MaximumLength = sizeof(db);
|
||||
blocked = TRUE;
|
||||
}
|
||||
|
||||
if (QueryDevName(lower, &dev)) {
|
||||
if (MatchBlockFile(&dev))
|
||||
hit = TRUE;
|
||||
}
|
||||
else {
|
||||
hit = TRUE;
|
||||
}
|
||||
if (!hit && s->MajorFunction == IRP_MJ_WRITE && s->FileObject && IsDASDRoot(s->FileObject))
|
||||
hit = TRUE;
|
||||
if (!blocked && IsBootSectorWrite(s)) {
|
||||
blocked = TRUE;
|
||||
}
|
||||
|
||||
if (blocked) {
|
||||
if (KeGetCurrentIrql() <= APC_LEVEL) {
|
||||
WCHAR db[512] = { 0 };
|
||||
UNICODE_STRING dev = { 0 };
|
||||
dev.Buffer = db;
|
||||
dev.MaximumLength = sizeof(db);
|
||||
QueryDevName(lower, &dev);
|
||||
|
||||
if (hit) {
|
||||
blocked = TRUE;
|
||||
if (dev.Length == 0)
|
||||
QueryDevName(lower, &dev);
|
||||
LogAnsi3("BOOT_BLOCK", upid, &exe, dev.Length ? &dev : &exe);
|
||||
}
|
||||
}
|
||||
@@ -166,8 +167,9 @@ NTSTATUS BootProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS rs2 = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return rs2;
|
||||
}
|
||||
}
|
||||
+143
-100
@@ -6,39 +6,34 @@
|
||||
#pragma comment(lib, "FltMgr.lib")
|
||||
|
||||
PFLT_FILTER g_FilterHandle = NULL;
|
||||
PDRIVER_OBJECT g_DriverObject = NULL;
|
||||
extern PDRIVER_OBJECT g_DriverObject;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
|
||||
typedef struct _MINIFILTER_DATA {
|
||||
PFLT_FILTER Filter;
|
||||
} MINIFILTER_DATA, * PMINIFILTER_DATA;
|
||||
// 定義標誌以便在不同 IRQL 下安全操作
|
||||
#define MAX_PATH_LEN 512
|
||||
|
||||
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
// 獲取進程名稱 (僅在確定需要攔截時調用,減少性能開銷)
|
||||
static VOID GetExeNameSafe(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || !exe->MaximumLength)
|
||||
return;
|
||||
|
||||
exe->Length = 0;
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, exe))
|
||||
return;
|
||||
}
|
||||
PEPROCESS eproc = NULL;
|
||||
NTSTATUS s = PsLookupProcessByProcessId(pid, &eproc);
|
||||
if (NT_SUCCESS(s) && eproc) {
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
|
||||
exe->Buffer[i] = (WCHAR)n[i];
|
||||
exe->Buffer[l] = 0;
|
||||
exe->Length = (USHORT)(l * sizeof(WCHAR));
|
||||
ObDereferenceObject(eproc);
|
||||
return;
|
||||
}
|
||||
PCSTR n = NULL;
|
||||
|
||||
// 嘗試獲取短名稱 (最安全)
|
||||
PEPROCESS cur = PsGetCurrentProcess();
|
||||
if (cur)
|
||||
n = PsGetProcessImageFileName(cur);
|
||||
PCSTR n = NULL;
|
||||
if (cur) n = PsGetProcessImageFileName(cur);
|
||||
|
||||
// 如果在 PASSIVE_LEVEL,嘗試獲取完整路徑 (用於精確白名單)
|
||||
// 注意:這裡依然有風險,但僅在攔截路徑執行
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, exe)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 降級方案:使用短名稱
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
|
||||
exe->Buffer[i] = (WCHAR)n[i];
|
||||
@@ -46,41 +41,24 @@ static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
exe->Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
static BOOLEAN PreOpCheck(ULONG* outUpid)
|
||||
static BOOLEAN PreOpCheck(VOID)
|
||||
{
|
||||
if (g_Unloading)
|
||||
return FALSE;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return FALSE;
|
||||
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
ULONG upid = (ULONG)(ULONG_PTR)pid;
|
||||
if (upid == 0 || upid == 4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return FALSE;
|
||||
}
|
||||
*outUpid = upid;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static VOID PostOpRelease()
|
||||
static VOID PostOpRelease(VOID)
|
||||
{
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
static BOOLEAN IsExeWhitelisted(ULONG pid)
|
||||
{
|
||||
WCHAR buffer[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = buffer;
|
||||
exe.MaximumLength = sizeof(buffer);
|
||||
GetExeNameForLog((HANDLE)pid, &exe);
|
||||
return IsWhitelist(&exe);
|
||||
}
|
||||
|
||||
static BOOLEAN IsDeleteOrRename(PFLT_CALLBACK_DATA Data)
|
||||
{
|
||||
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
|
||||
|
||||
if (infoClass == FileDispositionInformation) {
|
||||
PFILE_DISPOSITION_INFORMATION di = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
|
||||
return di && di->DeleteFile;
|
||||
@@ -91,37 +69,30 @@ static BOOLEAN IsDeleteOrRename(PFLT_CALLBACK_DATA Data)
|
||||
}
|
||||
if (infoClass == FileRenameInformation || infoClass == FileRenameInformationEx)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static FLT_PREOP_CALLBACK_STATUS CheckAndBlockFile(PFLT_CALLBACK_DATA Data, ULONG upid, PUNICODE_STRING exe)
|
||||
static BOOLEAN IsWriteAccess(PFLT_CALLBACK_DATA Data)
|
||||
{
|
||||
if (KeGetCurrentIrql() > APC_LEVEL)
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
// 如果是寫入操作,直接返回
|
||||
if (Data->Iopb->MajorFunction == IRP_MJ_WRITE)
|
||||
return TRUE;
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
// 如果是設置信息 (已經通過 IsDeleteOrRename 過濾),視為寫入(修改元數據)
|
||||
if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION)
|
||||
return TRUE;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
// 如果是創建/打開文件,檢查權限
|
||||
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
|
||||
ULONG access = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
|
||||
// 檢查是否包含寫入數據、追加數據、刪除、修改屬性等權限
|
||||
if (access & (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE |
|
||||
WRITE_DAC | WRITE_OWNER | GENERIC_WRITE | GENERIC_ALL)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
|
||||
BOOLEAN blocked = hitFile || hitRansom;
|
||||
|
||||
if (blocked) {
|
||||
LogAnsi3(hitFile && !hitRansom ? "FILE_BLOCK" : "RANSOM_BLOCK", upid, exe, &nameInfo->Name);
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static FLT_PREOP_CALLBACK_STATUS FilePreOp(
|
||||
@@ -133,63 +104,120 @@ static FLT_PREOP_CALLBACK_STATUS FilePreOp(
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
ULONG upid;
|
||||
if (!PreOpCheck(&upid))
|
||||
// 1. 基礎檢查 (Paging IO 不攔截)
|
||||
if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO)) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
if (!PreOpCheck())
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) {
|
||||
// 2. 獲取 PID
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
ULONG upid = (ULONG)(ULONG_PTR)pid;
|
||||
|
||||
// 忽略 System (4) 和 Idle (0) 進程,避免系統崩潰
|
||||
if (upid == 0 || upid == 4) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// 3. 檢查操作類型 (僅關心 Create, Write, SetInfo)
|
||||
BOOLEAN checkFile = (Data->Iopb->MajorFunction == IRP_MJ_CREATE || Data->Iopb->MajorFunction == IRP_MJ_WRITE);
|
||||
BOOLEAN checkSetInfo = (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION && IsDeleteOrRename(Data));
|
||||
BOOLEAN checkSetInfo = (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION);
|
||||
|
||||
if (!checkFile && !checkSetInfo) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
if (checkSetInfo && !IsDeleteOrRename(Data)) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// 4. 獲取文件名 (這一步最關鍵)
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
// 使用 QUERY_DEFAULT | NORMALIZED 確保獲取標準化路徑
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
WCHAR exe_buffer[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = exe_buffer;
|
||||
exe.MaximumLength = sizeof(exe_buffer);
|
||||
GetExeNameForLog(PsGetCurrentProcessId(), &exe);
|
||||
|
||||
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
|
||||
BOOLEAN isExcept = IsWhitelistExcept(&exe);
|
||||
// 5. 邏輯判斷 (優化順序:文件名白名單 -> 黑名單 -> 操作檢查 -> 進程白名單)
|
||||
|
||||
if ((hitFile || hitRansom) && !isExcept) {
|
||||
LogAnsi3(hitFile && !hitRansom ? "FILE_BLOCK" : "RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
if (isExcept && hitRansom) {
|
||||
LogAnsi3("RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
// A. 全局文件白名單 (例如 Windows 目錄下的某些操作)
|
||||
if (IsWhitelist(&nameInfo->Name)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// B. 黑名單匹配
|
||||
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
|
||||
// 勒索防護:必須在敏感目錄 且 有敏感後綴 (如 .txt, .doc)
|
||||
// 註:Updater.bat 產生 .txt,應命中這裡
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
|
||||
|
||||
// 如果沒有命中任何規則,直接放行,節省資源
|
||||
if (!hitFile && !hitRansom) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// C. 檢查是否為寫入行為 (讀取不攔截)
|
||||
if (!IsWriteAccess(Data)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// D. 到了這裡,說明命中規則且正在寫入,準備攔截
|
||||
// 這時候才去獲取進程名稱,檢查進程白名單
|
||||
WCHAR exe_buffer[MAX_PATH_LEN] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = exe_buffer;
|
||||
exe.MaximumLength = sizeof(exe_buffer);
|
||||
|
||||
GetExeNameSafe(pid, &exe); // 獲取進程名
|
||||
|
||||
if (IsWhitelistExcept(&exe)) {
|
||||
// 進程在白名單中 (如 ntoskrnl, explorer 等)
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// E. 執行攔截
|
||||
if (hitRansom) {
|
||||
LogAnsi3("RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
if (hitFile) {
|
||||
LogAnsi3("FILE_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
// 默認放行
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
@@ -218,7 +246,7 @@ static NTSTATUS FilterInstanceSetup(
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
|
||||
// 自動附加到所有卷
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -249,13 +277,28 @@ const FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
|
||||
const FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION, 0, NULL, Callbacks, FilterUnloadCallback, FilterInstanceSetup, NULL,
|
||||
FilterInstanceTeardownStart, FilterInstanceTeardownComplete, NULL, NULL, NULL, NULL
|
||||
FLT_REGISTRATION_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
Callbacks,
|
||||
FilterUnloadCallback,
|
||||
FilterInstanceSetup,
|
||||
NULL,
|
||||
FilterInstanceTeardownStart,
|
||||
FilterInstanceTeardownComplete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
NTSTATUS InitFileProtect(VOID)
|
||||
{
|
||||
ExInitializeRundownProtection(&g_Rundown);
|
||||
|
||||
if (!g_DriverObject)
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
NTSTATUS status = FltRegisterFilter(g_DriverObject, &FilterRegistration, &g_FilterHandle);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
@@ -276,4 +319,4 @@ VOID UninitFileProtect(VOID)
|
||||
FltUnregisterFilter(g_FilterHandle);
|
||||
g_FilterHandle = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,8 @@ static VOID ImageLoadNotify(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessI
|
||||
{
|
||||
size_t elen = exe.Length / sizeof(WCHAR);
|
||||
if ((elen >= 14 && _wcsnicmp(exe.Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
|
||||
(elen >= 8 && _wcsnicmp(exe.Buffer + elen - 8, L"pwsh.exe", 8) == 0)) {
|
||||
(elen >= 8 && _wcsnicmp(exe.Buffer + elen - 8, L"pwsh.exe", 8) == 0) ||
|
||||
(elen >= 10 && _wcsnicmp(exe.Buffer + elen - 10, L"sppsvc.exe", 10) == 0)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,36 +119,38 @@ static BOOLEAN EndsWithNameUS(PUNICODE_STRING s, const wchar_t* name)
|
||||
return n >= m && _wcsnicmp(s->Buffer + (n - m), name, m) == 0;
|
||||
}
|
||||
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
{
|
||||
if (USOK(key) && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
|
||||
if (USOK(key) && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
|
||||
return FALSE;
|
||||
|
||||
|
||||
if (USOK(exe) && IsWhitelist(exe))
|
||||
return FALSE;
|
||||
|
||||
|
||||
if (USOK(exe) && USOK(key)) {
|
||||
if ((EndsWithNameUS(exe, L"services.exe") || EndsWithNameUS(exe, L"svchost.exe") || EndsWithNameUS(exe, L"sc.exe")) &&
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
if ((EndsWithNameUS(exe, L"services.exe") || EndsWithNameUS(exe, L"svchost.exe") || EndsWithNameUS(exe, L"sc.exe")) &&
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
|
||||
|
||||
if ((EndsWithNameUS(exe, L"powershell.exe") || EndsWithNameUS(exe, L"pwsh.exe")) &&
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**")))
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data")))
|
||||
return FALSE;
|
||||
|
||||
|
||||
if ((EndsWithNameUS(exe, L"msiexec.exe") || EndsWithNameUS(exe, L"TrustedInstaller.exe")) &&
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**")))
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**")))
|
||||
return FALSE;
|
||||
|
||||
if (EndsWithNameUS(exe, L"schtasks.exe") && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
|
||||
|
||||
if (EndsWithNameUS(exe, L"schtasks.exe") && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(key) && MatchBlockReg(key))
|
||||
return TRUE;
|
||||
|
||||
|
||||
if (USOK(valueName)) {
|
||||
for (SIZE_T i = 0; g_BlockReg[i]; ++i) {
|
||||
wchar_t* pat = g_BlockReg[i];
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "ProtectRules.h"
|
||||
|
||||
wchar_t* g_Whitelist[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Program*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\**",
|
||||
L"**\\PYAS*.exe",
|
||||
@@ -14,26 +13,18 @@ wchar_t* g_Whitelist[] = {
|
||||
wchar_t* g_WhitelistExcept[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\vds.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdedit.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\reg.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\cmd.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\mshta.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wscript.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\cscript.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\schtasks.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\diskpart.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\format.com",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\mountvol.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\ntoskrnl.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wbadmin.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdboot.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\dism.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bootsect.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\cipher.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\net.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\net1.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\msiexec.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\WindowsPowerShell\\*\\powershell.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Program*\\PowerShell\\*\\pwsh.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\lsass.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\csrss.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\smss.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wininit.exe",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -58,7 +49,6 @@ wchar_t* g_BlockFile[] = {
|
||||
L"\\Device\\HarddiskVolume*\\$*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\ProgramData\\PYAS\\**",
|
||||
L"**\\PYAS.exe",
|
||||
|
||||
L"**\\CON**",
|
||||
L"**\\PRN**",
|
||||
L"**\\AUX**",
|
||||
@@ -77,6 +67,7 @@ wchar_t* g_BlockRansom[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Music\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Pictures\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Videos\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\OneDrive\\**",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -97,30 +88,10 @@ wchar_t* g_BlockReg[] = {
|
||||
L"\\REGISTRY\\MACHINE\\BCD00000000\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SAM\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SECURITY\\**",
|
||||
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Control\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Enum\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Services\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\MountedDevices\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\Setup\\**",
|
||||
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Classes\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Policies\\Microsoft\\MMC\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows\\System\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Wow6432Node\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\MachineGuid",
|
||||
L"\\REGISTRY\\MACHINE\\**\\Microsoft\\Windows Defender\\**",
|
||||
|
||||
L"\\REGISTRY\\**\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\*\\StubPath",
|
||||
L"\\REGISTRY\\**\\SOFTWARE\\Microsoft\\Windows*\\CurrentVersion\\**",
|
||||
L"\\REGISTRY\\**\\SOFTWARE\\Microsoft\\Windows Script Host\\Settings\\**",
|
||||
|
||||
L"\\REGISTRY\\USER\\*_Classes\\*\\shell\\open\\command\\**",
|
||||
L"\\REGISTRY\\USER\\*_Classes\\CLSID\\{fdb00e52-a214-4aa1-8fba-4357bb0072ec}\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\NetWire\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\Remcos*\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\DC3_FEXEC\\**",
|
||||
|
||||
L"DisableAntiSpyware",
|
||||
L"DisableWindowsUpdateAccess",
|
||||
L"EnableLUA",
|
||||
@@ -195,7 +166,6 @@ wchar_t* g_BlockReg[] = {
|
||||
L"CmdLine",
|
||||
L"SetupType",
|
||||
L"AutoReboot",
|
||||
L"Shell",
|
||||
L"Userinit",
|
||||
L"UIHost",
|
||||
L"Debugger",
|
||||
@@ -293,4 +263,4 @@ wchar_t* g_ScreenUserSubdirs[] = {
|
||||
wchar_t* g_ScreenOtherProcNeedles[] = {
|
||||
L"\\ProgramData\\",
|
||||
NULL
|
||||
};
|
||||
};
|
||||
@@ -55,15 +55,115 @@ PYAS/
|
||||
└── ... # Other supplementary folders and files
|
||||
```
|
||||
|
||||
## Program Architecture
|
||||
## Architecture diagram
|
||||
|
||||
PYAS antivirus software complete program flow architecture diagram.
|
||||
PYAS Security antivirus software general architecture diagram.
|
||||
|
||||
<img width="23000" height="6000" alt="PYAS_Architecture" src="https://github.com/user-attachments/assets/3a7ce637-c2b3-4719-9658-07f286cfa879" />
|
||||
```mermaid
|
||||
graph TD
|
||||
%% Global Styles
|
||||
classDef ui fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
|
||||
classDef logic fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
|
||||
classDef engine fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
|
||||
classDef thread fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
|
||||
classDef kernel fill:#ffebee,stroke:#c62828,stroke-width:2px;
|
||||
classDef data fill:#eceff1,stroke:#455a64,stroke-width:2px,stroke-dasharray: 5 5;
|
||||
|
||||
## Packaged Releases
|
||||
User((User))
|
||||
|
||||
Packaged Releases Download: https://github.com/87owo/PYAS/releases
|
||||
subgraph Presentation_Layer ["Presentation Layer (View)"]
|
||||
direction TB
|
||||
UI_Static["PYAS_Interface.py<br/>(Ui_main_window Class)"]:::ui
|
||||
UI_Controller["PYAS.py<br/>(MainWindow_Controller Class)"]:::ui
|
||||
TrayIcon["System Tray Icon"]:::ui
|
||||
|
||||
User <--> UI_Controller
|
||||
UI_Static --- UI_Controller
|
||||
UI_Controller --- TrayIcon
|
||||
end
|
||||
|
||||
subgraph Core_Logic_Layer ["Core Logic & Controller"]
|
||||
direction TB
|
||||
Init_Env["Initialization<br/>(init_environ, init_variable,<br/>init_windll)"]:::logic
|
||||
Config_Mgr["Configuration Manager<br/>(load_config, save_config)"]:::logic
|
||||
|
||||
subgraph System_Tools ["System Tools"]
|
||||
Sys_Repair["System Repair<br/>(repair_system_mbr,<br/>repair_system_image, etc.)"]:::logic
|
||||
Sys_Clean["System Cleaner<br/>(clean_button, traverse_temp)"]:::logic
|
||||
Proc_Mgr["Process Manager<br/>(list_process, kill_process)"]:::logic
|
||||
List_Mgr["List Manager<br/>(manage_named_list:<br/>Whitelist/Quarantine)"]:::logic
|
||||
end
|
||||
|
||||
UI_Controller --> Init_Env
|
||||
Init_Env --> Config_Mgr
|
||||
UI_Controller --> System_Tools
|
||||
end
|
||||
|
||||
subgraph Protection_Layer ["Real-time Protection Threads (Daemons)"]
|
||||
direction TB
|
||||
Thread_Proc["Process Guard Thread<br/>(protect_proc_thread)<br/>[WMI/Snapshot]"]:::thread
|
||||
Thread_File["File Guard Thread<br/>(protect_file_thread)<br/>[ReadDirectoryChangesW]"]:::thread
|
||||
Thread_Net["Network Guard Thread<br/>(protect_net_thread)<br/>[GetExtendedTcpTable]"]:::thread
|
||||
Thread_Sys["System Guard Thread<br/>(protect_system_thread)<br/>[Reg/MBR Check]"]:::thread
|
||||
Thread_Popup["Popup Blocker Thread<br/>(popup_intercept_thread)<br/>[EnumWindows]"]:::thread
|
||||
Thread_Pipe["Driver IPC Thread<br/>(pipe_server_thread)<br/>[NamedPipe Server]"]:::thread
|
||||
|
||||
Init_Env --> Thread_Proc
|
||||
Init_Env --> Thread_File
|
||||
Init_Env --> Thread_Net
|
||||
Init_Env --> Thread_Sys
|
||||
Init_Env --> Thread_Popup
|
||||
Init_Env --> Thread_Pipe
|
||||
end
|
||||
|
||||
subgraph Engine_Layer ["Detection Engine (PYAS_Engine.py)"]
|
||||
direction TB
|
||||
Scan_Worker["Scan Worker<br/>(scan_worker / scan_engine)"]:::engine
|
||||
|
||||
subgraph Scanners
|
||||
Sign_Check["Signature Scanner<br/>(sign_scanner)<br/>[WinVerifyTrust]"]:::engine
|
||||
AI_Model["AI Model Scanner<br/>(model_scanner)<br/>[ONNX/ResNet]"]:::engine
|
||||
Yara_Rules["Rule Scanner<br/>(rule_scanner)<br/>[Yara/IP Lists]"]:::engine
|
||||
end
|
||||
|
||||
Thread_Proc --> Scan_Worker
|
||||
Thread_File --> Scan_Worker
|
||||
Thread_Net --> Scan_Worker
|
||||
UI_Controller -->|Manual Scan| Scan_Worker
|
||||
|
||||
Scan_Worker --> Sign_Check
|
||||
Scan_Worker --> AI_Model
|
||||
Scan_Worker --> Yara_Rules
|
||||
end
|
||||
|
||||
subgraph Kernel_OS_Layer ["Windows Kernel & Driver"]
|
||||
direction TB
|
||||
WinAPI["Windows API (ctypes)<br/>(kernel32, user32, ntdll,<br/>advapi32, iphlpapi, psapi)"]:::kernel
|
||||
Driver_Sys["Kernel Driver<br/>(PYAS_Driver.sys)"]:::kernel
|
||||
OS_FS["File System (NTFS)"]:::kernel
|
||||
OS_Net["Network Stack"]:::kernel
|
||||
|
||||
Init_Env -- Load DLLs --> WinAPI
|
||||
Thread_File -- Monitor --> OS_FS
|
||||
Thread_Net -- Monitor --> OS_Net
|
||||
Thread_Proc -- Suspend/Kill --> WinAPI
|
||||
|
||||
Thread_Pipe <==>|"Named Pipe IPC<br/>(\\.\pipe\PYAS_Output_Pipe)"| Driver_Sys
|
||||
Driver_Sys -- Callback/Block --> WinAPI
|
||||
end
|
||||
|
||||
subgraph Data_Persistence ["Data & Resources"]
|
||||
Config_File[("Config.json")]:::data
|
||||
Model_Files[("Model Files<br/>(.onnx)")]:::data
|
||||
Rule_Files[("Rule Files<br/>(.yar)")]:::data
|
||||
Quarantine_Dir[("/Quarantine/")]:::data
|
||||
|
||||
Config_Mgr <--> Config_File
|
||||
AI_Model <--> Model_Files
|
||||
Yara_Rules <--> Rule_Files
|
||||
List_Mgr <--> Quarantine_Dir
|
||||
end
|
||||
```
|
||||
|
||||
## Support System
|
||||
|
||||
@@ -72,14 +172,24 @@ Packaged Releases Download: https://github.com/87owo/PYAS/releases
|
||||
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
|
||||
| Recommend | Administrator | >= Windows 10 (21H2) | 3 GHz | 500MB | 200MB |
|
||||
|
||||
## Packaged Releases
|
||||
|
||||
Download the installer. If it is incompatible with your system, you can repackage it yourself.
|
||||
|
||||
Packaged Download: https://github.com/87owo/PYAS/releases
|
||||
|
||||
## Official Website
|
||||
|
||||
Official Website : https://pyantivirus.wixsite.com/pyas
|
||||
If you are interested in this project, you can visit the website to see other related content.
|
||||
|
||||
Open Source Website : https://github.com/87owo/PYAS
|
||||
Source Available : https://github.com/87owo/PYAS
|
||||
|
||||
Official Website : https://pyas-security.com/antivirus
|
||||
|
||||
## Project License
|
||||
|
||||
Please read the Project License. If you would like to apply, please submit your application to Github issues.
|
||||
For any questions, needs, or bug feedback, please contact us through the following website.
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
Source Issues : https://github.com/87owo/PYAS/issues
|
||||
|
||||
Official Email : mailto:service.pyas@gmail.com
|
||||
Reference in new issue
Block a user