Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
264dd50969 | ||
|
|
26d0ad1a6e | ||
|
|
90284018e5 | ||
|
|
46eeec16d4 | ||
|
|
083fb0dccc | ||
|
|
d46f145806 | ||
|
|
868eddfd2b | ||
|
|
8295e6bb5b | ||
|
|
424914b842 | ||
|
|
8c5027df0f | ||
|
|
0b7817d992 | ||
|
|
ca6c9bfd05 | ||
|
|
0a1abd8040 | ||
|
|
ed69a4fdf6 | ||
|
|
2ae19a6d4e | ||
|
|
404bc50dd4 | ||
|
|
cada5eeeaf | ||
|
|
5e9bd40e8b | ||
|
|
0788bab0dd | ||
|
|
4e8e0dbea7 | ||
|
|
b8c5279137 | ||
|
|
a3255b1681 | ||
|
|
f151379f8f | ||
|
|
7784544583 | ||
|
|
f742937296 | ||
|
|
5fd9242291 | ||
|
|
9f343196a9 | ||
|
|
5468eded04 | ||
|
|
1b97758fc9 | ||
|
|
b2e8e49447 | ||
|
|
3eec781139 | ||
|
|
b6f1977fa0 | ||
|
|
e0aa080f00 | ||
|
|
66aa76c836 | ||
|
|
bb9444e1ae | ||
|
|
6c9bfa7755 | ||
|
|
c4fa849290 | ||
|
|
4807e2c410 | ||
|
|
5388efdebc | ||
|
|
1d656de7ba | ||
|
|
eaa1d725a9 | ||
|
|
dea2150d63 | ||
|
|
80f9d6cf75 | ||
|
|
7430d38594 | ||
|
|
6804e4af9f | ||
|
|
138756c31d | ||
|
|
349bc293c6 | ||
|
|
2f87cd10f2 | ||
|
|
fc50eafcca | ||
|
|
b48e20a727 | ||
|
|
ce7ed2e6eb | ||
|
|
043d71e0ff | ||
|
|
1855ae0e1d | ||
|
|
77323a50df | ||
|
|
1c2f6b85ca | ||
|
|
2b5c478e4b | ||
|
|
90fd83a81f | ||
|
|
8fa2a166f3 | ||
|
|
e8cc45ebe7 | ||
|
|
eb87117b91 | ||
|
|
3799dfa1e8 | ||
|
|
163c7fd680 | ||
|
|
44ca762f69 | ||
|
|
8db311ba33 | ||
|
|
99d8604db4 | ||
|
|
90aa88daeb | ||
|
|
ae6eebad4d | ||
|
|
cd31ce1c38 | ||
|
|
ed3846e451 | ||
|
|
0c79cf8e37 | ||
|
|
81c7e16c0e | ||
|
|
16c03095b8 | ||
|
|
b5dab3aa22 | ||
|
|
e4a3c871f8 | ||
|
|
a2fe947b08 | ||
|
|
8df49ecfc1 | ||
|
|
526b9bd4c7 | ||
|
|
c67ff3d936 | ||
|
|
417b17da98 | ||
|
|
4ca19a4eb0 | ||
|
|
e834dfb7ee | ||
|
|
5e1ff74fb3 | ||
|
|
7a1d0f2ca7 | ||
|
|
b305522106 | ||
|
|
ff3495645a | ||
|
|
ddc000321f | ||
|
|
fe4e6af3b9 | ||
|
|
a1a83cc32c | ||
|
|
e37665218b | ||
|
|
2a876f4136 | ||
|
|
290d8e8a64 | ||
|
|
121bce529f | ||
|
|
1232d98573 | ||
|
|
208d9150b0 | ||
|
|
351bf7725e | ||
|
|
a547776766 | ||
|
|
86a8d20757 | ||
|
|
760c420f95 |
No files matched your search
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"Suffix": [".com", ".exe", ".dll", ".sys", ".scr", ".bat", ".cmd", ".ps1", ".vbs", ".wsf"],
|
||||
|
||||
"Labels": ["Pefile/White", "Script/White", "Pefile/General", "Script/General"],
|
||||
|
||||
"Detect": ["Pefile/General", "Script/General"],
|
||||
|
||||
"Resize": [224, 224, 1, "nearest"],
|
||||
|
||||
"Values": 95
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
from PIL import Image
|
||||
import numpy, pefile, os
|
||||
|
||||
def file_to_images(file_data, output_dir, file_name, target_size=(224, 224)):#
|
||||
wah = int(numpy.ceil(numpy.sqrt(len(file_data))))
|
||||
file_data = numpy.frombuffer(file_data, dtype=numpy.uint8)
|
||||
image_array = numpy.zeros((wah * wah,), dtype=numpy.uint8)
|
||||
image_array[:len(file_data)] = file_data
|
||||
image = Image.fromarray(image_array.reshape((wah, wah)), 'L')
|
||||
image = image.resize(target_size, Image.Resampling.NEAREST)
|
||||
image.save(os.path.join(output_dir, f"{file_name}.png"))
|
||||
|
||||
def check_file_type(file_path):
|
||||
try:
|
||||
shell_section = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"cry", "tvm", "dec", "enc", "vmp", "upx", "aes", "lzma", "press",
|
||||
"pack", "enigma", "protect", "secur"]
|
||||
|
||||
unknown_section = ["asmstub", "base", "bss", "clr_uef", "cursors",
|
||||
"engine", "fio", "fothk", "h~;", "icapsec", "malloc_h", "miniex",
|
||||
"mssmixer", "ndr64", "nsys_wr", "obr", "wow", "wow64svc", "wpp_sf",
|
||||
"pad", "pgae", "poolmi", "proxy", "qihoo", "res", "retpol", "uedbg",
|
||||
"rwexec", "rygs", "s:@", "sanontcp", "segm", "test", "tracesup",
|
||||
"transit", "trs_age", "wisevec"]
|
||||
|
||||
unimportant_section = ["viahwaes", "orpc", "nep", "ace", "extjmp",
|
||||
"no_bbt", "data", "page", "hexpthk"]
|
||||
|
||||
shells = shell_section + unimportant_section + unknown_section
|
||||
|
||||
ftype = str(f".{file_path.split('.')[-1]}").lower()
|
||||
|
||||
if ftype in [".bat", ".cmd", ".ps1", ".vbs", ".wsf", ".js", ".html"]:
|
||||
with open(file_path, 'rb') as f:
|
||||
return [f.read()]
|
||||
|
||||
match_data = []
|
||||
if ftype in [".com", ".exe", ".dll", ".sys", ".scr"]:
|
||||
with pefile.PE(file_path, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
section_name = section.Name.rstrip(b'\x00').decode('latin1')
|
||||
#if (section.Characteristics & 0x00000020 and not
|
||||
#any(shell in section_name.lower() for shell in shells)):
|
||||
if section.Characteristics & 0x00000020:
|
||||
match_data.append(section.get_data())
|
||||
return match_data
|
||||
return False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
def batch_file_to_images(input_dir, output_dir):
|
||||
for root, dirs, files in os.walk(input_dir):
|
||||
for file in files:
|
||||
try:
|
||||
file_path = os.path.join(root, file)
|
||||
file_data = check_file_type(file_path)
|
||||
if file_data:
|
||||
for i, data in enumerate(file_data, 1):
|
||||
print(i, file_path)
|
||||
relative_path = os.path.relpath(root, input_dir)
|
||||
output_path_dir = os.path.join(output_dir, relative_path)
|
||||
if not os.path.exists(output_path_dir):
|
||||
os.makedirs(output_path_dir)
|
||||
file_to_images(data, output_path_dir, f"{i}_{file}")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
input_directory = input('Enter the folder path: ')
|
||||
output_directory = "./Image_File"
|
||||
batch_file_to_images(input_directory, output_directory)
|
||||
input('Conversion Complete')
|
||||
@@ -0,0 +1,29 @@
|
||||
import os, hashlib
|
||||
|
||||
def calc_sha256(path):
|
||||
with open(path, "rb") as f:
|
||||
return hashlib.sha256(f.read()).digest()
|
||||
|
||||
def dedup_realtime(root):
|
||||
seen = set()
|
||||
deleted = 0
|
||||
for dirpath, _, files in os.walk(root):
|
||||
for name in files:
|
||||
p = os.path.join(dirpath, name)
|
||||
try:
|
||||
h = calc_sha256(p)
|
||||
except Exception:
|
||||
continue
|
||||
if h in seen:
|
||||
try:
|
||||
os.remove(p)
|
||||
print(p)
|
||||
deleted += 1
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
seen.add(h)
|
||||
print(f"deleted {deleted}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
dedup_realtime(r".\Image_File")
|
||||
@@ -0,0 +1,112 @@
|
||||
import tensorflow as tf
|
||||
import os, tf2onnx, numpy, random
|
||||
from PIL import Image
|
||||
from tensorflow.keras import *
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
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 load_dataset(train_dir, val_dir, val_split=0.01, batch_size=128, color_mode="grayscale"):
|
||||
channels = 1 if color_mode == "grayscale" else 3
|
||||
if train_dir == val_dir:
|
||||
file_paths, labels, class_indices = get_file_list(train_dir)
|
||||
combined = list(zip(file_paths, labels))
|
||||
random.shuffle(combined)
|
||||
file_paths[:], labels[:] = zip(*combined)
|
||||
num_val = int(val_split * len(file_paths))
|
||||
train_files = file_paths[num_val:]
|
||||
train_labels = labels[num_val:]
|
||||
val_files = file_paths[:num_val]
|
||||
val_labels = labels[:num_val]
|
||||
else:
|
||||
train_files, train_labels, class_indices = get_file_list(train_dir)
|
||||
val_files, val_labels, _ = get_file_list(val_dir)
|
||||
num_classes = len(class_indices)
|
||||
|
||||
def parse_fn(filename, label):
|
||||
img = tf.io.read_file(filename)
|
||||
img = tf.image.decode_image(img, channels=channels, expand_animations=False)
|
||||
img = tf.cast(img, tf.float32) / 255.0
|
||||
label = tf.one_hot(label, depth=num_classes)
|
||||
return img, label
|
||||
|
||||
train_ds = tf.data.Dataset.from_tensor_slices((train_files, train_labels))
|
||||
train_ds = train_ds.map(parse_fn, num_parallel_calls=tf.data.AUTOTUNE)
|
||||
train_ds = train_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)
|
||||
val_ds = tf.data.Dataset.from_tensor_slices((val_files, val_labels))
|
||||
val_ds = val_ds.map(parse_fn, num_parallel_calls=tf.data.AUTOTUNE)
|
||||
val_ds = val_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)
|
||||
train_ds.class_indices = class_indices
|
||||
return train_ds, val_ds
|
||||
|
||||
def gated_conv(x, strides):
|
||||
c = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='gelu')(x)
|
||||
g = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='sigmoid')(x)
|
||||
return layers.Multiply()([c, g])
|
||||
|
||||
def multi_conv(x, strides):
|
||||
convs = [layers.DepthwiseConv2D(kernel_size=k, strides=strides, padding='same')(x) for k in [1, 3, 5, 7]]
|
||||
x = layers.Concatenate()(convs)
|
||||
x = layers.BatchNormalization()(x)
|
||||
return layers.Activation('gelu')(x)
|
||||
|
||||
def se_block(x, reduction):
|
||||
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])
|
||||
|
||||
def build_model(input_shape, num_classes):
|
||||
i = layers.Input(shape=input_shape)
|
||||
x = multi_conv(i, strides=1)
|
||||
x = multi_conv(x, strides=2)
|
||||
x = gated_conv(x, strides=2)
|
||||
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 = layers.GlobalAveragePooling2D()(x)
|
||||
o = layers.Dense(num_classes, activation='softmax', dtype='float32')(x)
|
||||
return models.Model(i, o)
|
||||
|
||||
def lr_scheduler(epoch, lr):
|
||||
return 1e-3 * (0.7 ** epoch)
|
||||
|
||||
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)
|
||||
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', r'./Image_File')
|
||||
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)
|
||||
input("Training complete.")
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2025 87owo (PYAS Security)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
PYAS Security Software Development License Agreement
|
||||
Last updated: 2025-08-15
|
||||
|
||||
1. License & Ownership
|
||||
– A single device key may be required to activate the software or source code.
|
||||
– Non exclusive, non transferable, and for personal, internal use only.
|
||||
– 87owo reserves all rights. Intellectual property rights are not transferred.
|
||||
|
||||
2. Use & Prohibitions
|
||||
– Private redistribution, sublicensing, or rental is prohibited.
|
||||
– Private reverse engineering or removal of notices is prohibited.
|
||||
– Any of the above requires prior authorization from the original author.
|
||||
– Only personal internal use or modification of the source code is permitted.
|
||||
– Use of the software or source code for any illegal purpose is prohibited.
|
||||
|
||||
3. License Components
|
||||
– All project script archives and executable files are proprietary.
|
||||
|
||||
4. Updates & Terms
|
||||
– Updates to new versions may include patches, enhancements, or modifications.
|
||||
– Continued use constitutes your acceptance of these features or terms.
|
||||
|
||||
5. Warranty & Liability
|
||||
– This software is provided "as is," without warranty of any kind.
|
||||
– In no event shall there be any liability for consequential damages.
|
||||
|
||||
© 2020–2025 87owo (PYAS Security). All rights reserved.
|
||||
@@ -1,7 +1,7 @@
|
||||
import os, gc, re, sys, time, json, base64
|
||||
import os, gc, re, sys, time, json, shutil
|
||||
import ctypes, ctypes.wintypes, threading
|
||||
import requests, pyperclip, winreg, shutil
|
||||
import traceback, hashlib, msvcrt
|
||||
import requests, webbrowser, winreg, msvcrt
|
||||
import traceback, hashlib
|
||||
|
||||
from PYAS_Config import *
|
||||
from PYAS_Engine import *
|
||||
@@ -89,6 +89,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.python = sys.executable
|
||||
self.process_timer = QTimer(self)
|
||||
|
||||
self.sign = sign_scanner()
|
||||
self.sign.init_windll(["wintrust"])
|
||||
self.model = model_scanner()
|
||||
self.model.load_path(self.path_models)
|
||||
self.rule = rule_scanner()
|
||||
@@ -112,17 +114,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
"traditional_switch", "simplified_switch", "english_switch",
|
||||
]
|
||||
self.pyas_default = {
|
||||
"version": "3.3.2",
|
||||
"version": "3.3.5",
|
||||
"product": "00000-00000-00000-00000-00000",
|
||||
"language": "english_switch",
|
||||
"theme": "white_switch",
|
||||
"sensitive": 95,
|
||||
"custom_rule": {},
|
||||
"process_switch": True,
|
||||
"document_switch": True,
|
||||
"system_switch": True,
|
||||
"driver_switch": True,
|
||||
"network_switch": True,
|
||||
"custom_rule": [],
|
||||
}
|
||||
self.pass_windows = [
|
||||
{"exe": "System Idle Process", "class": "", "title": ""},
|
||||
@@ -131,14 +133,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
{"exe": "explorer.exe", "class": "", "title": ""},
|
||||
]
|
||||
self.block_replace = {
|
||||
"BOOT_BLOCK": "引導行為攔截",
|
||||
"REG_BLOCK": "註冊表行為攔截",
|
||||
"FILE_BLOCK": "檔案行為攔截",
|
||||
"RANSOM_BLOCK": "勒索行為攔截",
|
||||
"CLR_BLOCK": "托管行為攔截",
|
||||
"SHELLCODE_BLOCK": "殼碼行為攔截",
|
||||
"BOOT_BLOCK": "引導行為攔截",
|
||||
"REG_BLOCK": "註冊表行為攔截",
|
||||
"CLR_BLOCK": "執行環境攔截",
|
||||
"SHELLCODE_BLOCK": "殼碼加載攔截",
|
||||
"THREAD_BLOCK": "線程行為攔截",
|
||||
"INJECT_BLOCK": "注入行為攔截",
|
||||
"PROC_BLOCK": "進程行為攔截",
|
||||
"REMOTE_BLOCK": "遠控行為攔截",
|
||||
"SCREEN_BLOCK": "監控行為攔截",
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
@@ -147,6 +152,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.file_pyas = self.norm_path(sys.argv[0])
|
||||
self.args_pyas = sys.argv[1:]
|
||||
self.path_pyas = os.path.dirname(self.file_pyas)
|
||||
self.pid_pyas = int(os.getpid())
|
||||
|
||||
self.path_appdata = os.environ.get("APPDATA")
|
||||
self.path_name = os.environ.get("USERNAME")
|
||||
@@ -228,7 +234,14 @@ class MainWindow_Controller(QMainWindow):
|
||||
return default_value.copy()
|
||||
|
||||
def load_config(self):
|
||||
existed = os.path.exists(self.file_config)
|
||||
self.pyas_config = self.read_config(self.file_config, self.pyas_default)
|
||||
if existed:
|
||||
dv = str(self.pyas_default.get("version", "")).strip()
|
||||
cv = str(self.pyas_config.get("version", "")).strip()
|
||||
if dv and dv != cv:
|
||||
self.pyas_config["version"] = dv
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
|
||||
def config_list(self, key):
|
||||
if key not in self.pyas_config:
|
||||
@@ -257,6 +270,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.last_widget = self.widgets.get("home_window")
|
||||
self.setup_theme_mapping()
|
||||
self.setup_process_menu()
|
||||
self.setup_scan_menu()
|
||||
|
||||
def create_shadow(self):
|
||||
shadow = QGraphicsDropShadowEffect(self)
|
||||
@@ -334,11 +348,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
if checked:
|
||||
self.start_daemon_thread(self.protect_system_thread)
|
||||
elif name == "driver_switch":
|
||||
if checked:
|
||||
self.install_system_driver()
|
||||
if not checked:
|
||||
self.stop_system_driver()
|
||||
elif self.install_system_driver():
|
||||
self.start_daemon_thread(self.pipe_server_thread)
|
||||
else:
|
||||
self.stop_system_driver()
|
||||
self.send_message("驅動防護啟用失敗,請重啟裝置來修復錯誤", "warn", True)
|
||||
elif name == "network_switch":
|
||||
if checked:
|
||||
self.start_daemon_thread(self.protect_net_thread)
|
||||
@@ -388,11 +403,18 @@ class MainWindow_Controller(QMainWindow):
|
||||
if not widget.property("_connected_toggle"):
|
||||
widget.toggled.connect(lambda checked, b=widget: self.toggle_switch_text(b, checked))
|
||||
widget.setProperty("_connected_toggle", True)
|
||||
else:
|
||||
if not hasattr(widget, "_origin_text"):
|
||||
widget._origin_text = self.get_widget_text(widget)
|
||||
widget.setText(self.trans(lang, widget._origin_text))
|
||||
|
||||
elif hasattr(widget, "setText"):
|
||||
if not hasattr(widget, "_origin_text"):
|
||||
widget._origin_text = self.get_widget_text(widget)
|
||||
widget.setText(self.trans(lang, widget._origin_text))
|
||||
if name in ["log_text","license_text"]:
|
||||
pass
|
||||
else:
|
||||
if not hasattr(widget, "_origin_text"):
|
||||
widget._origin_text = self.get_widget_text(widget)
|
||||
widget.setText(self.trans(lang, widget._origin_text))
|
||||
|
||||
if hasattr(widget, "setStyleSheet"):
|
||||
if not hasattr(widget, "_origin_style"):
|
||||
@@ -555,6 +577,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.pyas_config["system_switch"] = False
|
||||
self.pyas_config["driver_switch"] = False
|
||||
self.pyas_config["network_switch"] = False
|
||||
|
||||
self.stop_button()
|
||||
self.stop_system_driver()
|
||||
|
||||
self.minimize_button()
|
||||
@@ -601,7 +625,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
if not isinstance(message, str):
|
||||
message = str(message)
|
||||
if message and translate:
|
||||
message = self.trans(self.pyas_config["language"], message)
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
message = self.trans(lang, message)
|
||||
|
||||
m = mode.lower()
|
||||
if m == "quest":
|
||||
@@ -622,16 +647,19 @@ class MainWindow_Controller(QMainWindow):
|
||||
elif m == "save":
|
||||
select = QFileDialog.getSaveFileName(self, message)[0]
|
||||
|
||||
select = select or None
|
||||
now = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
output = f"[{now}] | {mode} | {message} | {select}"
|
||||
log_widget = self.widgets.get("log_text")
|
||||
if log_widget:
|
||||
log_widget.append(output)
|
||||
return select or False
|
||||
return select
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def invoke_method(self, obj, method, *args):
|
||||
q_args = []
|
||||
for arg in args:
|
||||
@@ -643,14 +671,25 @@ class MainWindow_Controller(QMainWindow):
|
||||
q_args.append(Q_ARG("QString", str(arg)))
|
||||
QMetaObject.invokeMethod(obj, method, Qt.QueuedConnection, *q_args)
|
||||
|
||||
def norm_path(self, path):
|
||||
if isinstance(path, (list, tuple, set)):
|
||||
t = type(path)
|
||||
return t(self.norm_path(p) for p in path)
|
||||
def norm_path(self, path, must_exist=True):
|
||||
if isinstance(path, list):
|
||||
return [p for p in (self.norm_path(x, must_exist) for x in path) if p]
|
||||
if isinstance(path, tuple):
|
||||
return tuple(p for p in (self.norm_path(x, must_exist) for x in path) if p)
|
||||
if isinstance(path, set):
|
||||
return {p for p in (self.norm_path(x, must_exist) for x in path) if p}
|
||||
if isinstance(path, str):
|
||||
return os.path.normpath(os.path.abspath(path))
|
||||
ap = os.path.normpath(os.path.abspath(path))
|
||||
return ap if (not must_exist or os.path.exists(ap)) else None
|
||||
return path
|
||||
|
||||
def path_equal(self, a, b):
|
||||
pa = self.norm_path(a, must_exist=False)
|
||||
pb = self.norm_path(b, must_exist=False)
|
||||
if not pa or not pb:
|
||||
return False
|
||||
return os.path.normcase(pa) == os.path.normcase(pb)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@Slot()
|
||||
@@ -678,9 +717,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
@Slot(str)
|
||||
def slot_scan_result(self, msg):
|
||||
self.widgets["progress_text"].setText(msg)
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
self.widgets["progress_text"].setText(msg)
|
||||
self.widgets["stop_button"].hide()
|
||||
self.widgets["method_button"].show()
|
||||
self.send_message(msg, "notify", True)
|
||||
@@ -695,8 +734,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.scan_start = time.time()
|
||||
self.last_widget = self.widgets.get("scan_window")
|
||||
self.scan_reset_signal.emit()
|
||||
self.progress_title_signal.emit(self.trans(self.pyas_config["language"], "正在掃描"))
|
||||
self.scan_progress_signal.emit(self.trans(self.pyas_config["language"], "正在初始化中"))
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.scan_progress_signal.emit(self.trans(lang, "正在初始化中"))
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
@@ -730,21 +770,27 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.is_in_whitelist(norm_path):
|
||||
continue
|
||||
self.scan_count += 1
|
||||
|
||||
state = self.scan_engine(norm_path)
|
||||
if state:
|
||||
self.virus_results.append(norm_path)
|
||||
self.scan_add_virus_signal.emit(f"[{state}] > {norm_path}")
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "正在掃描"))
|
||||
self.scan_progress_signal.emit(norm_path)
|
||||
gc.collect()
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
finally:
|
||||
elapsed = int(time.time() - self.scan_start)
|
||||
count = len(self.virus_results)
|
||||
elapsed = int(time.time() - self.scan_start)
|
||||
result = (
|
||||
f"發現 {count} 個病毒,耗時 {elapsed} 秒,共掃描 {self.scan_count} 檔案"
|
||||
if count else f"未發現病毒,耗時 {elapsed} 秒,共掃描 {self.scan_count} 檔案")
|
||||
self.progress_title_signal.emit(self.trans(self.pyas_config["language"], "病毒掃描"))
|
||||
self.scan_result_signal.emit(self.trans(self.pyas_config["language"], result))
|
||||
f"發現 {count} 個病毒,共掃描 {self.scan_count} 檔案,耗時 {elapsed} 秒"
|
||||
if count else f"未發現病毒,共掃描 {self.scan_count} 檔案,耗時 {elapsed} 秒")
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "病毒掃描"))
|
||||
self.scan_result_signal.emit(self.trans(lang, result))
|
||||
|
||||
def yield_files(self, targets):
|
||||
if isinstance(targets, str):
|
||||
@@ -760,6 +806,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def scan_engine(self, file_path):
|
||||
try:
|
||||
if self.sign.sign_verify(file_path):
|
||||
return False
|
||||
|
||||
label, level = self.model.model_scan(file_path)
|
||||
if label and level >= self.pyas_config.get("sensitive", 95):
|
||||
return f"{label}.{level}"
|
||||
@@ -768,39 +817,106 @@ class MainWindow_Controller(QMainWindow):
|
||||
label, level = self.rule.yara_scan(file_path)
|
||||
if label and level:
|
||||
return f"{label}.{level}"
|
||||
return False
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
return None
|
||||
return None
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def solve_button(self):
|
||||
deleted = 0
|
||||
virus_list = self.widgets["virus_list"]
|
||||
start_ts = time.time()
|
||||
|
||||
for i in reversed(range(virus_list.count())):
|
||||
item = virus_list.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
file_path = self.norm_path(item.text().split(">")[1].strip())
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "正在刪除"))
|
||||
self.scan_progress_signal.emit(file_path)
|
||||
|
||||
try:
|
||||
file_path = self.norm_path(item.text().split(">")[1].strip())
|
||||
os.remove(file_path)
|
||||
deleted += 1
|
||||
virus_list.takeItem(i)
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
virus_list.takeItem(i)
|
||||
self.widgets["solve_button"].hide()
|
||||
msg = f"已清理 {deleted} 個病毒檔案" if deleted else "無檔案被刪除"
|
||||
self.widgets["progress_text"].setText(self.trans(self.pyas_config["language"], msg))
|
||||
QApplication.processEvents()
|
||||
|
||||
count = virus_list.count()
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
elapsed = int(time.time() - start_ts)
|
||||
result = (
|
||||
f"剩餘 {count} 個病毒,共刪除 {deleted} 檔案,耗時 {elapsed} 秒"
|
||||
if count else f"未剩餘病毒,共刪除 {deleted} 檔案,耗時 {elapsed} 秒")
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "病毒掃描"))
|
||||
self.scan_result_signal.emit(self.trans(lang, result))
|
||||
|
||||
def stop_button(self):
|
||||
self.scan_running = False
|
||||
self.widgets["stop_button"].hide()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def setup_scan_menu(self):
|
||||
widget = self.widgets.get("virus_list")
|
||||
if widget:
|
||||
widget.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
widget.customContextMenuRequested.connect(self.show_scan_menu)
|
||||
|
||||
def show_scan_menu(self, pos):
|
||||
widget = self.widgets.get("virus_list")
|
||||
if not widget:
|
||||
return
|
||||
item = widget.itemAt(pos)
|
||||
if not item:
|
||||
return
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
menu = QMenu()
|
||||
copy_action = menu.addAction(self.trans(lang, "複製路徑"))
|
||||
del_action = menu.addAction(self.trans(lang, "刪除檔案"))
|
||||
white_action = menu.addAction(self.trans(lang, "增加白名單"))
|
||||
act = menu.exec(widget.viewport().mapToGlobal(pos))
|
||||
|
||||
text = item.text()
|
||||
if ">" in text:
|
||||
path = text.split(">", 1)[1].strip()
|
||||
else:
|
||||
path = text.strip()
|
||||
|
||||
if act == copy_action:
|
||||
QGuiApplication.clipboard().setText(str(path or "").replace("/", "\\"))
|
||||
|
||||
elif act == del_action:
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要刪除檔案嗎?", "quest"):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
|
||||
elif act == white_action:
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def list_process(self):
|
||||
pe = self.get_process_entry()
|
||||
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
result, success = [], self.kernel32.Process32First(snapshot, ctypes.byref(pe))
|
||||
|
||||
while success:
|
||||
pid = pe.th32ProcessID
|
||||
name, file_path = (None, None)
|
||||
@@ -813,6 +929,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
name = raw.decode("mbcs", errors="replace") or None
|
||||
except Exception:
|
||||
name = None
|
||||
|
||||
result.append((pid, name, file_path))
|
||||
success = self.kernel32.Process32Next(snapshot, ctypes.byref(pe))
|
||||
self.kernel32.CloseHandle(snapshot)
|
||||
@@ -823,6 +940,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.proc_pid_map = [pid for pid, _, _ in procs]
|
||||
widget = self.widgets.get("manage_list")
|
||||
info_text = self.widgets.get("info_text")
|
||||
|
||||
if widget:
|
||||
model = QStringListModel([f"{name or 'None'} | {pid} | {file_path or 'None'}" for pid, name, file_path in procs])
|
||||
widget.setModel(model)
|
||||
@@ -844,6 +962,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def start_process_timer(self):
|
||||
if hasattr(self, "process_timer") and self.process_timer.isActive():
|
||||
return
|
||||
|
||||
self.process_timer = QTimer(self)
|
||||
self.process_timer.timeout.connect(self.refresh_process)
|
||||
self.process_timer.start(0)
|
||||
@@ -865,19 +984,36 @@ class MainWindow_Controller(QMainWindow):
|
||||
idx = widget.indexAt(pos)
|
||||
if not idx.isValid() or idx.row() >= len(self.proc_pid_map):
|
||||
return
|
||||
pid = self.proc_pid_map[idx.row()]
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
menu = QMenu()
|
||||
kill_action = menu.addAction("結束進程")
|
||||
if menu.exec(widget.viewport().mapToGlobal(pos)) == kill_action:
|
||||
copy_action = menu.addAction(self.trans(lang, "複製路徑"))
|
||||
kill_action = menu.addAction(self.trans(lang, "結束進程"))
|
||||
white_action = menu.addAction(self.trans(lang, "增加白名單"))
|
||||
act = menu.exec(widget.viewport().mapToGlobal(pos))
|
||||
|
||||
pid = self.proc_pid_map[idx.row()]
|
||||
name, file_path = self.get_exe_info(pid)
|
||||
|
||||
if act == copy_action:
|
||||
QGuiApplication.clipboard().setText(str(file_path or "").replace("/", "\\"))
|
||||
|
||||
elif act == kill_action:
|
||||
self.kill_process(pid)
|
||||
|
||||
elif act == white_action:
|
||||
if file_path and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
file_path = self.norm_path(file_path or "")
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
|
||||
def kill_process(self, pid):
|
||||
try:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if h:
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if hasattr(self, "file_pyas") and file_path == getattr(self, "file_pyas", None):
|
||||
self.close()
|
||||
if self.path_equal(file_path, self.file_pyas):
|
||||
self.close_button()
|
||||
else:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
@@ -962,6 +1098,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def repair_system_mbr(self):
|
||||
if not hasattr(self, "mbr_backup") or not self.mbr_backup:
|
||||
return
|
||||
|
||||
for drive, mbr_value in self.mbr_backup.items():
|
||||
drive_path = rf"\\.\PhysicalDrive{drive}"
|
||||
try:
|
||||
@@ -980,9 +1117,11 @@ class MainWindow_Controller(QMainWindow):
|
||||
wallpaper = os.path.join(self.path_system, "web", "wallpaper", "Windows", "img0.jpg")
|
||||
if not os.path.exists(wallpaper):
|
||||
return
|
||||
|
||||
key = r"Control Panel\\Desktop"
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_SET_VALUE) as reg:
|
||||
winreg.SetValueEx(reg, "Wallpaper", 0, winreg.REG_SZ, wallpaper)
|
||||
|
||||
theme_dir = os.path.join(self.path_appdata, "Microsoft", "Windows", "Themes")
|
||||
for fname in ["TranscodedWallpaper", "TranscodedWallpaper.tmp"]:
|
||||
fpath = os.path.join(theme_dir, fname)
|
||||
@@ -991,6 +1130,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
os.remove(fpath)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cache_dir = os.path.join(theme_dir, "CachedFiles")
|
||||
if os.path.exists(cache_dir):
|
||||
try:
|
||||
@@ -1100,11 +1240,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.send_message("請選擇要攔截的軟體彈窗", "quest", True):
|
||||
while True:
|
||||
QApplication.processEvents()
|
||||
title, class_name, process_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if process_name and not any(self.window_rule_match(item, process_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要攔截 {process_name} ({title}) 嗎?", "quest", True):
|
||||
if self.add_window_rule(self.pyas_config["block_list"], process_name, class_name, title):
|
||||
self.send_message(f"成功增加到彈窗攔截: {process_name} ({title})", "info", True)
|
||||
|
||||
title, class_name, proc_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if proc_name and not any(self.window_rule_match(item, proc_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要攔截 {proc_name} ({title}) 嗎?", "quest", True):
|
||||
if self.add_window_rule(self.pyas_config["block_list"], proc_name, class_name, title):
|
||||
self.send_message(f"成功增加到彈窗攔截: {proc_name} ({title})", "info", True)
|
||||
else:
|
||||
self.send_message("已存在彈窗攔截", "info", True)
|
||||
break
|
||||
@@ -1116,32 +1257,33 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.send_message("請選擇要取消攔截的軟體彈窗", "quest", True):
|
||||
while True:
|
||||
QApplication.processEvents()
|
||||
title, class_name, process_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if process_name and not any(self.window_rule_match(item, process_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要取消攔截 {process_name} ({title}) 嗎?", "quest", True):
|
||||
if self.remove_window_rule(self.pyas_config["block_list"], process_name, class_name, title):
|
||||
self.send_message(f"成功取消彈窗攔截: {process_name} ({title})", "info", True)
|
||||
|
||||
title, class_name, proc_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if proc_name and not any(self.window_rule_match(item, proc_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要取消攔截 {proc_name} ({title}) 嗎?", "quest", True):
|
||||
if self.remove_window_rule(self.pyas_config["block_list"], proc_name, class_name, title):
|
||||
self.send_message(f"成功取消彈窗攔截: {proc_name} ({title})", "info", True)
|
||||
else:
|
||||
self.send_message("未找到彈窗攔截", "info", True)
|
||||
break
|
||||
self.start_daemon_thread(self.popup_intercept_thread)
|
||||
|
||||
def window_rule_match(self, item, process_name, class_name, title):
|
||||
for k, v in zip(["exe", "class", "title"], [process_name, class_name, title]):
|
||||
def window_rule_match(self, item, proc_name, class_name, title):
|
||||
for k, v in zip(["exe", "class", "title"], [proc_name, class_name, title]):
|
||||
if item.get(k, "") and item.get(k, "") != v:
|
||||
return False
|
||||
return True
|
||||
|
||||
def add_window_rule(self, rule_list, process_name, class_name, title):
|
||||
if not any(self.window_rule_match(item, process_name, class_name, title) for item in rule_list):
|
||||
rule_list.append({"exe": process_name, "class": class_name, "title": title})
|
||||
def add_window_rule(self, rule_list, proc_name, class_name, title):
|
||||
if not any(self.window_rule_match(item, proc_name, class_name, title) for item in rule_list):
|
||||
rule_list.append({"exe": proc_name, "class": class_name, "title": title})
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_window_rule(self, rule_list, process_name, class_name, title):
|
||||
def remove_window_rule(self, rule_list, proc_name, class_name, title):
|
||||
before = len(rule_list)
|
||||
rule_list[:] = [item for item in rule_list if not self.window_rule_match(item, process_name, class_name, title)]
|
||||
rule_list[:] = [item for item in rule_list if not self.window_rule_match(item, proc_name, class_name, title)]
|
||||
if len(rule_list) < before:
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
return True
|
||||
@@ -1155,12 +1297,26 @@ class MainWindow_Controller(QMainWindow):
|
||||
rules = self.pyas_config.get("block_list", [])
|
||||
if not rules:
|
||||
continue
|
||||
|
||||
for hWnd in self.get_all_windows():
|
||||
title, class_name, process_name = self.get_window_info(hWnd)
|
||||
if (not any(self.window_rule_match(item, process_name, class_name, title) for item in self.pass_windows) and
|
||||
any(self.window_rule_match(item, process_name, class_name, title) for item in rules)):
|
||||
title, class_name, proc_name = self.get_window_info(hWnd)
|
||||
if (not any(self.window_rule_match(item, proc_name, class_name, title) for item in self.pass_windows) and
|
||||
any(self.window_rule_match(item, proc_name, class_name, title) for item in rules)):
|
||||
closed = False
|
||||
|
||||
for msg in [0x0010, 0x0002, 0x0012, 0x0112]:
|
||||
self.user32.SendMessageW(hWnd, msg, 0xF060, 0)
|
||||
if self.user32.SendMessageW(hWnd, msg, 0xF060, 0) != 0:
|
||||
closed = True
|
||||
break
|
||||
|
||||
if not closed and proc_name:
|
||||
pid = ctypes.c_ulong()
|
||||
self.user32.GetWindowThreadProcessId(hWnd, ctypes.byref(pid))
|
||||
if pid.value:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid.value)
|
||||
if h:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
@@ -1184,73 +1340,79 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.user32.GetClassNameW(hWnd, class_name, 256)
|
||||
pid = ctypes.c_ulong()
|
||||
self.user32.GetWindowThreadProcessId(hWnd, ctypes.byref(pid))
|
||||
process_name = ""
|
||||
|
||||
proc_name = ""
|
||||
try:
|
||||
process_name = self.get_process_name_by_pid(pid.value)
|
||||
proc_name = self.get_process_name(pid.value)
|
||||
except Exception:
|
||||
pass
|
||||
return str(title.value), str(class_name.value), process_name
|
||||
return str(title.value), str(class_name.value), proc_name
|
||||
|
||||
def get_process_name_by_pid(self, pid):
|
||||
def get_process_name(self, pid):
|
||||
name, file_path = self.get_exe_info(pid)
|
||||
if name:
|
||||
return name
|
||||
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
entry = PROCESSENTRY32()
|
||||
entry.dwSize = ctypes.sizeof(PROCESSENTRY32)
|
||||
name = ""
|
||||
|
||||
result = ""
|
||||
if self.kernel32.Process32First(snapshot, ctypes.byref(entry)):
|
||||
while True:
|
||||
if entry.th32ProcessID == pid:
|
||||
name = entry.szExeFile.decode("mbcs").rstrip("\x00")
|
||||
try:
|
||||
result = entry.szExeFile.decode("mbcs").rstrip("\x00")
|
||||
except Exception:
|
||||
result = ""
|
||||
break
|
||||
if not self.kernel32.Process32Next(snapshot, ctypes.byref(entry)):
|
||||
break
|
||||
self.kernel32.CloseHandle(snapshot)
|
||||
return name
|
||||
return result
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def whitelist_button(self):
|
||||
self.config_list("white_list")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info")
|
||||
|
||||
def whitelist_button_2(self):
|
||||
self.config_list("white_list")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要移除白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="remove", with_hash=True)
|
||||
self.send_message(f"成功移除白名單,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要移除白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="remove", with_hash=True)
|
||||
self.send_message(f"成功移除白名單,共 {n} 個檔案", "info")
|
||||
|
||||
def quarantine_button(self):
|
||||
self.config_list("quarantine")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要增加到隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="add", with_hash=True, lock_func=self.lock_file)
|
||||
if n > 0:
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要增加到隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="add", with_hash=True, lock_func=self.lock_file)
|
||||
if n > 0:
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info")
|
||||
|
||||
def quarantine_button_2(self):
|
||||
self.config_list("quarantine")
|
||||
quarantine_dir = os.path.join(self.path_config, "quarantine")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要移除隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="remove", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功移除隔離區,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要移除隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="remove", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功移除隔離區,共 {n} 個檔案", "info")
|
||||
|
||||
def add_to_quarantine(self, files):
|
||||
self.config_list("quarantine")
|
||||
return self.manage_named_list("quarantine", files, action="add", with_hash=True, lock_func=self.lock_file)
|
||||
|
||||
def is_in_whitelist(self, file_path):
|
||||
norm_path = self.norm_path(file_path)
|
||||
file_hash = self.calc_file_hash(norm_path)
|
||||
return any(item["file"] == norm_path and item["hash"] == file_hash
|
||||
for item in self.pyas_config.get("white_list", []))
|
||||
p = self.norm_path(file_path)
|
||||
if not p:
|
||||
return False
|
||||
file_hash = self.calc_file_hash(p)
|
||||
return any(os.path.normcase(item.get("file","")) == os.path.normcase(p) and
|
||||
item.get("hash","") == file_hash for item in self.pyas_config.get("white_list", []))
|
||||
|
||||
def init_whitelist(self):
|
||||
self.manage_named_list("white_list", [self.file_pyas], action="add", with_hash=True)
|
||||
@@ -1260,9 +1422,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
def manage_named_list(self, list_key, files, action="add", with_hash=True, lock_func=None):
|
||||
target_list = self.config_list(list_key)
|
||||
files = files or []
|
||||
norm_paths = self.norm_path(files)
|
||||
norm_paths = self.norm_path(files, must_exist=True)
|
||||
if isinstance(norm_paths, str):
|
||||
norm_paths = [norm_paths]
|
||||
norm_paths = [norm_paths] if norm_paths else []
|
||||
|
||||
n = 0
|
||||
if action == "add":
|
||||
@@ -1302,6 +1464,51 @@ class MainWindow_Controller(QMainWindow):
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def website_button(self):
|
||||
try:
|
||||
webbrowser.open("https://github.com/87owo/PYAS")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
return False
|
||||
|
||||
def update_button(self):
|
||||
try:
|
||||
current = str(self.pyas_config.get("version", "")).strip()
|
||||
page = "https://github.com/87owo/PYAS/releases"
|
||||
latest = ""
|
||||
try:
|
||||
j = requests.get("https://api.github.com/repos/87owo/PYAS/releases/latest", headers={"Accept": "application/vnd.github+json", "User-Agent": "PYAS"}, timeout=10).json()
|
||||
latest = (j.get("tag_name") or j.get("name") or "").strip()
|
||||
page = j.get("html_url") or page
|
||||
except Exception:
|
||||
try:
|
||||
u = requests.get("https://github.com/87owo/PYAS/releases/latest", headers={"User-Agent": "PYAS"}, allow_redirects=True, timeout=10).url
|
||||
page = u or page
|
||||
m = re.search(r"/tag/([^/]+)$", str(u))
|
||||
latest = m.group(1).strip() if m else ""
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
rl = re.sub(r"^[vV]\s*", "", str(latest))
|
||||
cl = re.sub(r"^[vV]\s*", "", str(current))
|
||||
tr = tuple(int(x) for x in re.findall(r"\d+", rl))
|
||||
tl = tuple(int(x) for x in re.findall(r"\d+", cl))
|
||||
if tr and (not tl or tr > tl):
|
||||
if self.send_message(f"發現新版本 {latest},您確定要前往更新嗎?", "quest", True):
|
||||
webbrowser.open(page)
|
||||
else:
|
||||
if cl:
|
||||
self.send_message(f"當前已是最新版本 {current}", "info", True)
|
||||
else:
|
||||
webbrowser.open(page)
|
||||
return True
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def lock_file(self, file, lock):
|
||||
@@ -1358,13 +1565,14 @@ class MainWindow_Controller(QMainWindow):
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if file_path and os.path.exists(file_path) and not self.is_in_whitelist(file_path):
|
||||
if file_path and not self.is_in_whitelist(file_path):
|
||||
self.suspend_process(h, True)
|
||||
state = self.scan_engine(file_path)
|
||||
if state:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.send_message(f"進程防護攔截 | 靜態掃描攔截 | {pid} | {file_path} | None", "notify", True)
|
||||
self.send_message(f"進程防護 | 靜態掃描攔截 | {pid} | {file_path} | None", "notify", True)
|
||||
self.suspend_process(h, False)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
@@ -1411,8 +1619,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
####################################################################################################
|
||||
|
||||
def protect_file_thread(self):
|
||||
FILE_LIST_DIRECTORY = 0x0001
|
||||
hDir = self.kernel32.CreateFileW(self.path_user, FILE_LIST_DIRECTORY, 0x00000007, None, 3, 0x02000000, None)
|
||||
hDir = self.kernel32.CreateFileW(self.path_user, 0x0001, 0x00000007, None, 3, 0x02000000, None)
|
||||
buffer = ctypes.create_string_buffer(65536)
|
||||
|
||||
while self.pyas_config.get("document_switch", False):
|
||||
@@ -1430,7 +1637,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
state = self.scan_engine(file_path)
|
||||
if state:
|
||||
self.add_to_quarantine([file_path])
|
||||
self.send_message(f"檔案防護攔截 | 靜態掃描攔截 | None | {file_path} | None", "notify", True)
|
||||
self.send_message(f"檔案防護 | 靜態掃描攔截 | None | {file_path} | None", "notify", True)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
self.kernel32.CloseHandle(hDir)
|
||||
@@ -1458,15 +1665,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
try:
|
||||
connections = set()
|
||||
size = ctypes.wintypes.DWORD()
|
||||
AF_INET = 2
|
||||
TCP_TABLE_OWNER_PID_ALL = 5
|
||||
|
||||
ret = self.iphlpapi.GetExtendedTcpTable(None, ctypes.byref(size), True, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)
|
||||
ret = self.iphlpapi.GetExtendedTcpTable(None, ctypes.byref(size), True, 2, 5, 0)
|
||||
if ret != 122:
|
||||
raise ctypes.WinError(ret)
|
||||
|
||||
buf = ctypes.create_string_buffer(size.value)
|
||||
ret = self.iphlpapi.GetExtendedTcpTable(buf, ctypes.byref(size), True, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)
|
||||
ret = self.iphlpapi.GetExtendedTcpTable(buf, ctypes.byref(size), True, 2, 5, 0)
|
||||
if ret != 0:
|
||||
raise ctypes.WinError(ret)
|
||||
|
||||
@@ -1486,17 +1690,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
def handle_new_connection(self, key):
|
||||
pid, remote_addr, remote_port = key
|
||||
try:
|
||||
h_process = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h_process:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
file_path = self.norm_path(self.get_process_file(h_process))
|
||||
remote_ip = f"{remote_addr & 0xFF}.{(remote_addr >> 8) & 0xFF}.{(remote_addr >> 16) & 0xFF}.{(remote_addr >> 24) & 0xFF}"
|
||||
|
||||
if file_path and os.path.exists(file_path) and not self.is_in_whitelist(file_path):
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if file_path and not self.is_in_whitelist(file_path):
|
||||
if hasattr(self.rule, "network") and remote_ip in self.rule.network:
|
||||
self.kernel32.TerminateProcess(h_process, 0)
|
||||
self.send_message(f"網路防護攔截 | 規則列表攔截 | {pid} | {file_path} | {remote_ip}", "notify", True)
|
||||
self.kernel32.CloseHandle(h_process)
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.send_message(f"網路防護 | 規則列表攔截 | {pid} | {file_path} | {remote_ip}", "notify", True)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
@@ -1556,8 +1760,51 @@ class MainWindow_Controller(QMainWindow):
|
||||
####################################################################################################
|
||||
|
||||
def pipe_server_thread(self):
|
||||
# The source code cannot be made public due to security concerns
|
||||
pass
|
||||
pipe_path = r"\\.\pipe\PYAS_Output_Pipe"
|
||||
PIPE_BUF_SIZE = 65536
|
||||
|
||||
while self.pyas_config.get("driver_switch", False):
|
||||
try:
|
||||
hPipe = self.kernel32.CreateNamedPipeW(pipe_path, 0x00000001, 0x00000004 | 0x00000002, 1, PIPE_BUF_SIZE, PIPE_BUF_SIZE, 0, None)
|
||||
if hPipe == ctypes.c_void_p(-1).value:
|
||||
time.sleep(0.2)
|
||||
continue
|
||||
|
||||
while self.pyas_config.get("driver_switch", False):
|
||||
if not self.kernel32.ConnectNamedPipe(hPipe, None) and self.kernel32.GetLastError() != 535:
|
||||
time.sleep(0.2)
|
||||
continue
|
||||
|
||||
buf = ctypes.create_string_buffer(PIPE_BUF_SIZE)
|
||||
bytes_read = ctypes.c_ulong(0)
|
||||
if not self.kernel32.ReadFile(hPipe, buf, PIPE_BUF_SIZE, ctypes.byref(bytes_read), None) or bytes_read.value == 0:
|
||||
self.kernel32.DisconnectNamedPipe(hPipe)
|
||||
time.sleep(0.2)
|
||||
continue
|
||||
|
||||
msg = buf.raw[:bytes_read.value].decode("utf-8", errors="ignore")
|
||||
parts = [p.strip() for p in msg.split("|", 3)]
|
||||
if len(parts) == 4:
|
||||
rules, pid, raw_path, target = parts
|
||||
for old, new in self.block_replace.items():
|
||||
rules = rules.replace(old, new)
|
||||
self.send_message(f"驅動防護 | {rules} | {pid} | {raw_path} | {target}", "notify", True)
|
||||
|
||||
file_path = self.norm_path(self.device_path_to_drive(raw_path))
|
||||
if not self.is_in_whitelist(file_path) and not self.sign.sign_verify(file_path):
|
||||
try:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, int(pid.strip()))
|
||||
if h:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
self.kernel32.DisconnectNamedPipe(hPipe)
|
||||
self.kernel32.CloseHandle(hPipe)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
time.sleep(0.5)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
|
||||
+5
-3
@@ -20,7 +20,7 @@ translate_dict = {
|
||||
"載":"载","誤":"误","報":"报","鏈":"链","竄":"窜","蹤":"踪","與":"与",
|
||||
"異":"异","實":"实","時":"时","彈":"弹","窗":"窗","暫":"暂","訂":"订",
|
||||
"資":"资","產":"产","金":"密","離":"离","規":"规","則":"则","來":"来",
|
||||
"殼":"壳","碼":"码","線":"线",},
|
||||
"殼":"壳","碼":"码","線":"线","餘":"余",},
|
||||
|
||||
"english_switch": {"開啟":"On","關閉":"Off","开启":"On","关闭":"Off",",":", ",
|
||||
"立即刪除":"Delete Now","安全模式":"Safe Mode","官方網站":"Official Website ",
|
||||
@@ -71,8 +71,10 @@ translate_dict = {
|
||||
"沙箱":"Sandbox ", "虛擬":"Virtual ", "隔離":"Quarantine ", "時":"",
|
||||
"建立":"Create ","環境":"Environment ","沙箱":"Sandbox ","化":"", "表":"",
|
||||
"達":"Reached ", "上限":"Limit ", "區":"", "金鑰":"Key ", "產品":"Product ",
|
||||
"托管":"CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
|
||||
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ", " ":" "},
|
||||
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
|
||||
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ",
|
||||
"版本":"Version ","是":"","剩餘":"Remain ","遠控":"Remote ","監控":"Monitor ",
|
||||
" ":" "},
|
||||
|
||||
"white_switch": {
|
||||
"_white": "_black",},
|
||||
|
||||
+81
-21
@@ -1,6 +1,72 @@
|
||||
import os, yara, numpy, locale, pefile, onnxruntime
|
||||
import os, yara, numpy, onnxruntime
|
||||
import ctypes, ctypes.wintypes, pefile
|
||||
from PIL import Image
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class GUID(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Data1", ctypes.wintypes.DWORD),
|
||||
("Data2", ctypes.wintypes.WORD),
|
||||
("Data3", ctypes.wintypes.WORD),
|
||||
("Data4", ctypes.c_ubyte * 8)]
|
||||
|
||||
class WINTRUST_FILE_INFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
|
||||
("hFile", ctypes.wintypes.HANDLE),
|
||||
("pgKnownSubject", ctypes.wintypes.LPVOID)]
|
||||
|
||||
class WINTRUST_DATA(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
|
||||
("pSIPClientData", ctypes.wintypes.LPVOID),
|
||||
("dwUIChoice", ctypes.wintypes.DWORD),
|
||||
("fdwRevocationChecks", ctypes.wintypes.DWORD),
|
||||
("dwUnionChoice", ctypes.wintypes.DWORD),
|
||||
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
|
||||
("dwStateAction", ctypes.wintypes.DWORD),
|
||||
("hWVTStateData", ctypes.wintypes.HANDLE),
|
||||
("pwszURLReference", ctypes.wintypes.LPCWSTR),
|
||||
("dwProvFlags", ctypes.wintypes.DWORD),
|
||||
("dwUIContext", ctypes.wintypes.DWORD),
|
||||
("pSignatureSettings", ctypes.wintypes.LPVOID)]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class sign_scanner:
|
||||
def __init__(self):
|
||||
self.verify = GUID(0x00AAC56B, 0xCD44, 0x11D0, (0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
|
||||
|
||||
def init_windll(self, path):
|
||||
for name in path:
|
||||
try:
|
||||
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
self.WinVerifyTrust = self.wintrust.WinVerifyTrust
|
||||
self.WinVerifyTrust.restype = ctypes.wintypes.LONG
|
||||
self.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.c_void_p]
|
||||
|
||||
def is_sign(self, file_path):
|
||||
with pefile.PE(file, fast_load=True) as pe:
|
||||
sec_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']]
|
||||
return sec_dir.VirtualAddress != 0 and sec_dir.Size > 0
|
||||
|
||||
def sign_verify(self, file_path):
|
||||
fi = WINTRUST_FILE_INFO(ctypes.sizeof(WINTRUST_FILE_INFO), file_path, None, None)
|
||||
data = WINTRUST_DATA(ctypes.sizeof(WINTRUST_DATA), None, None, 2, 0, 1,
|
||||
ctypes.pointer(fi), 1, None, None, 0, 0, None)
|
||||
s = self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
data.dwStateAction = 2
|
||||
self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
return s == 0
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class rule_scanner:
|
||||
def __init__(self):
|
||||
self.rules = {}
|
||||
@@ -26,9 +92,8 @@ class rule_scanner:
|
||||
|
||||
def yara_scan(self, file_path):
|
||||
try:
|
||||
data = open(file_path, "rb").read() if isinstance(file_path, str) else file_path
|
||||
for rules in self.rules.values():
|
||||
matches = rules.match(data=data)
|
||||
matches = rules.match(filepath=file_path)
|
||||
if matches:
|
||||
rule_name = str(matches[0])
|
||||
label = rule_name.split("_")[0]
|
||||
@@ -38,19 +103,18 @@ class rule_scanner:
|
||||
except Exception:
|
||||
return False, False
|
||||
|
||||
class model_scanner:
|
||||
SHELLS = {
|
||||
"0","1","2","3","4","5","6","7","8","9","cry","tvm","dec","enc","vmp","upx","aes","lzma","press","pack",
|
||||
"enigma","protect","secur","asmstub","base","bss","clr_uef","cursors","transit","trs_age","engine","fio",
|
||||
"fothk","h~;","icapsec","malloc_h","miniex","mssmixer","ndr64","nsys_wr","obr","wow","wow64svc","wpp_sf",
|
||||
"pad","pgae","poolmi","proxy","qihoo","retpol","uedbg","tracesup","rwexec","rygs","s:@","sanontcp","segm",
|
||||
"test","res","wisevec","viahwaes","orpc","nep","ace","extjmp","no_bbt","data","page","hexpthk"}
|
||||
####################################################################################################
|
||||
|
||||
class model_scanner:
|
||||
def __init__(self):
|
||||
self.models = {}
|
||||
self.suffix = {".com", ".exe", ".dll", ".sys", ".scr", ".bat", ".cmd", ".ps1", ".vbs", ".wsf"}
|
||||
self.labels = ["Pefile/White", "Script/White", "Pefile/General", "Script/General"]
|
||||
self.detect = {"Pefile/General", "Script/General"}
|
||||
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 = {
|
||||
"Pefile/General", "Script/General"}
|
||||
self.resize = (224, 224)
|
||||
|
||||
def load_path(self, path):
|
||||
@@ -65,14 +129,8 @@ class model_scanner:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def is_valid_suffix(self, file_path):
|
||||
return os.path.splitext(file_path)[-1].lower() in self.suffix
|
||||
|
||||
def model_scan(self, file_path):
|
||||
try:
|
||||
if not self.models or not self.is_valid_suffix(file_path):
|
||||
return False, False
|
||||
|
||||
model = next(iter(self.models.values()))
|
||||
shape = model.get_inputs()[0].shape
|
||||
channels = shape[-1] if shape[-1] in (1, 3) else (shape[1] if shape[1] in (1, 3) else 1)
|
||||
@@ -115,6 +173,7 @@ class model_scanner:
|
||||
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:
|
||||
@@ -131,7 +190,7 @@ class model_scanner:
|
||||
|
||||
def get_type(self, file_path):
|
||||
match_data = {}
|
||||
if not self.is_valid_suffix(file_path):
|
||||
if not os.path.splitext(file_path)[-1].lower() in self.suffix:
|
||||
return match_data
|
||||
try:
|
||||
with pefile.PE(file_path, fast_load=True) as pe:
|
||||
@@ -140,8 +199,9 @@ class model_scanner:
|
||||
return {}
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
if (section.Characteristics & 0x00000020) and name not in self.SHELLS:
|
||||
if section.Characteristics & 0x00000020:
|
||||
match_data[name] = section.get_data()
|
||||
|
||||
except pefile.PEFormatError:
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
|
||||
+51
-50
@@ -665,9 +665,7 @@ class Ui_main_window(object):
|
||||
self.license_text = QTextEdit(self.license_widget)
|
||||
self.license_text.setObjectName(u"license_text")
|
||||
self.license_text.setGeometry(QRect(10, 10, 730, 220))
|
||||
font7 = QFont()
|
||||
font7.setPointSize(9)
|
||||
self.license_text.setFont(font7)
|
||||
self.license_text.setFont(font2)
|
||||
self.license_text.setLayoutDirection(Qt.LeftToRight)
|
||||
self.license_text.setStyleSheet(u"QTextEdit { background-color: transparent; color: rgb(0, 0, 0); border: 1px solid rgb(235, 235, 235); border-radius: 10px; outline: none; } QTextEdit QScrollBar:vertical { background: transparent; width: 6px; margin: 0px; } QTextEdit QScrollBar::handle:vertical { background: rgb(200, 200, 200); border-radius: 3px; min-height: 20px; } QTextEdit QScrollBar:horizontal { background: transparent; height: 6px; margin: 0px; } QTextEdit QScrollBar::handle:horizontal { background: rgb(200, 200, 200); border-radius: 3px; min-width: 20px; } QTextEdit QScrollBar::handle:hover { background: rgb(180, 180, 180); } QTextEdit QScrollBar::add-line, QTextEdit QScrollBar::sub-line { width: 0px; height: 0px; } QTextEdit QScrollBar::add-page, QTextEdit QScrollBar::sub-page { background: transparent; }\n"
|
||||
"")
|
||||
@@ -1196,15 +1194,15 @@ class Ui_main_window(object):
|
||||
self.log_text = QTextEdit(self.log_widget)
|
||||
self.log_text.setObjectName(u"log_text")
|
||||
self.log_text.setGeometry(QRect(10, 10, 730, 220))
|
||||
font8 = QFont()
|
||||
font8.setFamilies([u"Microsoft YaHei"])
|
||||
font8.setPointSize(9)
|
||||
font8.setBold(False)
|
||||
font8.setItalic(False)
|
||||
font8.setStrikeOut(False)
|
||||
font8.setKerning(True)
|
||||
font8.setStyleStrategy(QFont.PreferAntialias)
|
||||
self.log_text.setFont(font8)
|
||||
font7 = QFont()
|
||||
font7.setFamilies([u"Microsoft YaHei"])
|
||||
font7.setPointSize(9)
|
||||
font7.setBold(False)
|
||||
font7.setItalic(False)
|
||||
font7.setStrikeOut(False)
|
||||
font7.setKerning(True)
|
||||
font7.setStyleStrategy(QFont.PreferAntialias)
|
||||
self.log_text.setFont(font7)
|
||||
self.log_text.setLayoutDirection(Qt.LeftToRight)
|
||||
self.log_text.setAutoFillBackground(False)
|
||||
self.log_text.setStyleSheet(u"QTextEdit { background-color: transparent; color: rgb(0, 0, 0); border: 1px solid rgb(235, 235, 235); border-radius: 10px; outline: none; } QTextEdit QScrollBar:vertical { background: transparent; width: 6px; margin: 0px; } QTextEdit QScrollBar::handle:vertical { background: rgb(200, 200, 200); border-radius: 3px; min-height: 20px; } QTextEdit QScrollBar:horizontal { background: transparent; height: 6px; margin: 0px; } QTextEdit QScrollBar::handle:horizontal { background: rgb(200, 200, 200); border-radius: 3px; min-width: 20px; } QTextEdit QScrollBar::handle:hover { background: rgb(180, 180, 180); } QTextEdit QScrollBar::add-line, QTextEdit QScrollBar::sub-line { width: 0px; height: 0px; } QTextEdit QScrollBar::add-page, QTextEdit QScrollBar::sub-page { background: transparent; }\n"
|
||||
@@ -1225,12 +1223,12 @@ class Ui_main_window(object):
|
||||
self.state_title = QLabel(self.state_widget)
|
||||
self.state_title.setObjectName(u"state_title")
|
||||
self.state_title.setGeometry(QRect(10, 200, 730, 30))
|
||||
font9 = QFont()
|
||||
font9.setFamilies([u"Microsoft YaHei"])
|
||||
font9.setPointSize(14)
|
||||
font9.setBold(True)
|
||||
font9.setStyleStrategy(QFont.PreferAntialias)
|
||||
self.state_title.setFont(font9)
|
||||
font8 = QFont()
|
||||
font8.setFamilies([u"Microsoft YaHei"])
|
||||
font8.setPointSize(14)
|
||||
font8.setBold(True)
|
||||
font8.setStyleStrategy(QFont.PreferAntialias)
|
||||
self.state_title.setFont(font8)
|
||||
self.state_title.setLayoutDirection(Qt.LeftToRight)
|
||||
self.state_title.setStyleSheet(u"QLabel { background-color: transparent; color: rgb(0, 0, 0); }\n"
|
||||
"")
|
||||
@@ -1242,13 +1240,13 @@ class Ui_main_window(object):
|
||||
self.state_icon.setSizePolicy(sizePolicy)
|
||||
self.state_icon.setMinimumSize(QSize(0, 171))
|
||||
self.state_icon.setMaximumSize(QSize(16777215, 16777215))
|
||||
font10 = QFont()
|
||||
font10.setFamilies([u"Microsoft YaHei"])
|
||||
font10.setPointSize(10)
|
||||
font10.setStrikeOut(False)
|
||||
font10.setKerning(True)
|
||||
font10.setStyleStrategy(QFont.PreferAntialias)
|
||||
self.state_icon.setFont(font10)
|
||||
font9 = QFont()
|
||||
font9.setFamilies([u"Microsoft YaHei"])
|
||||
font9.setPointSize(10)
|
||||
font9.setStrikeOut(False)
|
||||
font9.setKerning(True)
|
||||
font9.setStyleStrategy(QFont.PreferAntialias)
|
||||
self.state_icon.setFont(font9)
|
||||
self.state_icon.setContextMenuPolicy(Qt.DefaultContextMenu)
|
||||
self.state_icon.setLayoutDirection(Qt.LeftToRight)
|
||||
self.state_icon.setStyleSheet(u"")
|
||||
@@ -1478,37 +1476,40 @@ class Ui_main_window(object):
|
||||
self.license_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:9pt; font-weight:400; font-style:normal;\">\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-07</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=\"-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; m"
|
||||
"argin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 A single device key is required for activation.</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, 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 retains all rights; IP not transferred.</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; 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"
|
||||
"<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 No redistribution, sublicensing, rental "
|
||||
"or leasing. </p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 No reverse engineering, modification or removal of notices. </p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 The software may not be used for any unlawful purpose.</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. Open Source Component </p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 The main Python script is licensed under MIT License.</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 All drivers Protect*.c/h script remain proprietary.</p>\n"
|
||||
":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=\"-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 may include patches, enhancements, or new versions. </p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 Continued use of the software implies acceptance of the terms.</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=\"-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 The software is provided \u201cas is,\u201d without warranty. </p>\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"> \u2013 No liability for indirect or 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-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\u20132025 87owo (PYAS Security). All rights reserved. </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 & 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))
|
||||
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.2',
|
||||
version='3.3.5',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
|
||||
LARGE_INTEGER g_CmRegHandle;
|
||||
PDEVICE_OBJECT g_ControlDeviceObject;
|
||||
KSPIN_LOCK g_ProtectLock;
|
||||
FAST_MUTEX HookMutex;
|
||||
PDRIVER_OBJECT DiskDrvObj = NULL;
|
||||
KEVENT g_LogDrainEvent;
|
||||
BOOLEAN g_Unloading = FALSE;
|
||||
BOOLEAN g_CmRegActive = FALSE;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
PDEVICE_OBJECT g_GuardDevice = NULL;
|
||||
|
||||
PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS] = { 0 };
|
||||
ULONG g_MbrFilterCount = 0;
|
||||
|
||||
volatile LONG g_LogWorkCount = 0;
|
||||
static UNICODE_STRING g_DeviceName;
|
||||
static WCHAR g_DeviceNameBuf[64];
|
||||
static UNICODE_STRING g_SymLink = RTL_CONSTANT_STRING(L"\\??\\PYAS_Driver");
|
||||
static BOOLEAN g_SymLinkCreated = FALSE;
|
||||
|
||||
static NTSTATUS CombinedDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
if (DeviceObject == g_ControlDeviceObject) {
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (s->MajorFunction == IRP_MJ_CREATE || s->MajorFunction == IRP_MJ_CLOSE || s->MajorFunction == IRP_MJ_CLEANUP) {
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
Irp->IoStatus.Information = 0;
|
||||
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;
|
||||
}
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
}
|
||||
return FileProtectDispatch(DeviceObject, Irp);
|
||||
}
|
||||
|
||||
static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\Driver\\Disk");
|
||||
UNICODE_STRING sym = RTL_CONSTANT_STRING(L"IoDriverObjectType");
|
||||
POBJECT_TYPE* pType = (POBJECT_TYPE*)MmGetSystemRoutineAddress(&sym);
|
||||
|
||||
if (!pType || !*pType)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
NTSTATUS status = ObReferenceObjectByName(&name, OBJ_CASE_INSENSITIVE, NULL, 0, *pType, KernelMode, NULL, (PVOID*)&DiskDrvObj);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
ULONG done = 0;
|
||||
status = IoEnumerateDeviceObjectList(DiskDrvObj, NULL, 0, &done);
|
||||
if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_SUCCESS) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
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);
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
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);
|
||||
|
||||
KeInitializeSpinLock(&g_ProtectLock);
|
||||
ExInitializeFastMutex(&HookMutex);
|
||||
KeInitializeEvent(&g_LogDrainEvent, NotificationEvent, TRUE);
|
||||
g_Unloading = FALSE;
|
||||
g_CmRegActive = FALSE;
|
||||
|
||||
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;
|
||||
g_DeviceName.MaximumLength = sizeof(g_DeviceNameBuf);
|
||||
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;
|
||||
|
||||
DriverObject->DriverUnload = DriverUnload;
|
||||
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
|
||||
DriverObject->MajorFunction[i] = CombinedDispatch;
|
||||
|
||||
DriverObject->Flags |= 0x20;
|
||||
|
||||
g_ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
{
|
||||
UINT i = 0;
|
||||
for (; i < 50; ++i) {
|
||||
status = IoCreateSymbolicLink(&g_SymLink, &g_DeviceName);
|
||||
if (NT_SUCCESS(status))
|
||||
break;
|
||||
|
||||
LARGE_INTEGER d = { 0 };
|
||||
d.QuadPart = -10LL * 1000LL * 10LL;
|
||||
KeDelayExecutionThread(KernelMode, FALSE, &d);
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
}
|
||||
if (!NT_SUCCESS(status)) {
|
||||
IoDeleteDevice(g_ControlDeviceObject);
|
||||
g_ControlDeviceObject = NULL;
|
||||
if (g_GuardDevice) { IoDeleteDevice(g_GuardDevice); g_GuardDevice = NULL; }
|
||||
return status;
|
||||
}
|
||||
}
|
||||
g_SymLinkCreated = TRUE;
|
||||
{
|
||||
static WCHAR regAltBuf[32];
|
||||
static UNICODE_STRING regAlt;
|
||||
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
|
||||
RtlStringCchPrintfW(regAltBuf, RTL_NUMBER_OF(regAltBuf), L"385100.%I64x", t.QuadPart);
|
||||
RtlInitUnicodeString(®Alt, regAltBuf);
|
||||
|
||||
status = CmRegisterCallbackEx(RegistryProtectCallback, ®Alt, DriverObject, NULL, &g_CmRegHandle, NULL);
|
||||
if (NT_SUCCESS(status))
|
||||
g_CmRegActive = TRUE;
|
||||
}
|
||||
InitImageProtect();
|
||||
InitInjectProtect();
|
||||
InitRemoteProtect();
|
||||
InitScreenProtect();
|
||||
{
|
||||
NTSTATUS ps = InitProcessProtect();
|
||||
if (!NT_SUCCESS(ps)) {
|
||||
CHAR b[64] = { 0 };
|
||||
RtlStringCchPrintfA(b, RTL_NUMBER_OF(b), "PROC_INIT | 0x%08X", ps);
|
||||
SendPipeLog(b, strlen(b));
|
||||
}
|
||||
else {
|
||||
static CHAR ok[] = "PROC_INIT | 0x00000000";
|
||||
SendPipeLog(ok, sizeof(ok) - 1);
|
||||
}
|
||||
}
|
||||
AttachFsFilters(DriverObject);
|
||||
AttachDiskFilters(DriverObject);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
UninitInjectProtect();
|
||||
UninitImageProtect();
|
||||
UninitRemoteProtect();
|
||||
UninitScreenProtect();
|
||||
UninitProcessProtect();
|
||||
|
||||
ExWaitForRundownProtectionRelease(&g_Rundown);
|
||||
if (InterlockedCompareExchange((volatile LONG*)&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
if (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeResetEvent(&g_LogDrainEvent);
|
||||
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);
|
||||
IoDeleteDevice(g_MbrFilterTargets[i]);
|
||||
g_MbrFilterTargets[i] = NULL;
|
||||
}
|
||||
}
|
||||
g_MbrFilterCount = 0;
|
||||
if (DiskDrvObj) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
}
|
||||
if (g_ControlDeviceObject) {
|
||||
IoDeleteDevice(g_ControlDeviceObject);
|
||||
g_ControlDeviceObject = NULL;
|
||||
}
|
||||
if (g_GuardDevice) {
|
||||
IoDeleteDevice(g_GuardDevice);
|
||||
g_GuardDevice = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
|
||||
#define PIPE_NAME L"\\??\\pipe\\PYAS_Output_Pipe"
|
||||
#define MAX_MBR_TARGETS 256
|
||||
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x00000001
|
||||
#endif
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE 0x0001
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD 0x0002
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION 0x0008
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE 0x0020
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ 0x0010
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE 0x0040
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS 0x0080
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA 0x0100
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION 0x0200
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME 0x0800
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
|
||||
#endif
|
||||
#ifndef PROCESS_SET_LIMITED_INFORMATION
|
||||
#define PROCESS_SET_LIMITED_INFORMATION 0x2000
|
||||
#endif
|
||||
#ifndef THREAD_TERMINATE
|
||||
#define THREAD_TERMINATE 0x0001
|
||||
#endif
|
||||
#ifndef THREAD_SUSPEND_RESUME
|
||||
#define THREAD_SUSPEND_RESUME 0x0002
|
||||
#endif
|
||||
#ifndef THREAD_SET_CONTEXT
|
||||
#define THREAD_SET_CONTEXT 0x0010
|
||||
#endif
|
||||
#ifndef THREAD_IMPERSONATE
|
||||
#define THREAD_IMPERSONATE 0x0100
|
||||
#endif
|
||||
#ifndef THREAD_DIRECT_IMPERSONATION
|
||||
#define THREAD_DIRECT_IMPERSONATION 0x0200
|
||||
#endif
|
||||
#ifndef JOB_OBJECT_ASSIGN_PROCESS
|
||||
#define JOB_OBJECT_ASSIGN_PROCESS 0x0001
|
||||
#endif
|
||||
#ifndef JOB_OBJECT_TERMINATE
|
||||
#define JOB_OBJECT_TERMINATE 0x0008
|
||||
#endif
|
||||
#ifndef FILE_DISPOSITION_DELETE
|
||||
#define FILE_DISPOSITION_DELETE 0x00000001
|
||||
#endif
|
||||
#ifndef UINT
|
||||
typedef unsigned int UINT;
|
||||
#endif
|
||||
|
||||
extern POBJECT_TYPE* PsJobType;
|
||||
extern volatile LONG g_LogWorkCount;
|
||||
extern BOOLEAN g_Unloading;
|
||||
extern KEVENT g_LogDrainEvent;
|
||||
extern PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS];
|
||||
extern ULONG g_MbrFilterCount;
|
||||
extern KSPIN_LOCK g_ProtectLock;
|
||||
extern LARGE_INTEGER g_CmRegHandle;
|
||||
extern POBJECT_TYPE* IoDriverObjectType;
|
||||
extern POBJECT_TYPE* IoFileObjectType;
|
||||
extern FAST_MUTEX HookMutex;
|
||||
extern PDRIVER_OBJECT DiskDrvObj;
|
||||
extern PCSTR PsGetProcessImageFileName(PEPROCESS Process);
|
||||
extern EX_RUNDOWN_REF g_Rundown;
|
||||
extern PDEVICE_OBJECT g_GuardDevice;
|
||||
|
||||
typedef PVOID(NTAPI* _pyas_ExAllocatePool2)(ULONG, SIZE_T, ULONG);
|
||||
typedef VOID(NTAPI* _pyas_ExFreePool2)(PVOID, ULONG, PVOID, ULONG);
|
||||
typedef PVOID(NTAPI* _pyas_ExAllocatePoolWithTag)(POOL_TYPE, SIZE_T, ULONG);
|
||||
typedef VOID(NTAPI* _pyas_ExFreePoolWithTag)(PVOID, ULONG);
|
||||
|
||||
static __forceinline PVOID PYAS_ExAllocatePool2(ULONG f, SIZE_T s, ULONG t) {
|
||||
static _pyas_ExAllocatePool2 p2 = 0;
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePool2");
|
||||
p2 = (_pyas_ExAllocatePool2)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p2)
|
||||
return p2(f, s, t);
|
||||
static _pyas_ExAllocatePoolWithTag p1 = 0;
|
||||
if (!p1) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePoolWithTag");
|
||||
p1 = (_pyas_ExAllocatePoolWithTag)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p1)
|
||||
return p1((f & POOL_FLAG_NON_PAGED) ? NonPagedPoolNx : PagedPool, s, t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static __forceinline VOID PYAS_ExFreePool2(PVOID a, ULONG t, PVOID h, ULONG f) {
|
||||
UNREFERENCED_PARAMETER(h);
|
||||
UNREFERENCED_PARAMETER(f);
|
||||
|
||||
static _pyas_ExFreePool2 p2 = 0;
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePool2");
|
||||
p2 = (_pyas_ExFreePool2)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p2) {
|
||||
p2(a, t, h, f);
|
||||
return;
|
||||
}
|
||||
static _pyas_ExFreePoolWithTag p1 = 0;
|
||||
if (!p1) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePoolWithTag");
|
||||
p1 = (_pyas_ExFreePoolWithTag)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p1) {
|
||||
p1(a, t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define ExAllocatePool2 PYAS_ExAllocatePool2
|
||||
#define ExFreePool2 PYAS_ExFreePool2
|
||||
|
||||
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
|
||||
VOID SendPipeLog(PCSTR msg, SIZE_T len);
|
||||
VOID UninitImageProtect(VOID);
|
||||
VOID UninitInjectProtect(VOID);
|
||||
VOID UninitRemoteProtect(VOID);
|
||||
VOID UninitScreenProtect(VOID);
|
||||
VOID UninitProcessProtect(VOID);
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(
|
||||
_In_ HANDLE ProcessHandle,
|
||||
_In_ PROCESSINFOCLASS ProcessInformationClass,
|
||||
_Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
|
||||
_In_ ULONG ProcessInformationLength,
|
||||
_Out_opt_ PULONG ReturnLength
|
||||
);
|
||||
|
||||
NTSTATUS ObReferenceObjectByName(
|
||||
IN PUNICODE_STRING ObjectName,
|
||||
IN ULONG Attributes,
|
||||
IN PACCESS_STATE PassedAccessState OPTIONAL,
|
||||
IN ACCESS_MASK DesiredAccess OPTIONAL,
|
||||
IN POBJECT_TYPE ObjectType,
|
||||
IN KPROCESSOR_MODE AccessMode,
|
||||
IN OUT PVOID ParseContext OPTIONAL,
|
||||
OUT PVOID* Object
|
||||
);
|
||||
|
||||
NTSTATUS FileProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
|
||||
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2);
|
||||
NTSTATUS InitImageProtect(VOID);
|
||||
NTSTATUS InitInjectProtect(VOID);
|
||||
NTSTATUS InitRemoteProtect(VOID);
|
||||
NTSTATUS InitScreenProtect(VOID);
|
||||
NTSTATUS InitProcessProtect(VOID);
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
extern PDEVICE_OBJECT g_ControlDeviceObject;
|
||||
|
||||
typedef struct _PIPE_LOG_CTX {
|
||||
PIO_WORKITEM Item;
|
||||
SIZE_T Len;
|
||||
PCHAR Buf;
|
||||
}PIPE_LOG_CTX, * PPIPE_LOG_CTX;
|
||||
|
||||
static VOID PipeLogWork(PDEVICE_OBJECT DeviceObject, PVOID Context)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)Context;
|
||||
HANDLE h = NULL;
|
||||
UNICODE_STRING name;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
RtlInitUnicodeString(&name, PIPE_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
if (NT_SUCCESS(ZwCreateFile(&h, FILE_GENERIC_WRITE, &oa, &iosb, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) {
|
||||
ZwWriteFile(h, NULL, NULL, NULL, &iosb, ctx->Buf, (ULONG)(ctx->Len > 0xFFFFFFFF ? 0xFFFFFFFF : ctx->Len), NULL, NULL);
|
||||
ZwClose(h);
|
||||
}
|
||||
IoFreeWorkItem(ctx->Item);
|
||||
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
|
||||
if (InterlockedDecrement(&g_LogWorkCount) == 0) {
|
||||
KeSetEvent(&g_LogDrainEvent, IO_NO_INCREMENT, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
VOID SendPipeLog(PCSTR msg, SIZE_T len)
|
||||
{
|
||||
if (!msg || len == 0)
|
||||
return;
|
||||
if (!g_ControlDeviceObject)
|
||||
return;
|
||||
if (g_Unloading)
|
||||
return;
|
||||
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PIPE_LOG_CTX), 'golP');
|
||||
if (!ctx)
|
||||
return;
|
||||
ctx->Len = len;
|
||||
ctx->Buf = (PCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, ctx->Len, 'golP');
|
||||
if (!ctx->Buf) {
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
return;
|
||||
}
|
||||
RtlCopyMemory(ctx->Buf, msg, ctx->Len);
|
||||
ctx->Item = IoAllocateWorkItem(g_ControlDeviceObject);
|
||||
if (!ctx->Item) {
|
||||
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
return;
|
||||
}
|
||||
InterlockedIncrement(&g_LogWorkCount);
|
||||
IoQueueWorkItem(ctx->Item, PipeLogWork, DelayedWorkQueue, ctx);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
[Version]
|
||||
Signature = "$WINDOWS NT$"
|
||||
Class = System
|
||||
ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318}
|
||||
Provider = %ManufacturerName%
|
||||
CatalogFile = PYAS_Driver.cat
|
||||
PnpLockdown = 1
|
||||
DriverVer =
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir = 13
|
||||
|
||||
[DefaultInstall.ntamd64]
|
||||
CopyFiles = @PYAS_Driver.sys
|
||||
AddService = PYAS_Driver,0x00000002
|
||||
|
||||
[SourceDisksFiles]
|
||||
PYAS_Driver.sys = 1
|
||||
|
||||
[SourceDisksNames]
|
||||
1 = %DiskName%,,,""
|
||||
|
||||
[Strings]
|
||||
ManufacturerName = "PYAS_Driver"
|
||||
DiskName = "PYAS_Driver"
|
||||
ServiceName="PYAS_Driver"
|
||||
ServiceDisplayName="PYAS_Driver"
|
||||
Binary file not shown.
@@ -0,0 +1,323 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN GetFilePathFromFileObject(PFILE_OBJECT fo, PUNICODE_STRING out)
|
||||
{
|
||||
if (!fo || !out || !out->Buffer || out->MaximumLength == 0)
|
||||
return FALSE;
|
||||
|
||||
out->Length = 0;
|
||||
if (fo->DeviceObject && fo->FileName.Buffer && fo->FileName.Length) {
|
||||
ULONG need = 0;
|
||||
NTSTATUS s = ObQueryNameString(fo->DeviceObject, NULL, 0, &need);
|
||||
if (s == STATUS_INFO_LENGTH_MISMATCH && need > 0) {
|
||||
POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'nOmD');
|
||||
if (oni) {
|
||||
if (NT_SUCCESS(ObQueryNameString(fo->DeviceObject, oni, need, &need)) && oni->Name.Buffer && oni->Name.Length) {
|
||||
ULONG total = oni->Name.Length + fo->FileName.Length;
|
||||
if (total + sizeof(WCHAR) <= out->MaximumLength) {
|
||||
RtlCopyMemory(out->Buffer, oni->Name.Buffer, oni->Name.Length);
|
||||
RtlCopyMemory((PUCHAR)out->Buffer + oni->Name.Length, fo->FileName.Buffer, fo->FileName.Length);
|
||||
out->Length = (USHORT)total;
|
||||
out->Buffer[total / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
HANDLE hFile = NULL;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
if (!NT_SUCCESS(ObOpenObjectByPointer(fo, OBJ_KERNEL_HANDLE, NULL, FILE_READ_ATTRIBUTES, *IoFileObjectType, KernelMode, &hFile)))
|
||||
return FALSE;
|
||||
|
||||
ULONG buflen = sizeof(FILE_NAME_INFORMATION) + 2048 * sizeof(WCHAR);
|
||||
PFILE_NAME_INFORMATION pfi = (PFILE_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, buflen, 'nIIF');
|
||||
if (!pfi) {
|
||||
ZwClose(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
RtlZeroMemory(pfi, buflen);
|
||||
NTSTATUS st = ZwQueryInformationFile(hFile, &iosb, pfi, buflen, FileNormalizedNameInformation);
|
||||
|
||||
if (NT_SUCCESS(st) && pfi->FileNameLength > 0 && pfi->FileNameLength + sizeof(WCHAR) <= out->MaximumLength) {
|
||||
RtlCopyMemory(out->Buffer, pfi->FileName, pfi->FileNameLength);
|
||||
out->Length = (USHORT)pfi->FileNameLength;
|
||||
out->Buffer[out->Length / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(pfi, 'nIIF', NULL, 0);
|
||||
ZwClose(hFile);
|
||||
return TRUE;
|
||||
}
|
||||
ExFreePool2(pfi, 'nIIF', NULL, 0);
|
||||
ZwClose(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN WantDelete(PIRP Irp, PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (s->MajorFunction != IRP_MJ_SET_INFORMATION)
|
||||
return FALSE;
|
||||
|
||||
FILE_INFORMATION_CLASS c = s->Parameters.SetFile.FileInformationClass;
|
||||
if (c == FileDispositionInformation) {
|
||||
PFILE_DISPOSITION_INFORMATION di = (PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||||
return di && di->DeleteFile;
|
||||
}
|
||||
if (c == FileDispositionInformationEx) {
|
||||
PFILE_DISPOSITION_INFORMATION_EX dx = (PFILE_DISPOSITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
|
||||
return dx && (dx->Flags & FILE_DISPOSITION_DELETE);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN WantRename(PIRP Irp, PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (s->MajorFunction != IRP_MJ_SET_INFORMATION)
|
||||
return FALSE;
|
||||
|
||||
FILE_INFORMATION_CLASS c = s->Parameters.SetFile.FileInformationClass;
|
||||
if (c == FileRenameInformation || c == FileRenameInformationEx)
|
||||
return Irp->AssociatedIrp.SystemBuffer != NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || exe->MaximumLength == 0)
|
||||
return;
|
||||
|
||||
exe->Length = 0;
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, exe))
|
||||
return;
|
||||
}
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
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));
|
||||
}
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static BOOLEAN QueryDevName(PDEVICE_OBJECT dev, PUNICODE_STRING out)
|
||||
{
|
||||
if (!dev || !out || !out->Buffer || out->MaximumLength == 0)
|
||||
return FALSE;
|
||||
|
||||
ULONG need = 0;
|
||||
NTSTATUS s = ObQueryNameString(dev, NULL, 0, &need);
|
||||
if (s != STATUS_INFO_LENGTH_MISMATCH || need == 0)
|
||||
return FALSE;
|
||||
|
||||
POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'nOmD');
|
||||
if (!oni)
|
||||
return FALSE;
|
||||
|
||||
BOOLEAN ok = FALSE;
|
||||
s = ObQueryNameString(dev, oni, need, &need);
|
||||
if (NT_SUCCESS(s) && oni->Name.Buffer && oni->Name.Length && oni->Name.Length < out->MaximumLength) {
|
||||
RtlCopyMemory(out->Buffer, oni->Name.Buffer, oni->Name.Length);
|
||||
out->Length = (USHORT)oni->Name.Length;
|
||||
out->Buffer[out->Length / sizeof(WCHAR)] = 0;
|
||||
ok = TRUE;
|
||||
}
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static BOOLEAN IsDASDRoot(PFILE_OBJECT fo)
|
||||
{
|
||||
if (!fo)
|
||||
return TRUE;
|
||||
if (fo->FileName.Length == 0 && fo->RelatedFileObject == NULL)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsDangerousDiskIo(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)
|
||||
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;
|
||||
}
|
||||
|
||||
NTSTATUS FileProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
|
||||
if (!lower) {
|
||||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
Irp->IoStatus.Information = 0;
|
||||
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);
|
||||
NTSTATUS ps = PoCallDriver(lower, 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;
|
||||
BOOLEAN isWrite = (s->MajorFunction == IRP_MJ_WRITE);
|
||||
BOOLEAN isDeleteReq = WantDelete(Irp, s);
|
||||
BOOLEAN isRenameReq = WantRename(Irp, s);
|
||||
BOOLEAN canQuery = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = eb;
|
||||
exe.MaximumLength = sizeof(eb);
|
||||
|
||||
if (canQuery) {
|
||||
GetExeNameForLog(pid, &exe);
|
||||
if (IsWhitelist(&exe)) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS ns2 = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
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);
|
||||
|
||||
if (canQuery && QueryDevName(lower, &dev)) {
|
||||
if (MatchBlockFile(&dev))
|
||||
hit = TRUE;
|
||||
}
|
||||
else {
|
||||
hit = TRUE;
|
||||
}
|
||||
if (!hit && s->FileObject && canQuery) {
|
||||
WCHAR nb[512] = { 0 };
|
||||
UNICODE_STRING n2 = { 0 };
|
||||
n2.Buffer = nb;
|
||||
n2.MaximumLength = sizeof(nb);
|
||||
|
||||
if (QueryDevName(s->FileObject->DeviceObject, &n2)) {
|
||||
if (MatchBlockFile(&n2))
|
||||
hit = TRUE;
|
||||
}
|
||||
}
|
||||
if (!hit && s->MajorFunction == IRP_MJ_WRITE && s->FileObject && IsDASDRoot(s->FileObject))
|
||||
hit = TRUE;
|
||||
if (hit) {
|
||||
blocked = TRUE;
|
||||
if (canQuery) {
|
||||
if (dev.Length == 0)
|
||||
QueryDevName(lower, &dev);
|
||||
LogAnsi3("BOOT_BLOCK", upid, &exe, dev.Length ? &dev : &exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!blocked && (isWrite || isDeleteReq || isRenameReq) && s->FileObject && canQuery) {
|
||||
WCHAR pb[1024] = { 0 };
|
||||
UNICODE_STRING path = { 0 };
|
||||
path.Buffer = pb;
|
||||
path.MaximumLength = sizeof(pb);
|
||||
|
||||
if (GetFilePathFromFileObject(s->FileObject, &path)) {
|
||||
BOOLEAN hitFile = MatchBlockFile(&path);
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&path) && HasBlockedSuffix(&path);
|
||||
if (hitFile || hitRansom) {
|
||||
blocked = TRUE;
|
||||
LogAnsi3(hitFile && !hitRansom ? "FILE_BLOCK" : "RANSOM_BLOCK", upid, &exe, &path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ULONG need = 0;
|
||||
POBJECT_NAME_INFORMATION dev = NULL;
|
||||
NTSTATUS qs2 = ObQueryNameString(s->FileObject->DeviceObject, NULL, 0, &need);
|
||||
|
||||
if (qs2 == STATUS_INFO_LENGTH_MISMATCH && need > 0) {
|
||||
dev = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'dOnF');
|
||||
if (dev && NT_SUCCESS(ObQueryNameString(s->FileObject->DeviceObject, dev, need, &need)) && dev->Name.Buffer && dev->Name.Length) {
|
||||
if (MatchBlockFile(&dev->Name)) {
|
||||
blocked = TRUE;
|
||||
UNICODE_STRING rule = { 0 };
|
||||
rule.Buffer = GetMatchedBlockFileRule(&dev->Name);
|
||||
rule.Length = rule.Buffer ? (USHORT)(wcslen(rule.Buffer) * sizeof(WCHAR)) : 0;
|
||||
rule.MaximumLength = rule.Length;
|
||||
LogAnsi3("FILE_BLOCK", upid, &exe, rule.Buffer ? &rule : &dev->Name);
|
||||
}
|
||||
}
|
||||
if (dev)
|
||||
ExFreePool2(dev, 'dOnF', NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blocked) {
|
||||
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS rs2 = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return rs2;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_ImageNotifyEnabled = FALSE;
|
||||
|
||||
static BOOLEAN CheckVadExec(PVOID a)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
SIZE_T ret = 0;
|
||||
NTSTATUS s = ZwQueryVirtualMemory(NtCurrentProcess(), a, MemoryBasicInformation, &mbi, sizeof(mbi), &ret);
|
||||
|
||||
if (!NT_SUCCESS(s))
|
||||
return FALSE;
|
||||
if (mbi.State != MEM_COMMIT)
|
||||
return FALSE;
|
||||
if (mbi.Type == SEC_IMAGE)
|
||||
return FALSE;
|
||||
if (mbi.Type != MEM_PRIVATE && mbi.Type != MEM_MAPPED)
|
||||
return FALSE;
|
||||
return (mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
|
||||
}
|
||||
|
||||
static BOOLEAN WalkForShellcode(ULONG h)
|
||||
{
|
||||
#define MAXWALK 20
|
||||
PVOID frames[MAXWALK] = { 0 };
|
||||
ULONG c = RtlWalkFrameChain(frames, MAXWALK, 1);
|
||||
if (c == 0)
|
||||
return FALSE;
|
||||
|
||||
ULONG limit = RTL_NUMBER_OF(frames);
|
||||
ULONG count = c < limit ? c : limit;
|
||||
ULONG d = h < count ? h : count;
|
||||
|
||||
for (ULONG i = 0; i < d; i++) {
|
||||
PVOID p = frames[i];
|
||||
if (p && CheckVadExec(p))
|
||||
return TRUE;
|
||||
}
|
||||
for (ULONG i = 0; i < d; i++) {
|
||||
ULONG idx = count - 1 - i;
|
||||
if (idx >= d) {
|
||||
PVOID p = frames[idx];
|
||||
if (p && CheckVadExec(p))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static VOID ImageLoadNotify(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (ProcessId == (HANDLE)0 || ProcessId == (HANDLE)4)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = eb;
|
||||
exe.MaximumLength = sizeof(eb);
|
||||
exe.Length = 0;
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &exe)) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(eb) - 1; i++)
|
||||
eb[i] = (WCHAR)n[i];
|
||||
eb[l] = 0;
|
||||
exe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
if (exe.Length & 1) exe.Length--;
|
||||
|
||||
{
|
||||
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)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (IsWhitelist(&exe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
if (FullImageName && FullImageName->Buffer) {
|
||||
if (MatchClrFromNonFramework(FullImageName)) {
|
||||
LogAnsi3("CLR_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, FullImageName);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (WalkForShellcode(8)) {
|
||||
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("SHELLCODE_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, &none);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitImageProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(s))
|
||||
g_ImageNotifyEnabled = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitImageProtect(VOID)
|
||||
{
|
||||
if (g_ImageNotifyEnabled) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static PVOID g_ObRegHandle = NULL;
|
||||
static POB_OPERATION_REGISTRATION g_ObOps = NULL;
|
||||
static OB_CALLBACK_REGISTRATION g_ObReg = { 0 };
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
if (g_Unloading)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info->Object)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE cur = PsGetCurrentProcessId();
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
HANDLE target = PsGetProcessId((PEPROCESS)Info->Object);
|
||||
if (target == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
ACCESS_MASK mask = PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE;
|
||||
PCSTR tn = PsGetProcessImageFileName((PEPROCESS)Info->Object);
|
||||
if (tn && (_stricmp(tn, "lsass.exe") == 0))
|
||||
mask |= PROCESS_VM_READ;
|
||||
|
||||
if ((*desired & mask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
WCHAR cbuf[512] = { 0 }, tbuf[512] = { 0 };
|
||||
UNICODE_STRING cexe = { 0 }, texe = { 0 };
|
||||
cexe.Buffer = cbuf; cexe.MaximumLength = sizeof(cbuf);
|
||||
texe.Buffer = tbuf; texe.MaximumLength = sizeof(tbuf);
|
||||
|
||||
if (logok)
|
||||
GetProcessImagePathByPid(cur, &cexe);
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK old = *desired;
|
||||
*desired &= ~mask;
|
||||
if ((*desired) != old && logok) {
|
||||
GetProcessImagePathByPid(target, &texe);
|
||||
if (cexe.Length == 0) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
cbuf[i] = (WCHAR)n[i];
|
||||
cbuf[l] = 0;
|
||||
cexe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
LogAnsi3("INJECT_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &texe);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreThreadCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Object == NULL)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE cur = PsGetCurrentProcessId();
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
HANDLE owner = PsGetThreadProcessId((PETHREAD)Info->Object);
|
||||
if (owner == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK tmask = THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
|
||||
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
if ((*desired & tmask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
WCHAR cbuf[512] = { 0 };
|
||||
UNICODE_STRING cexe = { 0 };
|
||||
cexe.Buffer = cbuf;
|
||||
cexe.MaximumLength = sizeof(cbuf);
|
||||
|
||||
if (logok)
|
||||
GetProcessImagePathByPid(cur, &cexe);
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK old = *desired;
|
||||
*desired &= ~tmask;
|
||||
if ((*desired) != old && logok) {
|
||||
if (cexe.Length == 0) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
cbuf[i] = (WCHAR)n[i];
|
||||
cbuf[l] = 0;
|
||||
cexe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
UNICODE_STRING tnone = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("THREAD_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &tnone);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitInjectProtect(VOID)
|
||||
{
|
||||
static UNICODE_STRING obAlt;
|
||||
RtlInitUnicodeString(&obAlt, L"385000");
|
||||
|
||||
g_ObOps = (POB_OPERATION_REGISTRATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OB_OPERATION_REGISTRATION) * 2, 'bOpr');
|
||||
if (!g_ObOps)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
RtlZeroMemory(g_ObOps, sizeof(OB_OPERATION_REGISTRATION) * 2);
|
||||
g_ObOps[0].ObjectType = PsProcessType;
|
||||
g_ObOps[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
g_ObOps[0].PreOperation = PreProcessCallback;
|
||||
g_ObOps[1].ObjectType = PsThreadType;
|
||||
g_ObOps[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
g_ObOps[1].PreOperation = PreThreadCallback;
|
||||
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
g_ObReg.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
g_ObReg.OperationRegistrationCount = 2;
|
||||
g_ObReg.Altitude = obAlt;
|
||||
g_ObReg.RegistrationContext = NULL;
|
||||
g_ObReg.OperationRegistration = g_ObOps;
|
||||
|
||||
NTSTATUS s = ObRegisterCallbacks(&g_ObReg, &g_ObRegHandle);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
|
||||
g_ObOps = NULL;
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitInjectProtect(VOID)
|
||||
{
|
||||
if (g_ObRegHandle) {
|
||||
ObUnRegisterCallbacks(g_ObRegHandle);
|
||||
g_ObRegHandle = NULL;
|
||||
}
|
||||
if (g_ObOps) {
|
||||
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
|
||||
g_ObOps = NULL;
|
||||
}
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN WildMatchN(const wchar_t* str, SIZE_T n, const wchar_t* pat)
|
||||
{
|
||||
if (*pat == 0)
|
||||
return n == 0;
|
||||
|
||||
if (*pat == L'*') {
|
||||
if (pat[1] == L'*') {
|
||||
for (SIZE_T i = 0; i <= n; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 2))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
SIZE_T i = 0;
|
||||
for (; i < n && str[i] != L'\\'; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 1))
|
||||
return TRUE;
|
||||
return WildMatchN(str + i, n - i, pat + 1);
|
||||
}
|
||||
if (*pat == L'?')
|
||||
return n > 0 && str[0] != L'\\' && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
if (n == 0)
|
||||
return FALSE;
|
||||
|
||||
WCHAR sc = str[0], pc = *pat;
|
||||
if (sc >= L'A' && sc <= L'Z')
|
||||
sc += 32;
|
||||
if (pc >= L'A' && pc <= L'Z')
|
||||
pc += 32;
|
||||
return sc == pc && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
}
|
||||
|
||||
static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
return FALSE;
|
||||
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
size_t pat_len = wcslen(pat);
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
if (s_len == pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static wchar_t* MatchListGetRule(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
return 0;
|
||||
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
size_t pat_len = wcslen(pat);
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return pat;
|
||||
}
|
||||
else {
|
||||
if (s_len == pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return pat;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0)
|
||||
return pat;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return pat;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __forceinline BOOLEAN USOK(PUNICODE_STRING s)
|
||||
{
|
||||
return s && s->Buffer && s->Length;
|
||||
}
|
||||
|
||||
static BOOLEAN StrStrIW(const wchar_t* s, const wchar_t* sub)
|
||||
{
|
||||
if (!s || !sub)
|
||||
return FALSE;
|
||||
|
||||
SIZE_T n = wcslen(s), m = wcslen(sub);
|
||||
if (m == 0 || n < m)
|
||||
return FALSE;
|
||||
|
||||
for (SIZE_T i = 0; i + m <= n; i++) {
|
||||
SIZE_T j = 0;
|
||||
for (; j < m; j++) {
|
||||
WCHAR a = s[i + j], b = sub[j];
|
||||
if (a >= L'A' && a <= L'Z') a += 32;
|
||||
if (b >= L'A' && b <= L'Z') b += 32;
|
||||
if (a != b) break;
|
||||
}
|
||||
if (j == m)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN EndsWithNameUS(PUNICODE_STRING s, const wchar_t* name)
|
||||
{
|
||||
if (!USOK(s) || !name)
|
||||
return FALSE;
|
||||
SIZE_T n = s->Length / sizeof(WCHAR);
|
||||
SIZE_T m = wcslen(name);
|
||||
if (n < m)
|
||||
return FALSE;
|
||||
return _wcsnicmp(s->Buffer + (n - m), name, m) == 0;
|
||||
}
|
||||
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
{
|
||||
if (USOK(key)) {
|
||||
size_t klen0 = key->Length / sizeof(WCHAR);
|
||||
if (WildMatchN(key->Buffer, klen0, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(exe)) {
|
||||
if (IsWhitelist(exe))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(exe) && USOK(key)) {
|
||||
size_t elen = exe->Length / sizeof(WCHAR);
|
||||
size_t klen = key->Length / sizeof(WCHAR);
|
||||
|
||||
if (((elen >= 12 && _wcsnicmp(exe->Buffer + elen - 12, L"services.exe", 12) == 0) ||
|
||||
(elen >= 11 && _wcsnicmp(exe->Buffer + elen - 11, L"svchost.exe", 11) == 0) ||
|
||||
(elen >= 6 && _wcsnicmp(exe->Buffer + elen - 6, L"sc.exe", 6) == 0))) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
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)) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if ((elen >= 11 && _wcsnicmp(exe->Buffer + elen - 11, L"msiexec.exe", 11) == 0) ||
|
||||
(elen >= 20 && _wcsnicmp(exe->Buffer + elen - 20, L"TrustedInstaller.exe", 20) == 0)) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (elen >= 12 && _wcsnicmp(exe->Buffer + elen - 12, L"schtasks.exe", 12) == 0) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (USOK(key)) {
|
||||
if (MatchBlockReg(key))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(valueName)) {
|
||||
SIZE_T n = valueName->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; i < (SIZE_T)-1 && g_BlockReg[i]; ++i) {
|
||||
wchar_t* pat = g_BlockReg[i];
|
||||
if (wcschr(pat, L'\\') || wcschr(pat, L'*') || wcschr(pat, L'?'))
|
||||
continue;
|
||||
size_t m = wcslen(pat);
|
||||
if (n == m && _wcsnicmp(valueName->Buffer, pat, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
|
||||
{
|
||||
PEPROCESS process = NULL;
|
||||
NTSTATUS status = PsLookupProcessByProcessId(pid, &process);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FALSE;
|
||||
|
||||
HANDLE h = NULL;
|
||||
status = ObOpenObjectByPointer(process, OBJ_KERNEL_HANDLE, NULL, PROCESS_QUERY_LIMITED_INFORMATION, *PsProcessType, KernelMode, &h);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
ULONG len = 0;
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, NULL, 0, &len);
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH || len == 0) {
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
PVOID buf = ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'iPgN');
|
||||
if (!buf) {
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, buf, len, &len);
|
||||
if (NT_SUCCESS(status)) {
|
||||
PUNICODE_STRING image = (PUNICODE_STRING)buf;
|
||||
if (image->Buffer && image->Length > 0 && ProcessImagePath->MaximumLength >= image->Length + sizeof(WCHAR)) {
|
||||
RtlCopyMemory(ProcessImagePath->Buffer, image->Buffer, image->Length);
|
||||
ProcessImagePath->Length = image->Length;
|
||||
ProcessImagePath->Buffer[image->Length / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s)
|
||||
{
|
||||
if (MatchList(s, g_WhitelistExcept))
|
||||
return FALSE;
|
||||
return MatchList(s, g_Whitelist);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockReg);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockReg);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockFile(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockRansom(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockRansom);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s)
|
||||
{
|
||||
if (!USOK(s))
|
||||
return FALSE;
|
||||
SIZE_T n = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; g_Blocksuffix[i]; ++i) {
|
||||
wchar_t* ext = g_Blocksuffix[i];
|
||||
SIZE_T m = wcslen(ext);
|
||||
if (n >= m && _wcsnicmp(s->Buffer + (n - m), ext, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img)
|
||||
{
|
||||
for (SIZE_T i = 0; g_RemoteSuspectBins[i]; ++i)
|
||||
if (EndsWithNameUS(img, g_RemoteSuspectBins[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(cmd))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = cmd->Buffer;
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsHttp[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i]))
|
||||
return TRUE;
|
||||
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsGeneric[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsGeneric[i]))
|
||||
return TRUE;
|
||||
|
||||
if (USOK(img) && EndsWithNameUS(img, L"cmd.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromCmdExe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromCmdExe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"rundll32.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromRundll32Exe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromRundll32Exe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"regsvr32.exe")) {
|
||||
BOOLEAN need = FALSE, http = FALSE;
|
||||
for (SIZE_T i = 0; g_RemoteCmdRegsvr32Need[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdRegsvr32Need[i])) {
|
||||
need = TRUE;
|
||||
break;
|
||||
}
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsHttp[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i])) {
|
||||
http = TRUE;
|
||||
break;
|
||||
}
|
||||
if (need && http)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (USOK(img) && EndsWithNameUS(img, L"mshta.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromMshtaExe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromMshtaExe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod)
|
||||
{
|
||||
for (SIZE_T i = 0; g_ScreenCapModules[i]; ++i)
|
||||
if (EndsWithNameUS(mod, g_ScreenCapModules[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(img))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = img->Buffer;
|
||||
if (StrStrIW(s, L"\\Users\\")) {
|
||||
for (SIZE_T i = 0; g_ScreenUserSubdirs[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenUserSubdirs[i]))
|
||||
return TRUE;
|
||||
}
|
||||
for (SIZE_T i = 0; g_ScreenOtherProcNeedles[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenOtherProcNeedles[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName)
|
||||
{
|
||||
if (!USOK(fullImageName))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = fullImageName->Buffer;
|
||||
BOOLEAN isClr = StrStrIW(s, L"\\clr.dll") || StrStrIW(s, L"\\mscorwks.dll");
|
||||
|
||||
if (!isClr)
|
||||
return FALSE;
|
||||
if (!StrStrIW(s, L"\\Windows\\Microsoft.NET\\"))
|
||||
return TRUE;
|
||||
if (!StrStrIW(s, L"\\Framework\\") && !StrStrIW(s, L"\\Framework64\\"))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s);
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s);
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s);
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s);
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe);
|
||||
|
||||
BOOLEAN MatchBlockFile(PUNICODE_STRING s);
|
||||
BOOLEAN MatchBlockRansom(PUNICODE_STRING s);
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s);
|
||||
|
||||
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img);
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img);
|
||||
|
||||
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod);
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img);
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName);
|
||||
@@ -0,0 +1,224 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
typedef union _PS_PROTECTION {
|
||||
UCHAR Level;
|
||||
struct {
|
||||
UCHAR Type : 3;
|
||||
UCHAR Audit : 1;
|
||||
UCHAR Signer : 4;
|
||||
}Flags;
|
||||
}PS_PROTECTION, * PPS_PROTECTION;
|
||||
|
||||
typedef struct _OBCTX {
|
||||
POBJECT_TYPE ProcessType;
|
||||
POBJECT_TYPE ThreadType;
|
||||
}OBCTX, * POBCTX;
|
||||
|
||||
static PVOID g_PsSetProcessProtection = NULL;
|
||||
static BOOLEAN g_ProcNotifyEnabled = FALSE;
|
||||
static PVOID g_ObCookie = NULL;
|
||||
static OBCTX g_ObCtx = { 0 };
|
||||
static HANDLE g_ProtectedPid = NULL;
|
||||
static PWCH g_ObAltitudeBuf = NULL;
|
||||
|
||||
static BOOLEAN IsProcNameEq(PEPROCESS p, PCSTR name)
|
||||
{
|
||||
PCSTR img = PsGetProcessImageFileName(p);
|
||||
if (!img || !name)
|
||||
return FALSE;
|
||||
|
||||
ANSI_STRING a = { 0 }, b = { 0 };
|
||||
RtlInitAnsiString(&a, img);
|
||||
RtlInitAnsiString(&b, name);
|
||||
return RtlEqualString(&a, &b, TRUE) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static NTSTATUS SetPPL31(PEPROCESS p)
|
||||
{
|
||||
if (!g_PsSetProcessProtection)
|
||||
return STATUS_PROCEDURE_NOT_FOUND;
|
||||
|
||||
PS_PROTECTION pr = { 0 };
|
||||
pr.Level = 0x31;
|
||||
((VOID(NTAPI*)(PEPROCESS, PS_PROTECTION))g_PsSetProcessProtection)(p, pr);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID LogSet(HANDLE pid, NTSTATUS st)
|
||||
{
|
||||
CHAR b[128] = { 0 };
|
||||
RtlStringCchPrintfA(b, RTL_NUMBER_OF(b), "PROC_PPL_SET | %u | 0x31 | 0x%08X", (ULONG)(ULONG_PTR)pid, st);
|
||||
SendPipeLog(b, strlen(b));
|
||||
}
|
||||
|
||||
static VOID UpdateProtectedPid(HANDLE pid)
|
||||
{
|
||||
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, pid);
|
||||
}
|
||||
|
||||
static VOID BootstrapExisting(VOID)
|
||||
{
|
||||
typedef PEPROCESS(NTAPI* PFN_PsGetNextProcess)(PEPROCESS);
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"PsGetNextProcess");
|
||||
PFN_PsGetNextProcess fp = (PFN_PsGetNextProcess)MmGetSystemRoutineAddress(&n);
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
PEPROCESS p = fp(NULL);
|
||||
while (p) {
|
||||
if (IsProcNameEq(p, "PYAS.exe")) {
|
||||
HANDLE pid = PsGetProcessId(p);
|
||||
NTSTATUS s = SetPPL31(p);
|
||||
UpdateProtectedPid(pid);
|
||||
LogSet(pid, s);
|
||||
}
|
||||
PEPROCESS nx = fp(p);
|
||||
ObDereferenceObject(p);
|
||||
p = nx;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID StripProcessAccess(POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
ACCESS_MASK d = PROCESS_TERMINATE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_SET_INFORMATION | PROCESS_SET_LIMITED_INFORMATION | PROCESS_SUSPEND_RESUME | PROCESS_DUP_HANDLE;
|
||||
if (Info->Operation == OB_OPERATION_HANDLE_CREATE) {
|
||||
Info->Parameters->CreateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
else {
|
||||
Info->Parameters->DuplicateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID StripThreadAccess(POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
ACCESS_MASK d = THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
|
||||
if (Info->Operation == OB_OPERATION_HANDLE_CREATE) {
|
||||
Info->Parameters->CreateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
else {
|
||||
Info->Parameters->DuplicateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS ObPreOperation(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (g_Unloading)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
if (Info->ObjectType == g_ObCtx.ProcessType) {
|
||||
PEPROCESS pe = (PEPROCESS)Info->Object;
|
||||
HANDLE tpid = PsGetProcessId(pe);
|
||||
if (g_ProtectedPid && tpid == g_ProtectedPid) {
|
||||
if (PsGetCurrentProcessId() != g_ProtectedPid) StripProcessAccess(Info);
|
||||
}
|
||||
}
|
||||
else if (Info->ObjectType == g_ObCtx.ThreadType) {
|
||||
PETHREAD th = (PETHREAD)Info->Object;
|
||||
HANDLE owner = PsGetThreadProcessId(th);
|
||||
if (g_ProtectedPid && owner == g_ProtectedPid) {
|
||||
if (PsGetCurrentProcessId() != g_ProtectedPid) StripThreadAccess(Info);
|
||||
}
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InitObCallbacks(VOID)
|
||||
{
|
||||
g_ObCtx.ProcessType = *PsProcessType;
|
||||
g_ObCtx.ThreadType = *PsThreadType;
|
||||
|
||||
static OB_OPERATION_REGISTRATION ops[2];
|
||||
RtlZeroMemory(ops, sizeof(ops));
|
||||
ops[0].ObjectType = PsProcessType;
|
||||
ops[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ops[0].PreOperation = ObPreOperation;
|
||||
ops[1].ObjectType = PsThreadType;
|
||||
ops[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ops[1].PreOperation = ObPreOperation;
|
||||
|
||||
const WCHAR* altStatic = L"321000";
|
||||
USHORT alen = (USHORT)(wcslen(altStatic) * sizeof(WCHAR));
|
||||
g_ObAltitudeBuf = (PWCH)ExAllocatePool2(POOL_FLAG_NON_PAGED, alen + sizeof(WCHAR), 'tlaO');
|
||||
if (!g_ObAltitudeBuf)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
RtlCopyMemory(g_ObAltitudeBuf, altStatic, alen);
|
||||
g_ObAltitudeBuf[alen / sizeof(WCHAR)] = 0;
|
||||
|
||||
UNICODE_STRING alt = { 0 };
|
||||
alt.Buffer = g_ObAltitudeBuf;
|
||||
alt.Length = alen;
|
||||
alt.MaximumLength = alen;
|
||||
|
||||
OB_CALLBACK_REGISTRATION reg = { 0 };
|
||||
reg.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
reg.OperationRegistrationCount = 2;
|
||||
reg.RegistrationContext = g_ObAltitudeBuf;
|
||||
reg.Altitude = alt;
|
||||
reg.OperationRegistration = ops;
|
||||
|
||||
NTSTATUS s = ObRegisterCallbacks(®, &g_ObCookie);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ExFreePool2(g_ObAltitudeBuf, 'tlaO', NULL, 0);
|
||||
g_ObAltitudeBuf = NULL;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static VOID ProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
|
||||
{
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
if (CreateInfo) {
|
||||
if (IsProcNameEq(Process, "PYAS.exe")) {
|
||||
NTSTATUS s = SetPPL31(Process);
|
||||
UpdateProtectedPid(ProcessId);
|
||||
LogSet(ProcessId, s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (g_ProtectedPid && ProcessId == g_ProtectedPid) UpdateProtectedPid(NULL);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitProcessProtect(VOID)
|
||||
{
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"PsSetProcessProtection");
|
||||
g_PsSetProcessProtection = MmGetSystemRoutineAddress(&n);
|
||||
|
||||
NTSTATUS s = InitObCallbacks();
|
||||
if (!NT_SUCCESS(s))
|
||||
return s;
|
||||
|
||||
s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
|
||||
if (NT_SUCCESS(s))
|
||||
g_ProcNotifyEnabled = TRUE;
|
||||
BootstrapExisting();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID UninitProcessProtect(VOID)
|
||||
{
|
||||
if (g_ProcNotifyEnabled) {
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, TRUE);
|
||||
g_ProcNotifyEnabled = FALSE;
|
||||
}
|
||||
if (g_ObCookie) {
|
||||
ObUnRegisterCallbacks(g_ObCookie);
|
||||
g_ObCookie = NULL;
|
||||
}
|
||||
if (g_ObAltitudeBuf) {
|
||||
ExFreePool2(g_ObAltitudeBuf, 'tlaO', NULL, 0);
|
||||
g_ObAltitudeBuf = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath, POBJECT_NAME_INFORMATION* OutInfo)
|
||||
{
|
||||
ULONG len = 0;
|
||||
NTSTATUS s = ObQueryNameString(KeyObject, NULL, 0, &len);
|
||||
if (s != STATUS_INFO_LENGTH_MISMATCH)
|
||||
return FALSE;
|
||||
|
||||
POBJECT_NAME_INFORMATION p = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'rgeR');
|
||||
if (!p)
|
||||
return FALSE;
|
||||
|
||||
s = ObQueryNameString(KeyObject, p, len, &len);
|
||||
if (!NT_SUCCESS(s) || !p->Name.Buffer || p->Name.Length == 0) {
|
||||
ExFreePool2(p, 'rgeR', NULL, 0);
|
||||
return FALSE;
|
||||
}
|
||||
*OutInfo = p;
|
||||
OutPath->Buffer = p->Name.Buffer;
|
||||
OutPath->Length = p->Name.Length;
|
||||
OutPath->MaximumLength = p->Name.Length;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ctx);
|
||||
if (g_Unloading)
|
||||
return STATUS_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
REG_NOTIFY_CLASS type = (REG_NOTIFY_CLASS)(ULONG_PTR)arg1;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
UNICODE_STRING checkReg = { 0 };
|
||||
POBJECT_NAME_INFORMATION nameInfo = NULL;
|
||||
|
||||
UNICODE_STRING exe = { 0 };
|
||||
WCHAR exeBuf[260] = { 0 };
|
||||
exe.Buffer = exeBuf;
|
||||
exe.MaximumLength = sizeof(exeBuf);
|
||||
exe.Length = 0;
|
||||
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
if (pid == (HANDLE)0 || pid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
PUNICODE_STRING valueName = NULL;
|
||||
BOOLEAN need_log = FALSE;
|
||||
BOOLEAN canQuery = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
if (type == RegNtPreSetValueKey) {
|
||||
PREG_SET_VALUE_KEY_INFORMATION s = (PREG_SET_VALUE_KEY_INFORMATION)arg2;
|
||||
valueName = s->ValueName;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(s->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreDeleteValueKey) {
|
||||
PREG_DELETE_VALUE_KEY_INFORMATION d = (PREG_DELETE_VALUE_KEY_INFORMATION)arg2;
|
||||
valueName = d->ValueName;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreDeleteKey) {
|
||||
PREG_DELETE_KEY_INFORMATION d = (PREG_DELETE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreRenameKey) {
|
||||
PREG_RENAME_KEY_INFORMATION r = (PREG_RENAME_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(r->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreCreateKeyEx) {
|
||||
PREG_CREATE_KEY_INFORMATION c = (PREG_CREATE_KEY_INFORMATION)arg2;
|
||||
if (c->CompleteName)
|
||||
checkReg = *c->CompleteName;
|
||||
}
|
||||
else if (type == RegNtPreReplaceKey) {
|
||||
PREG_REPLACE_KEY_INFORMATION rk = (PREG_REPLACE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(rk->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreSaveKey) {
|
||||
PREG_SAVE_KEY_INFORMATION sk = (PREG_SAVE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(sk->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreRestoreKey) {
|
||||
PREG_RESTORE_KEY_INFORMATION res = (PREG_RESTORE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(res->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreLoadKey) {
|
||||
PREG_LOAD_KEY_INFORMATION lk = (PREG_LOAD_KEY_INFORMATION)arg2;
|
||||
if (lk->KeyName)
|
||||
checkReg = *lk->KeyName;
|
||||
}
|
||||
|
||||
if (checkReg.Buffer || (valueName && valueName->Buffer)) {
|
||||
if (canQuery)
|
||||
GetProcessImagePathByPid(pid, &exe);
|
||||
if (IsRegistryBlock(&checkReg, valueName, &exe)) {
|
||||
need_log = TRUE;
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
if (nameInfo)
|
||||
ExFreePool2(nameInfo, 'rgeR', NULL, 0);
|
||||
if (need_log)
|
||||
LogAnsi3("REG_BLOCK", (ULONG)(ULONG_PTR)pid, &exe, &checkReg);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_RemoteNotifyEnabled = FALSE;
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static VOID ProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Process);
|
||||
UNREFERENCED_PARAMETER(ProcessId);
|
||||
|
||||
if (!CreateInfo)
|
||||
return;
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
|
||||
HANDLE ppid = CreateInfo->CreatingThreadId.UniqueProcess;
|
||||
if (ppid == (HANDLE)0 || ppid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
UNICODE_STRING parent = { 0 };
|
||||
WCHAR pbuf[512] = { 0 };
|
||||
parent.Buffer = pbuf;
|
||||
parent.MaximumLength = sizeof(pbuf);
|
||||
|
||||
if (!GetProcessImagePathByPid(ppid, &parent)) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
|
||||
pbuf[i] = (WCHAR)n[i];
|
||||
pbuf[l] = 0;
|
||||
parent.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
if (IsWhitelist(&parent)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
UNICODE_STRING img = { 0 };
|
||||
if (CreateInfo->ImageFileName)
|
||||
img = *CreateInfo->ImageFileName;
|
||||
UNICODE_STRING cmd = { 0 };
|
||||
if (CreateInfo->CommandLine)
|
||||
cmd = *CreateInfo->CommandLine;
|
||||
|
||||
BOOLEAN suspect = FALSE;
|
||||
if (MatchRemoteSuspectBin(&img) && MatchRemoteCommand(&cmd, &img))
|
||||
suspect = TRUE;
|
||||
|
||||
if (suspect) {
|
||||
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
|
||||
if (cmd.Buffer && cmd.Length)
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &cmd);
|
||||
else if (img.Buffer && img.Length)
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &img);
|
||||
else {
|
||||
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &none);
|
||||
}
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitRemoteProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
|
||||
if (NT_SUCCESS(s))
|
||||
g_RemoteNotifyEnabled = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitRemoteProtect(VOID)
|
||||
{
|
||||
if (g_RemoteNotifyEnabled) {
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, TRUE);
|
||||
g_RemoteNotifyEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
#include <ntifs.h>
|
||||
#include "ProtectRules.h"
|
||||
|
||||
wchar_t* g_Whitelist[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Program*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\**",
|
||||
L"**\\PYAS*.exe",
|
||||
L"Registry",
|
||||
L"vmmem*",
|
||||
NULL
|
||||
};
|
||||
|
||||
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*\\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",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_AttachDisk[] = {
|
||||
L"\\FileSystem\\Ntfs",
|
||||
L"\\FileSystem\\Refs",
|
||||
L"\\FileSystem\\Fastfat",
|
||||
L"\\FileSystem\\exfat",
|
||||
L"\\FileSystem\\Udfs",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_BlockFile[] = {
|
||||
L"\\Device\\Harddisk*\\DR*",
|
||||
L"\\Device\\Harddisk*\\Partition\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Boot\\**",
|
||||
L"\\Device\\HarddiskVolume*\\EFI\\**",
|
||||
L"\\Device\\HarddiskVolume*\\bootmgr",
|
||||
L"\\Device\\HarddiskVolume*\\Recovery\\**",
|
||||
L"\\Device\\HarddiskVolume*\\System Volume Information\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\**",
|
||||
L"\\Device\\HarddiskVolume*\\$*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\ProgramData\\PYAS\\**",
|
||||
L"**\\PYAS.exe",
|
||||
|
||||
L"**\\CON**",
|
||||
L"**\\PRN**",
|
||||
L"**\\AUX**",
|
||||
L"**\\NUL**",
|
||||
L"**\\COM*\\**",
|
||||
L"**\\LPT*\\**",
|
||||
L"**\\evil*.exe",
|
||||
L"**\\OSDATA**",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_BlockRansom[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Desktop\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Downloads\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Documents\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Music\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Pictures\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Videos\\**",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_Blocksuffix[] = {
|
||||
L".exe", L".dll", L".sys", L".com", L".scr",
|
||||
L".zip", L".7z", L".rar", L".tar", L".gz",
|
||||
L".js", L".bat", L".cmd", L".ps1", L".vbs",
|
||||
L".ppt", L".pptx", L".wps", L".txt", L".rtf",
|
||||
L".pdf", L".xls", L".xlsx", L".doc", L".docx",
|
||||
L".jpg", L".jpeg", L".png", L".webp", L".gif",
|
||||
L".mp3", L".wav", L".aac", L".ogg", L".flac",
|
||||
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
|
||||
L".aux", L".cur", L".mui", L".ttf", L".efi",
|
||||
NULL
|
||||
};
|
||||
|
||||
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",
|
||||
L"ConsentPromptBehaviorAdmin",
|
||||
L"NoControlPanel",
|
||||
L"NoDrives",
|
||||
L"NoFileMenu",
|
||||
L"NoFind",
|
||||
L"NoStartMenuPinnedList",
|
||||
L"NoSetFolders",
|
||||
L"NoSetFolderOptions",
|
||||
L"NoViewOnDrive",
|
||||
L"NoClose",
|
||||
L"NoDesktop",
|
||||
L"NoLogoff",
|
||||
L"NoFolderOptions",
|
||||
L"RestrictRun",
|
||||
L"NoViewContextMenu",
|
||||
L"HideClock",
|
||||
L"NoStartMenuMyGames",
|
||||
L"NoStartMenuMyMusic",
|
||||
L"DisableCMD",
|
||||
L"NoAddingComponents",
|
||||
L"NoWinKeys",
|
||||
L"NoStartMenuLogOff",
|
||||
L"NoSimpleNetIDList",
|
||||
L"NoLowDiskSpaceChecks",
|
||||
L"DisableLockWorkstation",
|
||||
L"Restrict_Run",
|
||||
L"DisableTaskMgr",
|
||||
L"DisableRegistryTools",
|
||||
L"DisableChangePassword",
|
||||
L"Wallpaper",
|
||||
L"NoComponents",
|
||||
L"NoStartMenuMorePrograms",
|
||||
L"NoActiveDesktop",
|
||||
L"NoSetActiveDesktop",
|
||||
L"NoRecentDocsMenu",
|
||||
L"NoWindowsUpdate",
|
||||
L"NoChangeStartMenu",
|
||||
L"NoFavoritesMenu",
|
||||
L"NoRecentDocsHistory",
|
||||
L"NoSetTaskbar",
|
||||
L"NoSMHelp",
|
||||
L"NoTrayContextMenu",
|
||||
L"NoManageMyComputerVerb",
|
||||
L"NoRealMode",
|
||||
L"NoRun",
|
||||
L"ClearRecentDocsOnExit",
|
||||
L"NoActiveDesktopChanges",
|
||||
L"NoStartMenuNetworkPlaces",
|
||||
L"NoLockScreen",
|
||||
L"HideFastUserSwitching",
|
||||
L"ConsentPromptBehaviorUser",
|
||||
L"EnableSecureUIAPaths",
|
||||
L"NoSRM",
|
||||
L"NoInteractiveServices",
|
||||
L"ShutdownWithoutLogon",
|
||||
L"AutoAdminLogon",
|
||||
L"ImagePath",
|
||||
L"BootExecute",
|
||||
L"SubSystems",
|
||||
L"FirmwareBootDevice",
|
||||
L"BootDriverFlags",
|
||||
L"SystemStartOptions",
|
||||
L"CrashDumpEnabled",
|
||||
L"DisableAutomaticRestartOnFailure",
|
||||
L"SystemBiosVersion",
|
||||
L"NoTheme",
|
||||
L"PendingFileRenameOperations",
|
||||
L"SystemSetupInProgress",
|
||||
L"CmdLine",
|
||||
L"SetupType",
|
||||
L"AutoReboot",
|
||||
L"Shell",
|
||||
L"Userinit",
|
||||
L"UIHost",
|
||||
L"Debugger",
|
||||
L"DefaultUserName",
|
||||
L"DefaultPassword",
|
||||
L"AltDefaultUserName",
|
||||
L"AltDefaultPassword",
|
||||
L"DisableCAD",
|
||||
L"LegalNoticeCaption",
|
||||
L"LegalNoticeText",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteSuspectBins[] = {
|
||||
L"powershell.exe",
|
||||
L"pwsh.exe",
|
||||
L"wscript.exe",
|
||||
L"cscript.exe",
|
||||
L"mshta.exe",
|
||||
L"rundll32.exe",
|
||||
L"regsvr32.exe",
|
||||
L"cmd.exe",
|
||||
L"bitsadmin.exe",
|
||||
L"msbuild.exe",
|
||||
L"installutil.exe",
|
||||
L"wmic.exe",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdIndicatorsHttp[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdIndicatorsGeneric[] = {
|
||||
L"-enc",
|
||||
L" -e ",
|
||||
L"-encodedcommand",
|
||||
L"frombase64string(",
|
||||
L"iex(",
|
||||
L"downloadstring(",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdFromCmdExe[] = {
|
||||
L"powershell",
|
||||
L"pwsh",
|
||||
L"mshta",
|
||||
L"rundll32",
|
||||
L"wscript",
|
||||
L"cscript",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdFromRundll32Exe[] = {
|
||||
L".dll,",
|
||||
L"javascript:",
|
||||
L"http",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdRegsvr32Need[] = {
|
||||
L"/i:",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdFromMshtaExe[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_ScreenCapModules[] = {
|
||||
L"windows.graphics.capture.dll",
|
||||
L"dxgi.dll",
|
||||
L"d3d11.dll",
|
||||
L"dwmapi.dll",
|
||||
L"dcomp.dll",
|
||||
L"mfreadwrite.dll",
|
||||
L"mfplat.dll",
|
||||
L"opengl32.dll",
|
||||
L"gdi32full.dll",
|
||||
L"win32u.dll",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_ScreenUserSubdirs[] = {
|
||||
L"\\AppData\\",
|
||||
L"\\Temp\\",
|
||||
L"\\Downloads\\",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_ScreenOtherProcNeedles[] = {
|
||||
L"\\ProgramData\\",
|
||||
NULL
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
|
||||
extern wchar_t* g_Whitelist[];
|
||||
extern wchar_t* g_WhitelistExcept[];
|
||||
|
||||
extern wchar_t* g_AttachDisk[];
|
||||
extern wchar_t* g_BlockFile[];
|
||||
extern wchar_t* g_BlockRansom[];
|
||||
extern wchar_t* g_Blocksuffix[];
|
||||
|
||||
extern wchar_t* g_BlockReg[];
|
||||
|
||||
extern wchar_t* g_RemoteSuspectBins[];
|
||||
|
||||
extern wchar_t* g_RemoteCmdIndicatorsHttp[];
|
||||
extern wchar_t* g_RemoteCmdIndicatorsGeneric[];
|
||||
extern wchar_t* g_RemoteCmdFromCmdExe[];
|
||||
extern wchar_t* g_RemoteCmdFromRundll32Exe[];
|
||||
extern wchar_t* g_RemoteCmdRegsvr32Need[];
|
||||
extern wchar_t* g_RemoteCmdFromMshtaExe[];
|
||||
|
||||
extern wchar_t* g_ScreenCapModules[];
|
||||
extern wchar_t* g_ScreenUserSubdirs[];
|
||||
extern wchar_t* g_ScreenOtherProcNeedles[];
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_ScreenImgCb = FALSE;
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
if (!FullImageName || !FullImageName->Buffer || FullImageName->Length == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
UNICODE_STRING proc = { 0 };
|
||||
WCHAR pbuf[512] = { 0 };
|
||||
proc.Buffer = pbuf;
|
||||
proc.MaximumLength = sizeof(pbuf);
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &proc)) {
|
||||
PEPROCESS eproc = NULL;
|
||||
if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &eproc))) {
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
SIZE_T l = n ? strlen(n) : 0;
|
||||
for (SIZE_T i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
|
||||
pbuf[i] = (WCHAR)n[i];
|
||||
pbuf[l] = 0;
|
||||
proc.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
ObDereferenceObject(eproc);
|
||||
}
|
||||
}
|
||||
if (proc.Length == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
if (IsWhitelist(&proc)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOLEAN suspicious = MatchSuspiciousProcPath(&proc);
|
||||
if (suspicious && MatchScreenCapModule(FullImageName))
|
||||
LogAnsi3("SCREEN_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &proc, FullImageName);
|
||||
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitScreenProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(s)) g_ScreenImgCb = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitScreenProtect(VOID)
|
||||
{
|
||||
if (g_ScreenImgCb) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ScreenImgCb = FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
# PYAS
|
||||
|
||||
Python antivirus software uses deep learning and behavioral monitoring to block threats!
|
||||
Antivirus software written in Python and C that blocks threats through deep learning and behavioral monitoring!
|
||||
|
||||

|
||||
<img width="2245" height="1477" alt="PYAS_UI" src="https://github.com/user-attachments/assets/87d40261-7655-49ad-a19c-1ffcca60584f" />
|
||||
|
||||
## Readme Language
|
||||
|
||||
English : https://github.com/87owo/PYAS/blob/main/README.md
|
||||
|
||||
繁體中文 : https://github.com/87owo/PYAS/blob/main/README_zh_TW.md
|
||||
|
||||
简体中文 : https://github.com/87owo/PYAS/blob/main/README_zh_CN.md
|
||||
@@ -32,37 +34,58 @@ The following lists the storage locations of all relevant code and other related
|
||||
```
|
||||
PYAS/
|
||||
├── Engine/
|
||||
│ ├── Models/ # Directory for ONNX AI models
|
||||
│ └── Rules/ # Directory for YARA and network rules
|
||||
│ ├── Models/
|
||||
│ │ ├── convert.py # Convert executable files or other files to images
|
||||
│ │ └── train.py # TensorFlow CNN model training complete code
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ ├── rules.yar # Yara virus signature rule matching
|
||||
│ └── rules.ips # IP network address rule matching
|
||||
│
|
||||
├── Plugins/
|
||||
│ └── Filter/ # Windows kernel driver for security protection
|
||||
│ └── Filter/
|
||||
│ ├── DriverEntry.c # Main driver entry and initialization logic
|
||||
│ ├── DriverEntry.h # Global driver definitions, constants, and functions
|
||||
│ ├── DriverPipe.c # Kernel to user pipe logging implementation
|
||||
│ ├── ProtectBoot.c # Disk boot sector write protection
|
||||
│ ├── ProtectImage.c # Image load monitoring and shellcode detection
|
||||
│ ├── ProtectInject.c # Process thread handle access control to prevent injection
|
||||
│ ├── ProtectReg.c # Registry modification protection
|
||||
│ ├── ProtectRules.c # White, block list, and matching logic for files registry
|
||||
│ └── ProtectRules.h # Protection rule declarations
|
||||
│
|
||||
├── PYAS.py # Main application and GUI interface
|
||||
├── PYAS_Config.py # Configuration handling and global parameters
|
||||
├── PYAS_Engine.py # YARA and ONNX Scanning engine
|
||||
├── PYAS_Interface.py # User interface definitions and widget logic
|
||||
├── PYAS_Resource.py # Static resource management
|
||||
├── PYAS_Version.py # Packaging and version info
|
||||
└── ... # Other supplementary folders and files
|
||||
├── PYAS.py # Main application entry point and UI to engine interface
|
||||
├── PYAS_Config.py # Configuration loading, saving, and global parameters
|
||||
├── PYAS_Engine.py # Core scanning engine: YARA, IP, ONNX model execution
|
||||
├── PYAS_Interface.py # User interface components and event handling
|
||||
├── PYAS_Resource.py # Static image and icon resource management
|
||||
├── PYAS_Version.py # Version metadata for packaging and updates
|
||||
└── ... # Other supplementary folders and files
|
||||
```
|
||||
|
||||
## Program Architecture
|
||||
|
||||
PYAS antivirus software complete program flow architecture diagram.
|
||||
|
||||
<img width="23000" height="6000" alt="PYAS_Architecture" src="https://github.com/user-attachments/assets/3a7ce637-c2b3-4719-9658-07f286cfa879" />
|
||||
|
||||
## Packaged Releases
|
||||
|
||||
Packaged Releases Download: https://github.com/87owo/PYAS/releases
|
||||
|
||||
## Support System
|
||||
|
||||
| Config | Permissions | System version | Available memory | Available storage |
|
||||
|-----------|---------------|----------------|------------------|-------------------|
|
||||
| Minimum | Administrator | Windows 8.1 | 200MB | 100MB |
|
||||
| Recommend | Administrator | Windows 10 | 300MB | 200MB |
|
||||
| Optimal | Administrator | Windows 11 | 500MB | 200MB |
|
||||
| Config | Permissions | System version | Processor | Memory | Storage |
|
||||
|-----------|---------------|----------------------|-----------|--------|---------|
|
||||
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
|
||||
| Recommend | Administrator | >= Windows 10 (21H2) | 3 GHz | 500MB | 200MB |
|
||||
|
||||
## Official Website
|
||||
|
||||
https://github.com/87owo/PYAS
|
||||
Official Website : https://pyantivirus.wixsite.com/pyas
|
||||
|
||||
Open Source Website : https://github.com/87owo/PYAS
|
||||
|
||||
## Project License
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.md
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
+59
-29
@@ -1,8 +1,20 @@
|
||||
# PYAS (Python 杀毒软件)
|
||||
# PYAS
|
||||
|
||||

|
||||
使用 Python 和 C 编写的杀毒软件,并通过深度学习和行为监控阻止威胁!
|
||||
|
||||
## 安装要求
|
||||
<img width="2245" height="1477" alt="PYAS_UI" src="https://github.com/user-attachments/assets/87d40261-7655-49ad-a19c-1ffcca60584f" />
|
||||
|
||||
## 说明语言
|
||||
|
||||
English : https://github.com/87owo/PYAS/blob/main/README.md
|
||||
|
||||
繁體中文 : https://github.com/87owo/PYAS/blob/main/README_zh_TW.md
|
||||
|
||||
简体中文 : https://github.com/87owo/PYAS/blob/main/README_zh_CN.md
|
||||
|
||||
## 安装需求
|
||||
|
||||
建议使用 Python 3.10。其他版本的 Python 可能需要不同的 pip 指令。
|
||||
|
||||
```
|
||||
pip install numpy==1.26.4
|
||||
@@ -15,47 +27,65 @@ pip install requests==2.32.4
|
||||
pip install yara-python==4.5.4
|
||||
```
|
||||
|
||||
## 文件信息
|
||||
## 文件讯息
|
||||
|
||||
以下列出所有相关代码与文件的存放位置。
|
||||
|
||||
```
|
||||
PYAS/
|
||||
├── PYAS.py # 主程序,提供 PySide6 视觉化操作介面
|
||||
├── PYAS_Config.py # 设定档处理与全域参数管理
|
||||
├── PYAS_Engine.py # 扫描引擎,整合 YARA 规则与 ONNX AI 模型
|
||||
├── PYAS_Interface.py # UI 介面定义与元件交互
|
||||
├── PYAS_Resource.py # 资源档与静态资源管理
|
||||
├── PYAS_Version.py # 程式版本与打包资讯
|
||||
├── Engine/
|
||||
│ ├── Models/ # AI 模型(ONNX 格式)存放目录
|
||||
│ └── Rules/ # YARA 规则与网路规则存放目录
|
||||
│ ├── Models/
|
||||
│ │ ├── convert.py # 将可执行文件或其他文件转换为图片
|
||||
│ │ └── train.py # TensorFlow CNN 模型训练完整代码
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ ├── rules.yar # YARA 病毒特征码规则匹配
|
||||
│ └── rules.ips # IP 网络地址规则匹配
|
||||
│
|
||||
├── Plugins/
|
||||
│ └── Filter/ # 驱动程式目录
|
||||
│ └── PYAS_Driver.sys # Windows 驱动程式(系统保护用)
|
||||
└── ... # 其它辅助资料夹与档案
|
||||
│ └── Filter/
|
||||
│ ├── DriverEntry.c # 驱动主入口与初始化逻辑
|
||||
│ ├── DriverEntry.h # 全局驱动定义、常量与函数
|
||||
│ ├── DriverPipe.c # 内核与用户层的管道日志传输实现
|
||||
│ ├── ProtectBoot.c # 磁盘引导区写入保护
|
||||
│ ├── ProtectImage.c # 映像加载监控与 shellcode 检测
|
||||
│ ├── ProtectInject.c # 进程与线程控制,防止注入
|
||||
│ ├── ProtectReg.c # 注册表修改保护
|
||||
│ ├── ProtectRules.c # 白名单、阻止名单与文件/注册表匹配逻辑
|
||||
│ └── ProtectRules.h # 保护规则声明
|
||||
│
|
||||
├── PYAS.py # 主应用程序入口点与 UI 到引擎的接口
|
||||
├── PYAS_Config.py # 配置加载、保存与全局参数
|
||||
├── PYAS_Engine.py # 核心扫描引擎:YARA、IP、ONNX 模型执行
|
||||
├── PYAS_Interface.py # 用户界面组件与事件处理
|
||||
├── PYAS_Resource.py # 静态图片与图标资源管理
|
||||
├── PYAS_Version.py # 版本元数据,用于打包与更新
|
||||
└── ... # 其他补充文件夹与文件
|
||||
```
|
||||
|
||||
## 官方网页
|
||||
## 代码架构
|
||||
|
||||
https://pyantivirus.wixsite.com/pyas
|
||||
PYAS 杀毒软件完整代码流程架构图。
|
||||
|
||||
https://github.com/87owo/PYAS
|
||||
<img width="23000" height="6000" alt="PYAS_Architecture" src="https://github.com/user-attachments/assets/3a7ce637-c2b3-4719-9658-07f286cfa879" />
|
||||
|
||||
## 微软运行库
|
||||
## 安装版本
|
||||
|
||||
https://github.com/87owo/Microsoft_Runtime/releases
|
||||
打包发行版下载:https://github.com/87owo/PYAS/releases
|
||||
|
||||
## 开源协议
|
||||
## 系统需求
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.md
|
||||
| 配置要求 | 权限 | 系统版本 | 处理器 | 可用内存空间 | 可用存储空间 |
|
||||
| ------- | ----- | ------ | ----- | ------------ | ---------- |
|
||||
| 最低配置 | 管理员 | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
|
||||
| 推荐配置 | 管理员 | >= Windows 10 (21H2) | 3 GHz | 300MB | 200MB |
|
||||
|
||||
## 支援系统
|
||||
## 官方网站
|
||||
|
||||
Windows 10, 11 (64-bit), 内存 500MB, 存储 200MB 或更高
|
||||
官方网站 : https://pyantivirus.wixsite.com/pyas
|
||||
|
||||
其他系统或版本可能会遇到软体功能故障或程序崩溃
|
||||
开源网站 : https://github.com/87owo/PYAS
|
||||
|
||||
## 特别感谢
|
||||
## 授权条款
|
||||
|
||||
Wix, mtkiao129, 0sha0, AV-T Team of LisectGroup
|
||||
|
||||
Copyright© 2020~2025 PYAS Security By 87owo
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
+58
-28
@@ -1,8 +1,20 @@
|
||||
# PYAS (Python 防毒軟體)
|
||||
# PYAS
|
||||
|
||||

|
||||
使用 Python 和 C 開發的防毒軟體,並透過深度學習和行為監控來阻止威脅!
|
||||
|
||||
## 安裝要求
|
||||
<img width="2245" height="1477" alt="PYAS_UI" src="https://github.com/user-attachments/assets/87d40261-7655-49ad-a19c-1ffcca60584f" />
|
||||
|
||||
## 說明語言
|
||||
|
||||
English : https://github.com/87owo/PYAS/blob/main/README.md
|
||||
|
||||
繁體中文 : https://github.com/87owo/PYAS/blob/main/README_zh_TW.md
|
||||
|
||||
简体中文 : https://github.com/87owo/PYAS/blob/main/README_zh_CN.md
|
||||
|
||||
## 安裝需求
|
||||
|
||||
建議使用 Python 3.10。其他版本的 Python 可能需要不同的 pip 指令。
|
||||
|
||||
```
|
||||
pip install numpy==1.26.4
|
||||
@@ -17,45 +29,63 @@ pip install yara-python==4.5.4
|
||||
|
||||
## 檔案資訊
|
||||
|
||||
以下列出所有相關程式碼與文件的存放位置。
|
||||
|
||||
```
|
||||
PYAS/
|
||||
├── PYAS.py # 主程式,提供 PySide6 視覺化操作介面
|
||||
├── PYAS_Config.py # 設定檔處理與全域參數管理
|
||||
├── PYAS_Engine.py # 掃描引擎,整合 YARA 規則與 ONNX AI 模型
|
||||
├── PYAS_Interface.py # UI 介面定義與元件交互
|
||||
├── PYAS_Resource.py # 資源檔與靜態資源管理
|
||||
├── PYAS_Version.py # 程式版本與打包資訊
|
||||
├── Engine/
|
||||
│ ├── Models/ # AI 模型(ONNX 格式)存放目錄
|
||||
│ └── Rules/ # YARA 規則與網路規則存放目錄
|
||||
│ ├── Models/
|
||||
│ │ ├── convert.py # 將可執行檔或其他檔案轉換為圖片
|
||||
│ │ └── train.py # TensorFlow CNN 模型訓練完整程式碼
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ ├── rules.yar # YARA 病毒特徵碼規則匹配
|
||||
│ └── rules.ips # IP 網路位址規則匹配
|
||||
│
|
||||
├── Plugins/
|
||||
│ └── Filter/ # 驅動程式目錄
|
||||
│ └── PYAS_Driver.sys # Windows 驅動程式(系統保護用)
|
||||
└── ... # 其它輔助資料夾與檔案
|
||||
│ └── Filter/
|
||||
│ ├── DriverEntry.c # 驅動主入口與初始化邏輯
|
||||
│ ├── DriverEntry.h # 全域驅動定義、常數與函式
|
||||
│ ├── DriverPipe.c # 核心層與使用者層的管道日誌傳輸實作
|
||||
│ ├── ProtectBoot.c # 磁碟開機區寫入保護
|
||||
│ ├── ProtectImage.c # 映像載入監控與 shellcode 偵測
|
||||
│ ├── ProtectInject.c # 程序與執行緒控制,防止注入
|
||||
│ ├── ProtectReg.c # 登錄檔修改保護
|
||||
│ ├── ProtectRules.c # 白名單、封鎖清單與檔案/登錄匹配邏輯
|
||||
│ └── ProtectRules.h # 保護規則宣告
|
||||
│
|
||||
├── PYAS.py # 主應用程式入口點與 UI 到引擎的介面
|
||||
├── PYAS_Config.py # 組態載入、儲存與全域參數
|
||||
├── PYAS_Engine.py # 核心掃描引擎:YARA、IP、ONNX 模型執行
|
||||
├── PYAS_Interface.py # 使用者介面元件與事件處理
|
||||
├── PYAS_Resource.py # 靜態圖片與圖示資源管理
|
||||
├── PYAS_Version.py # 版本中繼資料,用於打包與更新
|
||||
└── ... # 其他補充資料夾與檔案
|
||||
```
|
||||
|
||||
## 官方網頁
|
||||
## 程式架構
|
||||
|
||||
https://pyantivirus.wixsite.com/pyas
|
||||
PYAS 防毒軟體完整程式流程架構圖。
|
||||
|
||||
https://github.com/87owo/PYAS
|
||||
<img width="23000" height="6000" alt="PYAS_Architecture" src="https://github.com/user-attachments/assets/3a7ce637-c2b3-4719-9658-07f286cfa879" />
|
||||
|
||||
## 微軟運行庫
|
||||
## 安裝版本
|
||||
|
||||
https://github.com/87owo/Microsoft_Runtime/releases
|
||||
打包發行版下載 : https://github.com/87owo/PYAS/releases
|
||||
|
||||
## 開源協議
|
||||
## 系統需求
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.md
|
||||
| 配置要求 | 權限 | 系統版本 | 處理器 | 可用記憶體空間 | 可用儲存空間 |
|
||||
| ------- | ---- | ------- | ------ | ------------ | ----------- |
|
||||
| 最低配置 | 管理員 | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
|
||||
| 推薦配置 | 管理員 | >= Windows 10 (21H2) | 3 GHz | 300MB | 200MB |
|
||||
|
||||
## 支援系統
|
||||
## 官方網站
|
||||
|
||||
Windows 10, 11 (64-bit),記憶體 500MB, 磁碟容量 200MB 或更高
|
||||
官方網站 : https://pyantivirus.wixsite.com/pyas
|
||||
|
||||
其他系統或版本可能會遇到軟體功能故障或程序崩潰
|
||||
開源網站 : https://github.com/87owo/PYAS
|
||||
|
||||
## 特別感謝
|
||||
## 授權條款
|
||||
|
||||
Wix, mtkiao129, 0sha0, AV-T Team of LisectGroup
|
||||
|
||||
Copyright© 2020~2025 PYAS Security By 87owo
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
Reference in new issue
Block a user