Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e538b837d5 | ||
|
|
7b995e1223 | ||
|
|
c7826b58a3 | ||
|
|
5786dbdb75 | ||
|
|
0f42c19ec7 | ||
|
|
87d44a64f3 | ||
|
|
77dd4ace33 | ||
|
|
0907d71957 | ||
|
|
e7a30646e3 | ||
|
|
98d21b3391 | ||
|
|
35d9203b9c | ||
|
|
e6e384a7f7 | ||
|
|
7d954c14ae | ||
|
|
41388962e2 | ||
|
|
6d51f6b6a9 | ||
|
|
c7af9a3702 | ||
|
|
da7c61202c | ||
|
|
c24e872a33 | ||
|
|
d0809ec313 | ||
|
|
c1a796da38 | ||
|
|
98c27800e0 | ||
|
|
7a18eadd13 | ||
|
|
b07adb7b30 | ||
|
|
4d7c3cce4d | ||
|
|
4ff0ab9cd8 |
No files matched your search
+56
-56
@@ -1,73 +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):
|
||||
def preprocess_image(file_data, size, channels=1):
|
||||
width, height = size
|
||||
wah = int(numpy.ceil(numpy.sqrt(len(file_data) / channels)))
|
||||
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
|
||||
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
|
||||
img[:len(file_data)] = arr
|
||||
|
||||
if channels == 1:
|
||||
image = Image.fromarray(img.reshape((wah, wah)), 'L')
|
||||
else:
|
||||
image = Image.fromarray(img.reshape((wah, wah, channels)), 'RGB')
|
||||
return image.resize((width, height), Image.Resampling.NEAREST)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def is_text_file(content, sample_size=1024):
|
||||
raw = content[:sample_size]
|
||||
if not raw:
|
||||
return False
|
||||
text_char = set(range(32, 127)) | {9, 10, 13}
|
||||
nontext = sum(b not in text_char for b in raw)
|
||||
return nontext / len(raw) < 0.15
|
||||
|
||||
def get_type(file_path):
|
||||
suffix = {
|
||||
".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys",
|
||||
".bat", ".cmd", ".js", ".php", ".ps1", ".vbs", ".wsf"}
|
||||
|
||||
match_data = {}
|
||||
if not os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
return match_data
|
||||
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"]
|
||||
with pefile.PE(file_path, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
if section.Characteristics & 0x00000020:
|
||||
match_data[name] = section.get_data()
|
||||
except pefile.PEFormatError:
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
if is_text_file(file_content):
|
||||
match_data[os.path.splitext(file_path)[-1].lower()] = file_content
|
||||
return match_data
|
||||
|
||||
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):
|
||||
def 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}")
|
||||
for name, data in get_type(file_path).items():
|
||||
print(name, 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)
|
||||
image = preprocess_image(data, (224, 224))
|
||||
image.save(os.path.join(output_path_dir, f"{name}_{file}.png"))
|
||||
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)
|
||||
file_to_images(input_directory, output_directory)
|
||||
input('Conversion Complete')
|
||||
@@ -1,5 +1,7 @@
|
||||
import os, hashlib
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def calc_sha256(path):
|
||||
with open(path, "rb") as f:
|
||||
return hashlib.sha256(f.read()).digest()
|
||||
@@ -25,5 +27,8 @@ def dedup_realtime(root):
|
||||
seen.add(h)
|
||||
print(f"deleted {deleted}")
|
||||
|
||||
####################################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
dedup_realtime(r".\Image_File")
|
||||
input('Deduplicate Complete')
|
||||
+54
-34
@@ -3,9 +3,13 @@ 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))])):
|
||||
@@ -18,50 +22,62 @@ def get_file_list(directory):
|
||||
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"):
|
||||
####################################################################################################
|
||||
|
||||
def parse_fn(filename, label, image_size, channels, num_classes):
|
||||
img = tf.io.read_file(filename)
|
||||
img = tf.image.decode_image(img, channels=channels, expand_animations=False)
|
||||
img = tf.cond(
|
||||
tf.reduce_all(tf.equal(tf.shape(img)[:2], image_size)),
|
||||
lambda: tf.cast(img, tf.float32),
|
||||
lambda: tf.image.resize(img, image_size))
|
||||
img = tf.cast(img, tf.float32) / 255.0
|
||||
label = tf.one_hot(label, depth=num_classes)
|
||||
return img, label
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=128, color_mode="grayscale"):
|
||||
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)
|
||||
file_paths, labels, class_indices = get_file_list(data_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]
|
||||
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.map(lambda f, l: parse_fn(f, l, image_size, channels, num_classes), 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.map(lambda f, l: parse_fn(f, l, image_size, channels, num_classes), 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 multi_conv(x, strides):
|
||||
convs = []
|
||||
for k in [1, 3, 5, 7]:
|
||||
c = layers.DepthwiseConv2D(kernel_size=k, strides=strides, padding='same', activation='gelu')(x)
|
||||
convs.append(c)
|
||||
x = layers.Concatenate()(convs)
|
||||
x = layers.BatchNormalization()(x)
|
||||
return layers.Activation('gelu')(x)
|
||||
|
||||
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)
|
||||
@@ -72,18 +88,20 @@ def se_block(x, reduction):
|
||||
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 = multi_conv(x, strides=2)
|
||||
x = se_block(x, reduction=2)
|
||||
x = layers.GlobalAveragePooling2D()(x)
|
||||
x = layers.GlobalMaxPooling2D()(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)
|
||||
return 1e-4 * (0.95 ** epoch)
|
||||
|
||||
class CustomModelCheckpoint(callbacks.Callback):
|
||||
def on_epoch_end(self, epoch, logs=False):
|
||||
@@ -93,7 +111,9 @@ class CustomModelCheckpoint(callbacks.Callback):
|
||||
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')
|
||||
####################################################################################################
|
||||
|
||||
train_ds, val_ds = load_dataset(r'.\Image_File')
|
||||
for imgs, _ in train_ds.take(1):
|
||||
train_ds.image_shape = imgs.shape[1:]
|
||||
break
|
||||
@@ -109,4 +129,4 @@ with strategy.scope():
|
||||
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.")
|
||||
input('Training Complete')
|
||||
+14
-19
@@ -1,27 +1,22 @@
|
||||
PYAS Security Software Development License Agreement
|
||||
Last updated: 2025-08-15
|
||||
87owo software and source code Development License Agreement
|
||||
Last updated: 2025-09-09
|
||||
|
||||
1. License & Ownership
|
||||
1. License and 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.
|
||||
2. Use and Prohibitions
|
||||
– Reverse engineering, removal of licenses, or use for illegal purposes is prohibited.
|
||||
– Personal internal use or modification of the source code is permitted.
|
||||
– Redistribution, etc. requires prior authorization from the original author.
|
||||
|
||||
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.
|
||||
3. Updates and Terms
|
||||
– New versions and updates may include patches, enhancements, or modifications.
|
||||
– Continued use constitutes your acceptance of these features or license.
|
||||
|
||||
4. Warranty and Liability
|
||||
– This software or code is provided "as is" without warranty of any kind.
|
||||
– In no event will we be liable for any direct or indirect damages.
|
||||
|
||||
© 2020–2025 87owo (PYAS Security). All rights reserved.
|
||||
@@ -56,6 +56,21 @@ class SERVICE_STATUS(ctypes.Structure):
|
||||
("dwCheckPoint", ctypes.wintypes.DWORD),
|
||||
("dwWaitHint", ctypes.wintypes.DWORD),]
|
||||
|
||||
class PROCESS_BASIC_INFORMATION(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Reserved1", ctypes.wintypes.LPVOID),
|
||||
("PebBaseAddress", ctypes.wintypes.LPVOID),
|
||||
("Reserved2", ctypes.wintypes.LPVOID * 2),
|
||||
("UniqueProcessId", ctypes.wintypes.LPVOID),
|
||||
("Reserved3", ctypes.wintypes.LPVOID)
|
||||
]
|
||||
|
||||
class UNICODE_STRING(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Length", ctypes.wintypes.USHORT),
|
||||
("MaximumLength", ctypes.wintypes.USHORT),
|
||||
("Buffer", ctypes.c_void_p)]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class MainWindow_Controller(QMainWindow):
|
||||
@@ -114,7 +129,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
"traditional_switch", "simplified_switch", "english_switch",
|
||||
]
|
||||
self.pyas_default = {
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6",
|
||||
"product": "00000-00000-00000-00000-00000",
|
||||
"language": "english_switch",
|
||||
"theme": "white_switch",
|
||||
@@ -144,6 +159,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
"PROC_BLOCK": "進程行為攔截",
|
||||
"REMOTE_BLOCK": "遠控行為攔截",
|
||||
"SCREEN_BLOCK": "監控行為攔截",
|
||||
"KILL_ATTEMPT": "終止行為攔截",
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
@@ -171,7 +187,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
####################################################################################################
|
||||
|
||||
def init_windll(self):
|
||||
for name in ["ntdll", "Psapi", "user32", "kernel32", "advapi32", "iphlpapi"]:
|
||||
for name in ["ntdll", "Psapi", "user32", "kernel32", "advapi32", "iphlpapi", "shell32"]:
|
||||
try:
|
||||
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
|
||||
except Exception as e:
|
||||
@@ -203,6 +219,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.advapi32.CloseServiceHandle.argtypes = [ctypes.wintypes.HANDLE]
|
||||
self.advapi32.CloseServiceHandle.restype = ctypes.wintypes.BOOL
|
||||
|
||||
self.ntdll.NtQueryInformationProcess.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.ULONG, ctypes.c_void_p, ctypes.wintypes.ULONG, ctypes.POINTER(ctypes.wintypes.ULONG)]
|
||||
self.ntdll.NtQueryInformationProcess.restype = ctypes.wintypes.ULONG
|
||||
|
||||
self.shell32.CommandLineToArgvW.argtypes = [ctypes.wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_int)]
|
||||
self.shell32.CommandLineToArgvW.restype = ctypes.POINTER(ctypes.wintypes.LPWSTR)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def save_config(self, file_path, config):
|
||||
@@ -694,8 +716,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
@Slot()
|
||||
def slot_scan_reset(self):
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
self.widgets["virus_list"].clear()
|
||||
self.widgets["solve_button"].hide()
|
||||
self.widgets["stop_button"].show()
|
||||
self.widgets["method_button"].hide()
|
||||
self.widgets["method_window"].hide()
|
||||
@@ -846,7 +869,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.send_message(e, "warn", False)
|
||||
QApplication.processEvents()
|
||||
|
||||
count = virus_list.count()
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
elapsed = int(time.time() - start_ts)
|
||||
result = (
|
||||
@@ -881,6 +904,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
copy_action = menu.addAction(self.trans(lang, "複製路徑"))
|
||||
del_action = menu.addAction(self.trans(lang, "刪除檔案"))
|
||||
white_action = menu.addAction(self.trans(lang, "增加白名單"))
|
||||
quarantine_action = menu.addAction(self.trans(lang, "增加隔離區"))
|
||||
act = menu.exec(widget.viewport().mapToGlobal(pos))
|
||||
|
||||
text = item.text()
|
||||
@@ -901,14 +925,31 @@ class MainWindow_Controller(QMainWindow):
|
||||
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)
|
||||
try:
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
except:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
elif act == quarantine_action:
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要增加到隔離區嗎?", "quest"):
|
||||
try:
|
||||
n = self.manage_named_list("quarantine", [file_path], action="add", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info", True)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
except:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -990,6 +1031,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
copy_action = menu.addAction(self.trans(lang, "複製路徑"))
|
||||
kill_action = menu.addAction(self.trans(lang, "結束進程"))
|
||||
white_action = menu.addAction(self.trans(lang, "增加白名單"))
|
||||
quarantine_action = menu.addAction(self.trans(lang, "增加隔離區"))
|
||||
act = menu.exec(widget.viewport().mapToGlobal(pos))
|
||||
|
||||
pid = self.proc_pid_map[idx.row()]
|
||||
@@ -1002,10 +1044,22 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.kill_process(pid)
|
||||
|
||||
elif act == white_action:
|
||||
file_path = self.norm_path(path)
|
||||
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)
|
||||
try:
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
except:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
elif act == quarantine_action:
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要增加到隔離區嗎?", "quest"):
|
||||
try:
|
||||
n = self.manage_named_list("quarantine", [file_path], action="add", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info", True)
|
||||
except:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
def kill_process(self, pid):
|
||||
try:
|
||||
@@ -1377,22 +1431,21 @@ class MainWindow_Controller(QMainWindow):
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
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")
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
|
||||
def whitelist_button_2(self):
|
||||
self.config_list("white_list")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
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")
|
||||
self.send_message(f"成功移除白名單,共 {n} 個檔案", "info", True)
|
||||
|
||||
def quarantine_button(self):
|
||||
self.config_list("quarantine")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
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")
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info", True)
|
||||
|
||||
def quarantine_button_2(self):
|
||||
self.config_list("quarantine")
|
||||
@@ -1400,7 +1453,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
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")
|
||||
self.send_message(f"成功移除隔離區,共 {n} 個檔案", "info", True)
|
||||
|
||||
def add_to_quarantine(self, files):
|
||||
self.config_list("quarantine")
|
||||
@@ -1420,36 +1473,36 @@ 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, must_exist=True)
|
||||
norm_paths = self.norm_path(files or [], must_exist=True)
|
||||
if isinstance(norm_paths, str):
|
||||
norm_paths = [norm_paths] if norm_paths else []
|
||||
|
||||
n = 0
|
||||
if action == "add":
|
||||
for path in norm_paths:
|
||||
QApplication.processEvents()
|
||||
file_hash = self.calc_file_hash(path) if with_hash else ""
|
||||
if not any(self.norm_path(item["file"]) == path and (not with_hash or item.get("hash", "") == file_hash) for item in target_list):
|
||||
if not any(self.norm_path(item["file"]) == path and
|
||||
(not with_hash or item.get("hash", "") == file_hash)
|
||||
for item in self.config_list(list_key)):
|
||||
if lock_func:
|
||||
try:
|
||||
lock_func(path, True)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
target_list.append({"file": path, "hash": file_hash})
|
||||
self.config_list(list_key).append({"file": path, "hash": file_hash})
|
||||
n += 1
|
||||
|
||||
elif action == "remove":
|
||||
remove_list = [item for item in target_list if item["file"] in norm_paths]
|
||||
for item in remove_list:
|
||||
for item in [item for item in self.config_list(list_key)
|
||||
if self.norm_path(item["file"]) in norm_paths]:
|
||||
try:
|
||||
if lock_func:
|
||||
lock_func(item["file"], False)
|
||||
target_list.remove(item)
|
||||
self.config_list(list_key).remove(item)
|
||||
n += 1
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
if n:
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
return n
|
||||
@@ -1549,31 +1602,31 @@ class MainWindow_Controller(QMainWindow):
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
def get_process_list(self):
|
||||
exist_process, pe = set(), self.get_process_entry()
|
||||
hSnapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
if self.kernel32.Process32First(hSnapshot, ctypes.byref(pe)):
|
||||
while True:
|
||||
exist_process.add(pe.th32ProcessID)
|
||||
if not self.kernel32.Process32Next(hSnapshot, ctypes.byref(pe)):
|
||||
break
|
||||
self.kernel32.CloseHandle(hSnapshot)
|
||||
return exist_process
|
||||
|
||||
def handle_new_process(self, pid):
|
||||
try:
|
||||
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 not self.is_in_whitelist(file_path):
|
||||
self.suspend_process(h, True)
|
||||
state = self.scan_engine(file_path)
|
||||
if state:
|
||||
self.suspend_process(h, True)
|
||||
cmdline = self.get_process_cmdline(h)
|
||||
paths = []
|
||||
for arg in self.split_commandline(cmdline):
|
||||
paths.extend(self.extract_paths_from_arg(arg))
|
||||
|
||||
seen = set()
|
||||
paths = [p for p in paths if p and (p not in seen and not seen.add(p))]
|
||||
for p in paths:
|
||||
file_path = self.norm_path(self.device_path_to_drive(p)) if p else ""
|
||||
if not file_path:
|
||||
continue
|
||||
if self.is_in_whitelist(file_path):
|
||||
continue
|
||||
if self.scan_engine(file_path):
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.send_message(f"進程防護 | 靜態掃描攔截 | {pid} | {file_path} | None", "notify", True)
|
||||
self.suspend_process(h, False)
|
||||
|
||||
self.suspend_process(h, False)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
@@ -1592,6 +1645,19 @@ class MainWindow_Controller(QMainWindow):
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def get_process_list(self):
|
||||
exist_process, pe = set(), self.get_process_entry()
|
||||
hSnapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
if self.kernel32.Process32First(hSnapshot, ctypes.byref(pe)):
|
||||
while True:
|
||||
exist_process.add(pe.th32ProcessID)
|
||||
if not self.kernel32.Process32Next(hSnapshot, ctypes.byref(pe)):
|
||||
break
|
||||
self.kernel32.CloseHandle(hSnapshot)
|
||||
return exist_process
|
||||
|
||||
def get_process_file(self, h_process):
|
||||
buf = ctypes.create_unicode_buffer(1024)
|
||||
if self.psapi.GetProcessImageFileNameW(h_process, buf, 1024):
|
||||
@@ -1616,6 +1682,73 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.kernel32.CloseHandle(h)
|
||||
return name, file_path
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def get_process_cmdline(self, h):
|
||||
pbi = PROCESS_BASIC_INFORMATION()
|
||||
retlen = ctypes.wintypes.ULONG(0)
|
||||
status = self.ntdll.NtQueryInformationProcess(h, 0, ctypes.byref(pbi), ctypes.sizeof(pbi), ctypes.byref(retlen))
|
||||
if status != 0:
|
||||
return ""
|
||||
|
||||
pointer_size = ctypes.sizeof(ctypes.c_void_p)
|
||||
peb_address = int(pbi.PebBaseAddress) if pbi.PebBaseAddress else 0
|
||||
read_buf = (ctypes.c_ubyte * pointer_size)()
|
||||
lpNumberOfBytesRead = ctypes.c_size_t(0)
|
||||
offset_process_parameters = 0x20 if pointer_size == 8 else 0x10
|
||||
addr_pp = peb_address + offset_process_parameters
|
||||
if not addr_pp:
|
||||
return ""
|
||||
if not self.kernel32.ReadProcessMemory(h, ctypes.c_void_p(addr_pp), read_buf, pointer_size, ctypes.byref(lpNumberOfBytesRead)):
|
||||
return ""
|
||||
|
||||
proc_params_address = ctypes.c_void_p.from_buffer_copy(read_buf).value
|
||||
if not proc_params_address:
|
||||
return ""
|
||||
|
||||
offset_command_line = 0x70 if pointer_size == 8 else 0x40
|
||||
us = UNICODE_STRING()
|
||||
addr_cmd = int(proc_params_address) + offset_command_line
|
||||
if not self.kernel32.ReadProcessMemory(h, ctypes.c_void_p(addr_cmd), ctypes.byref(us), ctypes.sizeof(us), ctypes.byref(lpNumberOfBytesRead)):
|
||||
return ""
|
||||
if not us.Buffer or us.Length == 0:
|
||||
return ""
|
||||
|
||||
buf_len = int(us.Length // 2)
|
||||
buf = (ctypes.c_wchar * buf_len)()
|
||||
if not self.kernel32.ReadProcessMemory(h, ctypes.c_void_p(int(us.Buffer)), buf, us.Length, ctypes.byref(lpNumberOfBytesRead)):
|
||||
return ""
|
||||
return "".join(buf)
|
||||
|
||||
def split_commandline(self, cmdline):
|
||||
if not cmdline:
|
||||
return []
|
||||
|
||||
argc = ctypes.c_int(0)
|
||||
argv = self.shell32.CommandLineToArgvW(cmdline, ctypes.byref(argc))
|
||||
if not argv:
|
||||
return []
|
||||
|
||||
args = [argv[i] for i in range(argc.value)]
|
||||
self.kernel32.LocalFree(ctypes.cast(argv, ctypes.c_void_p))
|
||||
return args
|
||||
|
||||
def extract_paths_from_arg(self, arg):
|
||||
patterns = [
|
||||
r'([A-Za-z]:\\[^"\']+)',
|
||||
r'(\\\\[^"\']+)',
|
||||
r'(\.\\[^"\']+)',
|
||||
r'(\./[^"\']+)',
|
||||
r'([A-Za-z]:/[^"\']+)',
|
||||
r'([^\s]*\\[^\s]+)'
|
||||
]
|
||||
found = []
|
||||
for p in patterns:
|
||||
for m in re.finditer(p, arg):
|
||||
val = m.group(1).strip('"').strip("'")
|
||||
found.append(val)
|
||||
return list(dict.fromkeys(found))
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def protect_file_thread(self):
|
||||
|
||||
+60
-65
@@ -1,6 +1,7 @@
|
||||
import os, yara, numpy, onnxruntime
|
||||
import os, yara, numpy, base64, onnxruntime
|
||||
import ctypes, ctypes.wintypes, pefile
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -52,7 +53,7 @@ class sign_scanner:
|
||||
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:
|
||||
with pefile.PE(file_path, 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
|
||||
|
||||
@@ -107,14 +108,10 @@ class rule_scanner:
|
||||
|
||||
class model_scanner:
|
||||
def __init__(self):
|
||||
self.models = {}
|
||||
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.model = None
|
||||
self.suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".bat", ".cmd", ".js", ".php", ".ps1", ".vbs", ".wsf"}
|
||||
self.labels = ["Pefile/White", "Script/White", "Pefile/General", "Script/General"]
|
||||
self.detect_set = {"Pefile/General", "Script/General"}
|
||||
self.resize = (224, 224)
|
||||
|
||||
def load_path(self, path):
|
||||
@@ -125,59 +122,62 @@ class model_scanner:
|
||||
def load_file(self, file):
|
||||
if file.lower().endswith('.onnx'):
|
||||
try:
|
||||
self.models[file] = onnxruntime.InferenceSession(file)
|
||||
self.model = onnxruntime.InferenceSession(file)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def model_scan(self, file_path):
|
||||
try:
|
||||
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)
|
||||
def model_scan(self, file_path, full_output=False):
|
||||
if not self.model or not file_path.exists():
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
file_sections = self.get_type(file_path)
|
||||
if not file_sections:
|
||||
return False, False
|
||||
sections = self.extract_sections(file_path)
|
||||
if not sections:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
images = [self.preprocess_image(data, self.resize) for data in sections.values()]
|
||||
if not images:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
batch_images = [self.preprocess_image(data, self.resize, channels) for data in file_sections.values()]
|
||||
arr = numpy.stack([numpy.asarray(img).astype('float32') / 255.0 for img in batch_images])
|
||||
if arr.ndim == 3:
|
||||
arr = arr[..., None]
|
||||
if shape[-1] in (1, 3):
|
||||
model_input = arr
|
||||
elif shape[1] in (1, 3):
|
||||
model_input = arr.transpose(0, 3, 1, 2)
|
||||
else:
|
||||
return False, False
|
||||
arr = numpy.stack([numpy.asarray(img).astype('float32') / 255.0 for img in images])[:, :, :, None]
|
||||
input_shape = self.model.get_inputs()[0].shape
|
||||
if len(input_shape) == 4 and input_shape[1] in (1, 3):
|
||||
arr = arr.transpose(0, 3, 1, 2)
|
||||
|
||||
input_name = model.get_inputs()[0].name
|
||||
predictions = model.run(None, {input_name: model_input})[0]
|
||||
input_name = self.model.get_inputs()[0].name
|
||||
preds = self.model.run(None, {input_name: arr})[0]
|
||||
exp = numpy.exp(preds - numpy.max(preds, axis=1, keepdims=True))
|
||||
probs = exp / numpy.sum(exp, axis=1, keepdims=True)
|
||||
|
||||
label_scores = {label: [] for label in self.labels}
|
||||
for pre_answers in predictions:
|
||||
for k, score in enumerate(pre_answers):
|
||||
label_scores[self.labels[k]].append(score)
|
||||
label_percentage = {label: (sum(scores)/len(scores))*100 for label, scores in label_scores.items() if scores}
|
||||
if not label_percentage:
|
||||
return False, False
|
||||
best_label, best_conf = max(label_percentage.items(), key=lambda x: x[1])
|
||||
if best_label in self.detect:
|
||||
return best_label, int(best_conf)
|
||||
return False, False
|
||||
except Exception:
|
||||
return False, False
|
||||
max_overall = max(numpy.max(row) for row in probs)
|
||||
|
||||
def preprocess_image(self, file_data, size, channels=1):
|
||||
results = []
|
||||
for name, pred, img in zip(sections.keys(), probs, images):
|
||||
idx = int(numpy.argmax(pred))
|
||||
label = self.labels[idx]
|
||||
conf = round((pred[idx] / max_overall) * 100, 2)
|
||||
if full_output:
|
||||
results.append((name, label, conf, self.pil_to_base64(img)))
|
||||
elif label in self.detect_set:
|
||||
return label, int(conf)
|
||||
|
||||
if full_output:
|
||||
malicious_count = sum(1 for _, lbl, _, _ in results if lbl in self.detect_set)
|
||||
total = len(results)
|
||||
status = f"{malicious_count}/{total}"
|
||||
return results, str(file_path), status
|
||||
return False, False
|
||||
|
||||
def pil_to_base64(self, img):
|
||||
buf = BytesIO()
|
||||
img.save(buf, format='PNG')
|
||||
return base64.b64encode(buf.getvalue()).decode()
|
||||
|
||||
def preprocess_image(self, data, size, channels=1):
|
||||
width, height = size
|
||||
wah = int(numpy.ceil(numpy.sqrt(len(file_data) / channels)))
|
||||
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
|
||||
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
|
||||
img[:len(file_data)] = arr
|
||||
|
||||
if channels == 1:
|
||||
image = Image.fromarray(img.reshape((wah, wah)), 'L')
|
||||
else:
|
||||
image = Image.fromarray(img.reshape((wah, wah, channels)), 'RGB')
|
||||
wah = int(numpy.ceil(numpy.sqrt(len(data) / channels)))
|
||||
arr = numpy.frombuffer(data, dtype=numpy.uint8)
|
||||
imgbuf = numpy.zeros(wah * wah * channels, dtype=numpy.uint8)
|
||||
imgbuf[:len(arr)] = arr
|
||||
image = Image.fromarray(imgbuf.reshape((wah, wah)), 'L')
|
||||
return image.resize((width, height), Image.Resampling.NEAREST)
|
||||
|
||||
def is_text_file(self, content, sample_size=1024):
|
||||
@@ -188,23 +188,18 @@ class model_scanner:
|
||||
nontext = sum(b not in text_char for b in raw)
|
||||
return nontext / len(raw) < 0.15
|
||||
|
||||
def get_type(self, file_path):
|
||||
def extract_sections(self, file_path):
|
||||
match_data = {}
|
||||
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:
|
||||
sec = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']]
|
||||
if bool(sec.VirtualAddress and sec.Size):
|
||||
return {}
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
if section.Characteristics & 0x00000020:
|
||||
match_data[name] = section.get_data()
|
||||
|
||||
except pefile.PEFormatError:
|
||||
except Exception:
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
if self.is_text_file(file_content):
|
||||
match_data[os.path.splitext(file_path)[-1].lower()] = file_content
|
||||
content = f.read()
|
||||
ext = file_path.suffix.lower()
|
||||
if ext in self.suffix or self.is_text_file(content):
|
||||
match_data[ext] = content
|
||||
return match_data
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.3.5',
|
||||
version='3.3.6',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -53,7 +53,7 @@ static NTSTATUS CombinedDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
return IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
}
|
||||
return FileProtectDispatch(DeviceObject, Irp);
|
||||
return BootProtectDispatch(DeviceObject, Irp);
|
||||
}
|
||||
|
||||
static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
@@ -61,7 +61,7 @@ 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;
|
||||
|
||||
@@ -94,7 +94,7 @@ static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
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) {
|
||||
@@ -242,18 +242,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
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);
|
||||
}
|
||||
}
|
||||
InitProcessProtect();
|
||||
AttachFsFilters(DriverObject);
|
||||
AttachDiskFilters(DriverObject);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
#include <ntstrsafe.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <fltKernel.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
|
||||
@@ -32,7 +32,7 @@
|
||||
#define PROCESS_CREATE_PROCESS 0x0080
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA 0x0100
|
||||
#define PROCESS_SET_QUota 0x0100
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION 0x0200
|
||||
@@ -73,22 +73,23 @@
|
||||
#ifndef UINT
|
||||
typedef unsigned int UINT;
|
||||
#endif
|
||||
#define MAX_MBR_TARGETS 64
|
||||
|
||||
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;
|
||||
extern PDRIVER_OBJECT g_DriverObject;
|
||||
extern PFLT_FILTER g_FilterHandle;
|
||||
|
||||
NTSYSAPI PCSTR NTAPI PsGetProcessImageFileName(PEPROCESS Process);
|
||||
|
||||
typedef PVOID(NTAPI* _pyas_ExAllocatePool2)(ULONG, SIZE_T, ULONG);
|
||||
typedef VOID(NTAPI* _pyas_ExFreePool2)(PVOID, ULONG, PVOID, ULONG);
|
||||
@@ -147,6 +148,9 @@ VOID UninitInjectProtect(VOID);
|
||||
VOID UninitRemoteProtect(VOID);
|
||||
VOID UninitScreenProtect(VOID);
|
||||
VOID UninitProcessProtect(VOID);
|
||||
VOID UninitFileProtect(VOID);
|
||||
|
||||
VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2);
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(
|
||||
_In_ HANDLE ProcessHandle,
|
||||
@@ -167,12 +171,17 @@ NTSTATUS ObReferenceObjectByName(
|
||||
OUT PVOID* Object
|
||||
);
|
||||
|
||||
NTSTATUS FileProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
|
||||
NTSTATUS BootProtectDispatch(
|
||||
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);
|
||||
NTSTATUS InitFileProtect(VOID);
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
extern PDEVICE_OBJECT g_ControlDeviceObject;
|
||||
@@ -60,3 +61,20 @@ VOID SendPipeLog(PCSTR msg, SIZE_T len)
|
||||
InterlockedIncrement(&g_LogWorkCount);
|
||||
IoQueueWorkItem(ctx->Item, PipeLogWork, DelayedWorkQueue, ctx);
|
||||
}
|
||||
|
||||
VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
|
||||
if (s1)
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
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 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 BootProtectDispatch(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;
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = eb;
|
||||
exe.MaximumLength = sizeof(eb);
|
||||
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
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 (QueryDevName(lower, &dev)) {
|
||||
if (MatchBlockFile(&dev))
|
||||
hit = TRUE;
|
||||
}
|
||||
else {
|
||||
hit = TRUE;
|
||||
}
|
||||
if (!hit && s->MajorFunction == IRP_MJ_WRITE && s->FileObject && IsDASDRoot(s->FileObject))
|
||||
hit = TRUE;
|
||||
|
||||
if (hit) {
|
||||
blocked = TRUE;
|
||||
if (dev.Length == 0)
|
||||
QueryDevName(lower, &dev);
|
||||
LogAnsi3("BOOT_BLOCK", upid, &exe, dev.Length ? &dev : &exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
+249
-293
@@ -1,323 +1,279 @@
|
||||
#include <ntifs.h>
|
||||
#include <fltKernel.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;
|
||||
#pragma comment(lib, "FltMgr.lib")
|
||||
|
||||
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;
|
||||
}
|
||||
PFLT_FILTER g_FilterHandle = NULL;
|
||||
PDRIVER_OBJECT g_DriverObject = NULL;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
|
||||
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;
|
||||
}
|
||||
typedef struct _MINIFILTER_DATA {
|
||||
PFLT_FILTER Filter;
|
||||
} MINIFILTER_DATA, * PMINIFILTER_DATA;
|
||||
|
||||
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || exe->MaximumLength == 0)
|
||||
if (!exe || !exe->Buffer || !exe->MaximumLength)
|
||||
return;
|
||||
|
||||
|
||||
exe->Length = 0;
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, exe))
|
||||
return;
|
||||
}
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
PEPROCESS eproc = NULL;
|
||||
NTSTATUS s = PsLookupProcessByProcessId(pid, &eproc);
|
||||
if (NT_SUCCESS(s) && eproc) {
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
|
||||
exe->Buffer[i] = (WCHAR)n[i];
|
||||
exe->Buffer[l] = 0;
|
||||
exe->Length = (USHORT)(l * sizeof(WCHAR));
|
||||
ObDereferenceObject(eproc);
|
||||
return;
|
||||
}
|
||||
PCSTR n = NULL;
|
||||
PEPROCESS cur = PsGetCurrentProcess();
|
||||
if (cur)
|
||||
n = PsGetProcessImageFileName(cur);
|
||||
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)
|
||||
static BOOLEAN PreOpCheck(ULONG* outUpid)
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (g_Unloading)
|
||||
return FALSE;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return FALSE;
|
||||
|
||||
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;
|
||||
return FALSE;
|
||||
}
|
||||
*outUpid = upid;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static VOID PostOpRelease()
|
||||
{
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
static BOOLEAN IsExeWhitelisted(ULONG pid)
|
||||
{
|
||||
WCHAR buffer[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = buffer;
|
||||
exe.MaximumLength = sizeof(buffer);
|
||||
GetExeNameForLog((HANDLE)pid, &exe);
|
||||
return IsWhitelist(&exe);
|
||||
}
|
||||
|
||||
static BOOLEAN IsDeleteOrRename(PFLT_CALLBACK_DATA Data)
|
||||
{
|
||||
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
|
||||
if (infoClass == FileDispositionInformation) {
|
||||
PFILE_DISPOSITION_INFORMATION di = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
|
||||
return di && di->DeleteFile;
|
||||
}
|
||||
if (infoClass == FileDispositionInformationEx) {
|
||||
PFILE_DISPOSITION_INFORMATION_EX dx = (PFILE_DISPOSITION_INFORMATION_EX)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
|
||||
return dx && (dx->Flags & FILE_DISPOSITION_DELETE);
|
||||
}
|
||||
if (infoClass == FileRenameInformation || infoClass == FileRenameInformationEx)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static FLT_PREOP_CALLBACK_STATUS CheckAndBlockFile(PFLT_CALLBACK_DATA Data, ULONG upid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (KeGetCurrentIrql() > APC_LEVEL)
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
|
||||
BOOLEAN blocked = hitFile || hitRansom;
|
||||
|
||||
if (blocked) {
|
||||
LogAnsi3(hitFile && !hitRansom ? "FILE_BLOCK" : "RANSOM_BLOCK", upid, exe, &nameInfo->Name);
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
static FLT_PREOP_CALLBACK_STATUS FilePreOp(
|
||||
_Inout_ PFLT_CALLBACK_DATA Data,
|
||||
_In_ PCFLT_RELATED_OBJECTS FltObjects,
|
||||
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
ULONG upid;
|
||||
if (!PreOpCheck(&upid))
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
BOOLEAN checkFile = (Data->Iopb->MajorFunction == IRP_MJ_CREATE || Data->Iopb->MajorFunction == IRP_MJ_WRITE);
|
||||
BOOLEAN checkSetInfo = (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION && IsDeleteOrRename(Data));
|
||||
if (!checkFile && !checkSetInfo) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
WCHAR exe_buffer[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = exe_buffer;
|
||||
exe.MaximumLength = sizeof(exe_buffer);
|
||||
GetExeNameForLog(PsGetCurrentProcessId(), &exe);
|
||||
|
||||
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
|
||||
BOOLEAN isExcept = IsWhitelistExcept(&exe);
|
||||
|
||||
if ((hitFile || hitRansom) && !isExcept) {
|
||||
LogAnsi3(hitFile && !hitRansom ? "FILE_BLOCK" : "RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
if (isExcept && hitRansom) {
|
||||
LogAnsi3("RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
if (IsWhitelist(&nameInfo->Name)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
static NTSTATUS FilterUnloadCallback(FLT_FILTER_UNLOAD_FLAGS Flags)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
g_Unloading = TRUE;
|
||||
ExWaitForRundownProtectionRelease(&g_Rundown);
|
||||
if (g_FilterHandle != NULL) {
|
||||
FltUnregisterFilter(g_FilterHandle);
|
||||
g_FilterHandle = NULL;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS FilterInstanceSetup(
|
||||
_In_ PCFLT_RELATED_OBJECTS FltObjects,
|
||||
_In_ FLT_INSTANCE_SETUP_FLAGS Flags,
|
||||
_In_ DEVICE_TYPE VolumeDeviceType,
|
||||
_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID FilterInstanceTeardownStart(
|
||||
_In_ PCFLT_RELATED_OBJECTS FltObjects,
|
||||
_In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static VOID FilterInstanceTeardownComplete(
|
||||
_In_ PCFLT_RELATED_OBJECTS FltObjects,
|
||||
_In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
const FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
{ IRP_MJ_CREATE, 0, FilePreOp, NULL },
|
||||
{ IRP_MJ_WRITE, 0, FilePreOp, NULL },
|
||||
{ IRP_MJ_SET_INFORMATION, 0, FilePreOp, NULL },
|
||||
{ IRP_MJ_OPERATION_END }
|
||||
};
|
||||
|
||||
const FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION, 0, NULL, Callbacks, FilterUnloadCallback, FilterInstanceSetup, NULL,
|
||||
FilterInstanceTeardownStart, FilterInstanceTeardownComplete, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
NTSTATUS InitFileProtect(VOID)
|
||||
{
|
||||
ExInitializeRundownProtection(&g_Rundown);
|
||||
NTSTATUS status = FltRegisterFilter(g_DriverObject, &FilterRegistration, &g_FilterHandle);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
status = FltStartFiltering(g_FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltUnregisterFilter(g_FilterHandle);
|
||||
g_FilterHandle = NULL;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
VOID UninitFileProtect(VOID)
|
||||
{
|
||||
g_Unloading = TRUE;
|
||||
ExWaitForRundownProtectionRelease(&g_Rundown);
|
||||
if (g_FilterHandle != NULL) {
|
||||
FltUnregisterFilter(g_FilterHandle);
|
||||
g_FilterHandle = NULL;
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -49,19 +49,6 @@ static BOOLEAN WalkForShellcode(ULONG h)
|
||||
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);
|
||||
|
||||
@@ -6,19 +6,6 @@ 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);
|
||||
|
||||
+130
-156
@@ -3,11 +3,16 @@
|
||||
#include "ProtectRules.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static __forceinline BOOLEAN USOK(PUNICODE_STRING s)
|
||||
{
|
||||
return s && s->Buffer && s->Length;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -25,7 +30,7 @@ static BOOLEAN WildMatchN(const wchar_t* str, SIZE_T n, const wchar_t* pat)
|
||||
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;
|
||||
@@ -36,24 +41,26 @@ static BOOLEAN WildMatchN(const wchar_t* str, SIZE_T n, const wchar_t* pat)
|
||||
|
||||
static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
if (!USOK(s) || !list)
|
||||
return FALSE;
|
||||
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
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))
|
||||
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)
|
||||
SIZE_T pat_len = wcslen(pat);
|
||||
if (s_len >= pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0) {
|
||||
if (s_len == pat_len || s->Buffer[s_len - pat_len - 1] == L'\\') {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
if (s_len >= pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
@@ -61,52 +68,42 @@ static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
|
||||
|
||||
static wchar_t* MatchListGetRule(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
if (!USOK(s) || !list)
|
||||
return 0;
|
||||
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
|
||||
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)
|
||||
SIZE_T pat_len = wcslen(pat);
|
||||
if (s_len >= pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0) {
|
||||
if (s_len == pat_len || s->Buffer[s_len - pat_len - 1] == L'\\') {
|
||||
return pat;
|
||||
}
|
||||
}
|
||||
if (s_len >= pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0) {
|
||||
return pat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __forceinline BOOLEAN USOK(PUNICODE_STRING s)
|
||||
static BOOLEAN StrStrIW(const wchar_t* s, const wchar_t* sub)
|
||||
{
|
||||
return s && s->Buffer && s->Length;
|
||||
}
|
||||
|
||||
static BOOLEAN StrStrIW(const wchar_t* s, const wchar_t* sub)
|
||||
{
|
||||
if (!s || !sub)
|
||||
if (!s || !sub)
|
||||
return FALSE;
|
||||
|
||||
SIZE_T n = wcslen(s), m = wcslen(sub);
|
||||
if (m == 0 || n < m)
|
||||
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)
|
||||
if (_wcsnicmp(s + i, sub, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -116,66 +113,46 @@ 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;
|
||||
return n >= m && _wcsnicmp(s->Buffer + (n - m), name, m) == 0;
|
||||
}
|
||||
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
{
|
||||
if (USOK(key)) {
|
||||
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(key) && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
|
||||
return FALSE;
|
||||
|
||||
if (USOK(exe) && IsWhitelist(exe))
|
||||
return FALSE;
|
||||
|
||||
if (USOK(exe) && USOK(key)) {
|
||||
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 ((EndsWithNameUS(exe, L"services.exe") || EndsWithNameUS(exe, L"svchost.exe") || EndsWithNameUS(exe, L"sc.exe")) &&
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
|
||||
if ((EndsWithNameUS(exe, L"powershell.exe") || EndsWithNameUS(exe, L"pwsh.exe")) &&
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**")))
|
||||
return FALSE;
|
||||
|
||||
if ((EndsWithNameUS(exe, L"msiexec.exe") || EndsWithNameUS(exe, L"TrustedInstaller.exe")) &&
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**")))
|
||||
return FALSE;
|
||||
|
||||
if (EndsWithNameUS(exe, L"schtasks.exe") && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(key) && MatchBlockReg(key))
|
||||
return TRUE;
|
||||
|
||||
if (USOK(valueName)) {
|
||||
SIZE_T n = valueName->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; i < (SIZE_T)-1 && g_BlockReg[i]; ++i) {
|
||||
for (SIZE_T i = 0; 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)
|
||||
if (!wcschr(pat, L'\\') && !wcschr(pat, L'*') && !wcschr(pat, L'?') && _wcsnicmp(valueName->Buffer, pat, wcslen(pat)) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -185,29 +162,27 @@ BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
|
||||
{
|
||||
PEPROCESS process = NULL;
|
||||
NTSTATUS status = PsLookupProcessByProcessId(pid, &process);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FALSE;
|
||||
|
||||
PVOID buf = NULL;
|
||||
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;
|
||||
}
|
||||
NTSTATUS status;
|
||||
ULONG len = 0;
|
||||
|
||||
status = PsLookupProcessByProcessId(pid, &process);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FALSE;
|
||||
|
||||
status = ObOpenObjectByPointer(process, OBJ_KERNEL_HANDLE, NULL, PROCESS_QUERY_LIMITED_INFORMATION, *PsProcessType, KernelMode, &h);
|
||||
if (!NT_SUCCESS(status))
|
||||
goto cleanup;
|
||||
|
||||
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;
|
||||
}
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH || len == 0)
|
||||
goto cleanup;
|
||||
|
||||
buf = ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'iPgN');
|
||||
if (!buf)
|
||||
goto cleanup;
|
||||
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, buf, len, &len);
|
||||
if (NT_SUCCESS(status)) {
|
||||
PUNICODE_STRING image = (PUNICODE_STRING)buf;
|
||||
@@ -215,21 +190,28 @@ BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
|
||||
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;
|
||||
status = TRUE;
|
||||
}
|
||||
else {
|
||||
status = FALSE;
|
||||
}
|
||||
}
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
else {
|
||||
status = FALSE;
|
||||
}
|
||||
cleanup:
|
||||
if (buf)
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
if (h)
|
||||
ZwClose(h);
|
||||
if (process)
|
||||
ObDereferenceObject(process);
|
||||
return NT_SUCCESS(status);
|
||||
}
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s)
|
||||
{
|
||||
if (MatchList(s, g_WhitelistExcept))
|
||||
if (MatchList(s, g_WhitelistExcept))
|
||||
return FALSE;
|
||||
return MatchList(s, g_Whitelist);
|
||||
}
|
||||
@@ -239,7 +221,7 @@ BOOLEAN MatchBlockReg(PUNICODE_STRING s)
|
||||
return MatchList(s, g_BlockReg);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockReg);
|
||||
}
|
||||
@@ -254,27 +236,29 @@ BOOLEAN MatchBlockRansom(PUNICODE_STRING s)
|
||||
return MatchList(s, g_BlockRansom);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s)
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s)
|
||||
{
|
||||
if (!USOK(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)
|
||||
if (EndsWithNameUS(s, g_Blocksuffix[i])) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(img))
|
||||
return FALSE;
|
||||
|
||||
for (SIZE_T i = 0; g_RemoteSuspectBins[i]; ++i)
|
||||
if (EndsWithNameUS(img, g_RemoteSuspectBins[i]))
|
||||
return TRUE;
|
||||
@@ -283,21 +267,21 @@ BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img)
|
||||
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(cmd))
|
||||
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]))
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i]))
|
||||
return TRUE;
|
||||
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsGeneric[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsGeneric[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]))
|
||||
if (StrStrIW(s, g_RemoteCmdFromCmdExe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"rundll32.exe")) {
|
||||
@@ -305,22 +289,9 @@ BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
|
||||
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"regsvr32.exe") && StrStrIW(s, L"/i:") && StrStrIW(s, L"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]))
|
||||
@@ -331,6 +302,9 @@ BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
|
||||
|
||||
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod)
|
||||
{
|
||||
if (!USOK(mod))
|
||||
return FALSE;
|
||||
|
||||
for (SIZE_T i = 0; g_ScreenCapModules[i]; ++i)
|
||||
if (EndsWithNameUS(mod, g_ScreenCapModules[i]))
|
||||
return TRUE;
|
||||
@@ -339,34 +313,34 @@ BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod)
|
||||
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(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]))
|
||||
if (StrStrIW(s, g_ScreenUserSubdirs[i]))
|
||||
return TRUE;
|
||||
}
|
||||
for (SIZE_T i = 0; g_ScreenOtherProcNeedles[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenOtherProcNeedles[i]))
|
||||
if (StrStrIW(s, g_ScreenOtherProcNeedles[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName)
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName)
|
||||
{
|
||||
if (!USOK(fullImageName))
|
||||
if (!USOK(fullImageName))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = fullImageName->Buffer;
|
||||
BOOLEAN isClr = StrStrIW(s, L"\\clr.dll") || StrStrIW(s, L"\\mscorwks.dll");
|
||||
return (StrStrIW(s, L"\\clr.dll") || StrStrIW(s, L"\\mscorwks.dll")) && !StrStrIW(s, L"\\Windows\\Microsoft.NET\\") && !StrStrIW(s, L"\\Framework");
|
||||
}
|
||||
|
||||
BOOLEAN IsWhitelistExcept(PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(img))
|
||||
return FALSE;
|
||||
|
||||
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;
|
||||
return MatchList(img, g_WhitelistExcept);
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s);
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s);
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s);
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
BOOLEAN IsWhitelistExcept(PUNICODE_STRING img);
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s);
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe);
|
||||
|
||||
+76
-175
@@ -1,4 +1,5 @@
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
typedef union _PS_PROTECTION {
|
||||
@@ -7,218 +8,118 @@ typedef union _PS_PROTECTION {
|
||||
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;
|
||||
} Flags;
|
||||
} PS_PROTECTION, * PPS_PROTECTION;
|
||||
|
||||
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 PVOID g_ObCookie = NULL;
|
||||
|
||||
static BOOLEAN IsProcNameEq(PEPROCESS p, PCSTR name)
|
||||
static NTSTATUS SetPPL31(_In_ PEPROCESS proc)
|
||||
{
|
||||
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)
|
||||
if (!proc || !g_PsSetProcessProtection)
|
||||
return STATUS_PROCEDURE_NOT_FOUND;
|
||||
|
||||
PS_PROTECTION pr = { 0 };
|
||||
pr.Level = 0x31;
|
||||
((VOID(NTAPI*)(PEPROCESS, PS_PROTECTION))g_PsSetProcessProtection)(p, pr);
|
||||
|
||||
PS_PROTECTION p = { 0 };
|
||||
p.Level = 0x31;
|
||||
((VOID(NTAPI*)(PEPROCESS, PS_PROTECTION))g_PsSetProcessProtection)(proc, p);
|
||||
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)
|
||||
static OB_PREOP_CALLBACK_STATUS ObPreOperation(
|
||||
_In_ PVOID RegistrationContext,
|
||||
_Inout_ POB_PRE_OPERATION_INFORMATION info
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (g_Unloading)
|
||||
|
||||
if (!info || info->KernelHandle)
|
||||
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);
|
||||
|
||||
if (info->ObjectType == *PsProcessType) {
|
||||
PEPROCESS proc = (PEPROCESS)info->Object;
|
||||
HANDLE pid = PsGetProcessId(proc);
|
||||
if (g_ProtectedPid && pid == g_ProtectedPid && PsGetCurrentProcessId() != g_ProtectedPid) {
|
||||
ACCESS_MASK* mask = (info->Operation == OB_OPERATION_HANDLE_CREATE)
|
||||
? &info->Parameters->CreateHandleInformation.DesiredAccess
|
||||
: &info->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
|
||||
if (*mask & PROCESS_TERMINATE) {
|
||||
LogAnsi3("KILL_ATTEMPT",
|
||||
(ULONG)(ULONG_PTR)PsGetCurrentProcessId(),
|
||||
NULL, NULL);
|
||||
*mask &= ~(PROCESS_TERMINATE | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
static VOID ProcessNotify(
|
||||
_In_opt_ PEPROCESS Process,
|
||||
_In_ HANDLE Pid,
|
||||
_In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
|
||||
)
|
||||
{
|
||||
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;
|
||||
UNICODE_STRING target = RTL_CONSTANT_STRING(L"\\??\\C:\\PYAS.exe");
|
||||
if (CreateInfo) {
|
||||
if (IsProcNameEq(Process, "PYAS.exe")) {
|
||||
NTSTATUS s = SetPPL31(Process);
|
||||
UpdateProtectedPid(ProcessId);
|
||||
LogSet(ProcessId, s);
|
||||
if (Process && CreateInfo->ImageFileName &&
|
||||
RtlEqualUnicodeString(CreateInfo->ImageFileName, &target, TRUE)) {
|
||||
|
||||
NTSTATUS st = SetPPL31(Process);
|
||||
UNREFERENCED_PARAMETER(st);
|
||||
|
||||
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, Pid);
|
||||
LogAnsi3("PROC_PPL", (ULONG)(ULONG_PTR)Pid, (PUNICODE_STRING)CreateInfo->ImageFileName, NULL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (g_ProtectedPid && ProcessId == g_ProtectedPid) UpdateProtectedPid(NULL);
|
||||
if (g_ProtectedPid && Pid == g_ProtectedPid) {
|
||||
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, 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))
|
||||
|
||||
static OB_OPERATION_REGISTRATION ops[1];
|
||||
RtlZeroMemory(ops, sizeof(ops));
|
||||
ops[0].ObjectType = PsProcessType;
|
||||
ops[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ops[0].PreOperation = ObPreOperation;
|
||||
ops[0].PostOperation = NULL;
|
||||
|
||||
UNICODE_STRING alt = RTL_CONSTANT_STRING(L"321000");
|
||||
OB_CALLBACK_REGISTRATION reg;
|
||||
RtlZeroMemory(®, sizeof(reg));
|
||||
reg.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
reg.OperationRegistrationCount = 1;
|
||||
reg.OperationRegistration = ops;
|
||||
reg.RegistrationContext = NULL;
|
||||
reg.Altitude = alt;
|
||||
|
||||
NTSTATUS s = ObRegisterCallbacks(®, &g_ObCookie);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
g_ObCookie = NULL;
|
||||
return s;
|
||||
|
||||
s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
|
||||
if (NT_SUCCESS(s))
|
||||
g_ProcNotifyEnabled = TRUE;
|
||||
BootstrapExisting();
|
||||
}
|
||||
s = PsSetCreateProcessNotifyRoutineEx(ProcessNotify, FALSE);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ObUnRegisterCallbacks(g_ObCookie);
|
||||
g_ObCookie = NULL;
|
||||
return s;
|
||||
}
|
||||
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;
|
||||
}
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotify, TRUE);
|
||||
}
|
||||
@@ -25,19 +25,6 @@ static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath,
|
||||
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);
|
||||
|
||||
@@ -4,19 +4,6 @@
|
||||
|
||||
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);
|
||||
|
||||
@@ -23,6 +23,7 @@ wchar_t* g_WhitelistExcept[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\diskpart.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\format.com",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\mountvol.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\ntoskrnl.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wbadmin.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdboot.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\dism.exe",
|
||||
@@ -232,7 +233,7 @@ wchar_t* g_RemoteCmdIndicatorsHttp[] = {
|
||||
|
||||
wchar_t* g_RemoteCmdIndicatorsGeneric[] = {
|
||||
L"-enc",
|
||||
L" -e ",
|
||||
L"-e",
|
||||
L"-encodedcommand",
|
||||
L"frombase64string(",
|
||||
L"iex(",
|
||||
|
||||
@@ -4,19 +4,6 @@
|
||||
|
||||
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);
|
||||
|
||||
@@ -4,14 +4,6 @@ Antivirus software written in Python and C that blocks threats through deep lear
|
||||
|
||||
<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
|
||||
|
||||
## Requirements
|
||||
|
||||
Python 3.10 is recommended. Other Python versions may require different pip commands.
|
||||
@@ -88,4 +80,6 @@ Open Source Website : https://github.com/87owo/PYAS
|
||||
|
||||
## Project License
|
||||
|
||||
Please read the Project License. If you would like to apply, please submit your application to Github issues.
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
@@ -1,91 +0,0 @@
|
||||
# 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
|
||||
pip install onnxruntime==1.18.1
|
||||
pip install pefile==2023.2.7
|
||||
pip install Pillow==11.0.0
|
||||
pip install pyperclip==1.9.0
|
||||
pip install PySide6==6.9.1
|
||||
pip install requests==2.32.4
|
||||
pip install yara-python==4.5.4
|
||||
```
|
||||
|
||||
## 文件讯息
|
||||
|
||||
以下列出所有相关代码与文件的存放位置。
|
||||
|
||||
```
|
||||
PYAS/
|
||||
├── Engine/
|
||||
│ ├── Models/
|
||||
│ │ ├── convert.py # 将可执行文件或其他文件转换为图片
|
||||
│ │ └── train.py # TensorFlow CNN 模型训练完整代码
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ ├── rules.yar # YARA 病毒特征码规则匹配
|
||||
│ └── rules.ips # IP 网络地址规则匹配
|
||||
│
|
||||
├── Plugins/
|
||||
│ └── 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 # 版本元数据,用于打包与更新
|
||||
└── ... # 其他补充文件夹与文件
|
||||
```
|
||||
|
||||
## 代码架构
|
||||
|
||||
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/PYAS/releases
|
||||
|
||||
## 系统需求
|
||||
|
||||
| 配置要求 | 权限 | 系统版本 | 处理器 | 可用内存空间 | 可用存储空间 |
|
||||
| ------- | ----- | ------ | ----- | ------------ | ---------- |
|
||||
| 最低配置 | 管理员 | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
|
||||
| 推荐配置 | 管理员 | >= Windows 10 (21H2) | 3 GHz | 300MB | 200MB |
|
||||
|
||||
## 官方网站
|
||||
|
||||
官方网站 : https://pyantivirus.wixsite.com/pyas
|
||||
|
||||
开源网站 : https://github.com/87owo/PYAS
|
||||
|
||||
## 授权条款
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
@@ -1,91 +0,0 @@
|
||||
# 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
|
||||
pip install onnxruntime==1.18.1
|
||||
pip install pefile==2023.2.7
|
||||
pip install Pillow==11.0.0
|
||||
pip install pyperclip==1.9.0
|
||||
pip install PySide6==6.9.1
|
||||
pip install requests==2.32.4
|
||||
pip install yara-python==4.5.4
|
||||
```
|
||||
|
||||
## 檔案資訊
|
||||
|
||||
以下列出所有相關程式碼與文件的存放位置。
|
||||
|
||||
```
|
||||
PYAS/
|
||||
├── Engine/
|
||||
│ ├── Models/
|
||||
│ │ ├── convert.py # 將可執行檔或其他檔案轉換為圖片
|
||||
│ │ └── train.py # TensorFlow CNN 模型訓練完整程式碼
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ ├── rules.yar # YARA 病毒特徵碼規則匹配
|
||||
│ └── rules.ips # IP 網路位址規則匹配
|
||||
│
|
||||
├── Plugins/
|
||||
│ └── 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 # 版本中繼資料,用於打包與更新
|
||||
└── ... # 其他補充資料夾與檔案
|
||||
```
|
||||
|
||||
## 程式架構
|
||||
|
||||
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/PYAS/releases
|
||||
|
||||
## 系統需求
|
||||
|
||||
| 配置要求 | 權限 | 系統版本 | 處理器 | 可用記憶體空間 | 可用儲存空間 |
|
||||
| ------- | ---- | ------- | ------ | ------------ | ----------- |
|
||||
| 最低配置 | 管理員 | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
|
||||
| 推薦配置 | 管理員 | >= Windows 10 (21H2) | 3 GHz | 300MB | 200MB |
|
||||
|
||||
## 官方網站
|
||||
|
||||
官方網站 : https://pyantivirus.wixsite.com/pyas
|
||||
|
||||
開源網站 : https://github.com/87owo/PYAS
|
||||
|
||||
## 授權條款
|
||||
|
||||
https://github.com/87owo/PYAS/blob/main/LICENSE.txt
|
||||
Reference in new issue
Block a user