Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac4f960352 | ||
|
|
e4b3e4e438 | ||
|
|
d6ee476296 | ||
|
|
c23741ba9a | ||
|
|
7bac5b4740 | ||
|
|
1fe440168e | ||
|
|
26ad57138b | ||
|
|
92e1fdaf0f | ||
|
|
357540d0ad | ||
|
|
405622a91e | ||
|
|
5686c73c56 | ||
|
|
76e2f787ac | ||
|
|
02b67f4e6a | ||
|
|
a2ab527761 | ||
|
|
f1142ae742 | ||
|
|
decb222dac | ||
|
|
00c598e6f7 | ||
|
|
e9843dd3a5 | ||
|
|
37f390742b | ||
|
|
533e7dfd15 | ||
|
|
ddcbe1cd51 | ||
|
|
4eabd04bc6 | ||
|
|
c8f2da6ac9 | ||
|
|
8820ac4bf3 | ||
|
|
236b200f39 | ||
|
|
6789e32674 | ||
|
|
9eb55deedf | ||
|
|
6e76b5b8eb | ||
|
|
491b0da83d | ||
|
|
5d2c17e940 | ||
|
|
0497c4a9e7 | ||
|
|
6d0765848b | ||
|
|
40c4ae71bd | ||
|
|
8edba10dff | ||
|
|
85aa7bc652 | ||
|
|
854ad1db07 | ||
|
|
955600a1e1 | ||
|
|
b739134d85 | ||
|
|
723152566d | ||
|
|
678b045e0e | ||
|
|
681f4df49a | ||
|
|
b19f7d7098 | ||
|
|
daaae3ec10 | ||
|
|
c14957afff | ||
|
|
843a4998f1 | ||
|
|
9637e74edc | ||
|
|
b1bdf4f671 | ||
|
|
238ed8e364 | ||
|
|
8aa1509863 | ||
|
|
aac182e30b | ||
|
|
ac7e953acc | ||
|
|
6ae0f27e05 | ||
|
|
f6e5a92400 | ||
|
|
5e5ff3428c | ||
|
|
1cdbb9c402 | ||
|
|
8caacd1870 | ||
|
|
cfbc4c9c20 | ||
|
|
76dbf1c5d0 | ||
|
|
c0e7c4afa5 | ||
|
|
9da0970d85 | ||
|
|
d6f2ad32a7 | ||
|
|
8dbc87a331 | ||
|
|
77eb1ce44e | ||
|
|
e12f4ae61a | ||
|
|
c23a73625f | ||
|
|
3a66946686 | ||
|
|
7fef43701f | ||
|
|
7f5eb34ac6 | ||
|
|
ba4369f829 | ||
|
|
81af2e51fc | ||
|
|
dff09064c8 | ||
|
|
ee0bb32cce | ||
|
|
4a215c308b | ||
|
|
79493a96ea | ||
|
|
3cca1c4671 | ||
|
|
72e9923d08 | ||
|
|
406e152891 | ||
|
|
e9eb569d50 | ||
|
|
640b389e44 | ||
|
|
314e3349d6 | ||
|
|
0ae4d72bc9 | ||
|
|
8920a5fb21 | ||
|
|
b338ff5dd9 | ||
|
|
87e2fcb37c | ||
|
|
bab43e3b17 | ||
|
|
e602088330 | ||
|
|
2f6db92077 | ||
|
|
cee33c0f77 | ||
|
|
4fcdaa10b6 |
No files matched your search
File renamed without changes.
@@ -1,5 +1,5 @@
|
||||
from PIL import Image
|
||||
import numpy, pefile, os
|
||||
import numpy, os
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -14,33 +14,17 @@ def preprocess_image(file_data, size, channels=1):
|
||||
|
||||
####################################################################################################
|
||||
|
||||
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", ".mui", ".cpl"}
|
||||
|
||||
match_data = {}
|
||||
if not os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
return match_data
|
||||
try:
|
||||
with pefile.PE(file_path, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
data = section.get_data()
|
||||
if data:
|
||||
match_data[name] = section.get_data()
|
||||
except pefile.PEFormatError:
|
||||
file_content = None
|
||||
if not os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
return file_content
|
||||
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
|
||||
except:
|
||||
pass
|
||||
return file_content
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -49,14 +33,17 @@ def file_to_images(input_dir, output_dir):
|
||||
for file in files:
|
||||
try:
|
||||
file_path = os.path.join(root, file)
|
||||
for name, data in get_type(file_path).items():
|
||||
print(name, file_path)
|
||||
file_content = get_type(file_path)
|
||||
if file_content:
|
||||
print(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"))
|
||||
image = preprocess_image(file_content, (224, 224))
|
||||
safe_name = os.path.basename(file_path).replace('/', '_').replace('\\', '_')
|
||||
image.save(os.path.join(output_path_dir, f"{safe_name}.png"))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
File renamed without changes.
@@ -11,7 +11,7 @@ tf.keras.mixed_precision.set_global_policy('mixed_float16')
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=256, color_mode="grayscale"):
|
||||
def load_dataset(data_dir, image_size=(224, 224), val_split=0.0001, batch_size=256, color_mode="grayscale"):
|
||||
channels = 1 if color_mode == "grayscale" else 3
|
||||
file_paths, labels, class_indices = get_file_list(data_dir)
|
||||
|
||||
@@ -24,14 +24,13 @@ def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=
|
||||
train_labels = labels[num_val:]
|
||||
val_files = file_paths[:num_val]
|
||||
val_labels = labels[:num_val]
|
||||
num_classes = len(class_indices)
|
||||
|
||||
train_ds = tf.data.Dataset.from_tensor_slices((train_files, train_labels))
|
||||
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.map(lambda f, l: parse_fn(f, l, image_size, channels), 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(lambda f, l: parse_fn(f, l, image_size, channels, num_classes), num_parallel_calls=tf.data.AUTOTUNE)
|
||||
val_ds = val_ds.map(lambda f, l: parse_fn(f, l, image_size, channels), num_parallel_calls=tf.data.AUTOTUNE)
|
||||
val_ds = val_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)
|
||||
|
||||
train_ds.class_indices = class_indices
|
||||
@@ -39,12 +38,12 @@ def load_dataset(data_dir, image_size=(224, 224), val_split=0.00001, batch_size=
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def parse_fn(filename, label, image_size, channels, num_classes):
|
||||
def parse_fn(filename, label, image_size, channels):
|
||||
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)
|
||||
label = tf.cast(label, tf.float32)
|
||||
return img, label
|
||||
|
||||
def get_file_list(directory):
|
||||
@@ -99,7 +98,7 @@ def coordinate_attention(x, reduction):
|
||||
|
||||
return layers.Multiply()([x, x_h_sigmoid, x_w_sigmoid])
|
||||
|
||||
def build_model(input_shape, num_classes):
|
||||
def build_model(input_shape):
|
||||
i = layers.Input(shape=input_shape)
|
||||
x = multi_conv(i, strides=1)
|
||||
x = gated_conv(x, strides=2)
|
||||
@@ -112,52 +111,55 @@ def build_model(input_shape, num_classes):
|
||||
x = layers.GlobalMaxPooling2D()(x)
|
||||
|
||||
x = layers.Dense(x.shape[-1] // 2, activation='gelu', dtype='float32')(x)
|
||||
o = layers.Dense(num_classes, activation='softmax', dtype='float32')(x)
|
||||
o = layers.Dense(1, activation='sigmoid', dtype='float32')(x)
|
||||
return models.Model(i, o)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def calculate_lr_cosine_sq(epoch, total_epochs=25, lr_start=5e-4, lr_end=1e-6):
|
||||
def calculate_lr_cosine_sq(epoch, total_epochs=30, lr_start=5e-4, lr_end=5e-7):
|
||||
cos_val = 0.5 * (1 + numpy.cos(epoch / total_epochs * numpy.pi))
|
||||
return lr_end + (lr_start - lr_end) * (cos_val ** 2)
|
||||
|
||||
def lr_scheduler(epoch):
|
||||
return calculate_lr_cosine_sq(epoch)
|
||||
|
||||
def categorical_focal_loss(gamma=2.0, alpha=0.25):
|
||||
def binary_focal_loss(gamma=2.0, alpha=0.25):
|
||||
def focal_loss(y_true, y_pred):
|
||||
y_true = tf.cast(y_true, tf.float32)
|
||||
y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1.0 - tf.keras.backend.epsilon())
|
||||
cross_entropy = -y_true * tf.math.log(y_pred)
|
||||
loss = alpha * tf.math.pow(1.0 - y_pred, gamma) * cross_entropy
|
||||
return tf.reduce_sum(loss, axis=-1)
|
||||
p_t = tf.where(tf.equal(y_true, 1), y_pred, 1 - y_pred)
|
||||
alpha_t = tf.where(tf.equal(y_true, 1), alpha, 1 - alpha)
|
||||
loss = -alpha_t * tf.math.pow(1.0 - p_t, gamma) * tf.math.log(p_t)
|
||||
return tf.reduce_mean(loss)
|
||||
return focal_loss
|
||||
|
||||
class CustomModelCheckpoint(callbacks.Callback):
|
||||
def on_epoch_end(self, epoch, logs=False):
|
||||
models.save_model(self.model, f"PYAS_Model_Epoch_{epoch + 1}.h5")
|
||||
spec = (tf.TensorSpec((None,) + train_ds.image_shape, tf.float32),)
|
||||
spec = (tf.TensorSpec((None,) + self.model.input.shape[1:], tf.float32),)
|
||||
model_proto, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=17)
|
||||
with open(f"PYAS_Model_Epoch_{epoch + 1}.onnx", "wb") as f:
|
||||
f.write(model_proto.SerializeToString())
|
||||
|
||||
####################################################################################################
|
||||
|
||||
train_ds, val_ds = load_dataset(r'.\Image_File_Pefile')
|
||||
train_ds, val_ds = load_dataset(r'.\Image_Full_Pefile')
|
||||
for imgs, _ in train_ds.take(1):
|
||||
train_ds.image_shape = imgs.shape[1:]
|
||||
break
|
||||
|
||||
try:
|
||||
model = models.load_model('PYAS_Model_Epoch_0.h5')
|
||||
model = models.load_model('PYAS_Model_Epoch_0.h5', compile=False)
|
||||
print("Load model from disk")
|
||||
except:
|
||||
print("Creating a new model")
|
||||
model = build_model(train_ds.image_shape, len(train_ds.class_indices))
|
||||
model = build_model(train_ds.image_shape)
|
||||
|
||||
print(f"Total parameters: {model.count_params()}")
|
||||
model.compile(optimizer='adam', loss=categorical_focal_loss(gamma=2.0, alpha=0.25),
|
||||
metrics=['accuracy', metrics.Precision(name='precision'), metrics.Recall(name='recall'),
|
||||
model.compile(optimizer='adam', loss=binary_focal_loss(gamma=2.0, alpha=0.25),
|
||||
metrics=[metrics.BinaryAccuracy(name='accuracy'), metrics.AUC(name='auc'),
|
||||
metrics.Precision(name='precision'), metrics.Recall(name='recall'),
|
||||
metrics.FalsePositives(name='fp'), metrics.TrueNegatives(name='tn')])
|
||||
|
||||
model.fit(train_ds, epochs=25, callbacks=[CustomModelCheckpoint(), callbacks.LearningRateScheduler(lr_scheduler)], validation_data=val_ds)
|
||||
model.fit(train_ds, epochs=30, callbacks=[CustomModelCheckpoint(), callbacks.LearningRateScheduler(lr_scheduler)], validation_data=val_ds, class_weight={0: 10.0, 1: 1.0})
|
||||
input('Training Complete')
|
||||
@@ -0,0 +1,513 @@
|
||||
import os, sys, time, ctypes, sqlite3, datetime, pefile, math
|
||||
import numpy as np
|
||||
import ctypes.wintypes
|
||||
|
||||
####################################################################################################
|
||||
|
||||
DB_PATH = "pe_features.db"
|
||||
BATCH_SIZE = 100
|
||||
MAX_FILE_SIZE = 256 * 1024 * 1024
|
||||
TARGET_EXTENSIONS = {
|
||||
'.exe', '.dll', '.sys', '.ocx', '.scr', '.efi', '.acm', '.ax', '.cpl', '.drv', '.com'
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class Features:
|
||||
SCHEMA = [
|
||||
"FileEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit", "Machine", "Magic",
|
||||
"TimeDateStamp", "CheckSum", "ImageBase", "SizeOfImage", "SizeOfHeaders",
|
||||
"Characteristics", "DllCharacteristics", "Subsystem", "LoaderFlags",
|
||||
"MajorLinkerVersion", "MinorLinkerVersion", "SizeOfCode",
|
||||
"SizeOfInitializedData", "SizeOfUninitializedData", "AddressOfEntryPoint",
|
||||
"BaseOfCode", "SectionAlignment", "FileAlignment",
|
||||
"MajorOperatingSystemVersion", "MinorOperatingSystemVersion",
|
||||
"MajorImageVersion", "MinorImageVersion",
|
||||
"MajorSubsystemVersion", "MinorSubsystemVersion",
|
||||
"SizeOfStackReserve", "SizeOfStackCommit", "SizeOfHeapReserve", "SizeOfHeapCommit",
|
||||
"NumberOfSections", "NumberOfRvaAndSizes", "PointerToSymbolTable", "NumberOfSymbols",
|
||||
"SizeOfOptionalHeader",
|
||||
|
||||
"TextSection", "TextSizeRatio", "DataSection", "DataSizeRatio",
|
||||
"RsrcSection", "RsrcSizeRatio", "SectionCount", "ExecutableSections",
|
||||
"WritableSections", "ReadableSections", "SectionException",
|
||||
"IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount",
|
||||
"FileDescriptionLength", "FileVersionLength", "ProductNameLength",
|
||||
"ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength",
|
||||
"CommentsLength", "InternalNameLength", "LegalTrademarksLength",
|
||||
"SpecialBuildLength", "PrivateBuildLength",
|
||||
|
||||
"TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease",
|
||||
"IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks",
|
||||
"HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException",
|
||||
|
||||
"_CorExeMain", "_CorDllMain",
|
||||
|
||||
"GetDC", "CreateDCW", "CreateDCA", "BitBlt", "StretchBlt", "CreateCompatibleDC",
|
||||
"CreateCompatibleBitmap", "SelectObject", "DeleteDC", "DeleteObject", "GetDeviceCaps",
|
||||
"GetSystemMetrics",
|
||||
|
||||
"SetWindowsHookExA", "SetWindowsHookExW", "UnhookWindowsHookEx",
|
||||
"GetAsyncKeyState", "GetKeyState", "GetKeyboardState",
|
||||
"MapVirtualKeyA", "MapVirtualKeyW", "MapVirtualKeyExA", "MapVirtualKeyExW",
|
||||
"ToAscii", "ToAsciiEx", "ToUnicode", "ToUnicodeEx", "KeybdEvent", "SendInput",
|
||||
|
||||
"GetCursorPos", "SetCursorPos", "MouseEvent", "GetDoubleClickTime",
|
||||
"GetCapture", "SetCapture",
|
||||
|
||||
"WaveInOpen", "WaveInClose", "WaveInStart", "WaveInStop",
|
||||
"CapCreateCaptureWindowA", "CapCreateCaptureWindowW",
|
||||
"OpenClipboard", "CloseClipboard", "GetClipboardData", "SetClipboardData",
|
||||
|
||||
"GetDesktopWindow", "GetForegroundWindow", "GetWindowDC", "GetWindowRect",
|
||||
"GetClientRect", "PrintWindow", "SetWindowDisplayAffinity", "FindWindowA", "FindWindowW",
|
||||
"EnumWindows", "EnumChildWindows",
|
||||
|
||||
"Direct3DCreate9", "D3D11CreateDevice", "GdipSaveImageToFile",
|
||||
|
||||
"CreateProcessA", "CreateProcessW", "CreateThread", "ResumeThread", "SuspendThread",
|
||||
"OpenProcess", "TerminateProcess", "GetCurrentProcess", "GetCurrentProcessId",
|
||||
"ExitProcess", "WinExec", "ShellExecuteA", "ShellExecuteW",
|
||||
|
||||
"CreateRemoteThread", "QueueUserAPC", "VirtualAlloc", "VirtualAllocEx",
|
||||
"VirtualProtect", "VirtualProtectEx", "WriteProcessMemory", "ReadProcessMemory",
|
||||
"NtUnmapViewOfSection", "SetThreadContext", "GetThreadContext", "Wow64SetThreadContext",
|
||||
"ZwUnmapViewOfSection", "CreateFileMappingA", "CreateFileMappingW", "MapViewOfFile",
|
||||
"UnmapViewOfSection",
|
||||
|
||||
"LoadLibraryA", "LoadLibraryW", "LoadLibraryExA", "LoadLibraryExW",
|
||||
"GetProcAddress", "GetModuleHandleA", "GetModuleHandleW", "FreeLibrary",
|
||||
|
||||
"CreateFileA", "CreateFileW", "WriteFile", "ReadFile", "DeleteFileA", "DeleteFileW",
|
||||
"CopyFileA", "CopyFileW", "MoveFileA", "MoveFileW", "FindFirstFileA", "FindNextFileA",
|
||||
"GetTempPathA", "GetTempPathW", "GetTempFileNameA", "GetTempFileNameW",
|
||||
"SetFileAttributesA", "SetFileAttributesW", "DeviceIoControl", "SetFileTime",
|
||||
|
||||
"RegOpenKeyExA", "RegOpenKeyExW", "RegSetValueExA", "RegSetValueExW",
|
||||
"RegCreateKeyExA", "RegCreateKeyExW", "RegDeleteKeyA", "RegDeleteKeyW",
|
||||
"RegEnumValueA", "RegEnumValueW", "RegQueryValueExA", "RegQueryValueExW",
|
||||
"RegDeleteValueA", "RegDeleteValueW",
|
||||
|
||||
"OpenSCManagerA", "OpenSCManagerW", "CreateServiceA", "CreateServiceW",
|
||||
"StartServiceA", "StartServiceW", "ControlService", "DeleteService",
|
||||
|
||||
"AdjustTokenPrivileges", "LookupPrivilegeValueA", "LookupPrivilegeValueW",
|
||||
"OpenProcessToken", "NetUserAdd", "NetLocalGroupAddMembers",
|
||||
|
||||
"Socket", "Connect", "Send", "Recv", "WSAStartup", "gethostbyname", "getaddrinfo",
|
||||
"WSAIoctl", "WSASocketA", "WSASocketW",
|
||||
"URLDownloadToFileA", "URLDownloadToFileW",
|
||||
"InternetOpenA", "InternetOpenW", "InternetConnectA", "InternetConnectW",
|
||||
"InternetOpenUrlA", "InternetOpenUrlW",
|
||||
"HttpOpenRequestA", "HttpOpenRequestW", "HttpSendRequestA", "HttpSendRequestW",
|
||||
"InternetReadFile", "DnsQuery_A", "DnsQuery_W",
|
||||
|
||||
"IsDebuggerPresent", "CheckRemoteDebuggerPresent", "OutputDebugStringA", "OutputDebugStringW",
|
||||
"GetTickCount", "QueryPerformanceCounter", "Sleep", "GetSystemTimeAsFileTime",
|
||||
"GetLocalTime", "GlobalMemoryStatus", "GetVersionExA", "GetVersionExW",
|
||||
"GetComputerNameA", "GetComputerNameW", "GetUserNameA", "GetUserNameW",
|
||||
|
||||
"CryptAcquireContextA", "CryptAcquireContextW", "CryptCreateHash", "CryptHashData",
|
||||
"CryptDeriveKey", "CryptEncrypt", "CryptDecrypt", "CryptDestroyKey", "CryptDestroyHash",
|
||||
"CryptReleaseContext", "CryptGenKey", "CryptImportKey", "CryptExportKey",
|
||||
"RtlDecompressBuffer", "ConnectNamedPipe", "PeekNamedPipe"
|
||||
]
|
||||
|
||||
SCHEMA_SET = set(SCHEMA)
|
||||
PACKERS = {
|
||||
'upx', 'aspack', 'asprotect', 'pecompact', 'upack', 'fsg', 'mew',
|
||||
'mpress', 'ezip', 'pklt', 'shrink', 'petite', 'telock',
|
||||
|
||||
'themida', 'winlicense', 'tmd', 'vmp', 'enigma', 'obsidium',
|
||||
'pelock', 'exestealth', 'yoda', 'armadillo', 'zprotect',
|
||||
'sforce', 'starforce', 'qihoo', 'wisevec', 'megastop',
|
||||
}
|
||||
INSTALLER_SIGS = [b"Nullsoft.NSIS", b"Inno Setup", b"7-Zip.7zip", b"InstallShield"]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def verify_signature(file_path):
|
||||
if os.name != 'nt':
|
||||
return 0
|
||||
try:
|
||||
wintrust = ctypes.windll.wintrust
|
||||
WINTRUST_ACTION_GENERIC_VERIFY_V2 = ctypes.c_char_p(b'\x6b\xc5\xaa\x00\x44\xcd\xd0\x11\x8c\xc2\x00\xc0\x4f\xc2\x95\xee')
|
||||
|
||||
class WINTRUST_FILE_INFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
|
||||
("hFile", ctypes.wintypes.HANDLE),
|
||||
("pgKnownSubject", ctypes.wintypes.LPVOID)
|
||||
]
|
||||
|
||||
class WINTRUST_DATA(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
|
||||
("pSIPClientData", ctypes.wintypes.LPVOID),
|
||||
("dwUIChoice", ctypes.wintypes.DWORD),
|
||||
("fdwRevocationChecks", ctypes.wintypes.DWORD),
|
||||
("dwUnionChoice", ctypes.wintypes.DWORD),
|
||||
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
|
||||
("dwStateAction", ctypes.wintypes.DWORD),
|
||||
("hWVTStateData", ctypes.wintypes.HANDLE),
|
||||
("pwszURLReference", ctypes.wintypes.LPCWSTR),
|
||||
("dwProvFlags", ctypes.wintypes.DWORD),
|
||||
("dwUIContext", ctypes.wintypes.DWORD),
|
||||
("pSignatureSettings", ctypes.wintypes.LPVOID)
|
||||
]
|
||||
|
||||
fi = WINTRUST_FILE_INFO(ctypes.sizeof(WINTRUST_FILE_INFO), file_path, None, None)
|
||||
td = WINTRUST_DATA(ctypes.sizeof(WINTRUST_DATA), None, None, 2, 1, 1, ctypes.pointer(fi), 0, None, None, 0x00000080, 0, None)
|
||||
action = ctypes.wintypes.GUID.from_buffer_copy(WINTRUST_ACTION_GENERIC_VERIFY_V2)
|
||||
return 1 if wintrust.WinVerifyTrust(None, ctypes.byref(action), ctypes.byref(td)) == 0 else 0
|
||||
except:
|
||||
return 0
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def safe_float(val):
|
||||
try:
|
||||
f = float(val)
|
||||
if math.isinf(f) or math.isnan(f):
|
||||
return 0.0
|
||||
return f
|
||||
except:
|
||||
return 0.0
|
||||
|
||||
def process_single_file(file_path):
|
||||
def _calc_entropy(data):
|
||||
if not data:
|
||||
return 0.0
|
||||
arr = np.frombuffer(data, dtype=np.uint8)
|
||||
counts = np.bincount(arr, minlength=256)
|
||||
probs = counts[counts > 0] / len(arr)
|
||||
return float(-np.sum(probs * np.log2(probs)))
|
||||
|
||||
def _safe_div(n, d):
|
||||
return n / d if d > 0 else 0.0
|
||||
|
||||
try:
|
||||
fsize = os.path.getsize(file_path)
|
||||
if fsize == 0 or fsize > MAX_FILE_SIZE:
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
fts = {k: 0 for k in Features.SCHEMA}
|
||||
pe = None
|
||||
try:
|
||||
pe = pefile.PE(file_path, fast_load=True)
|
||||
|
||||
fts['TrustSigned'] = verify_signature(file_path)
|
||||
|
||||
fh = pe.FILE_HEADER
|
||||
fts['Machine'] = fh.Machine
|
||||
fts['NumberOfSections'] = fh.NumberOfSections
|
||||
fts['TimeDateStamp'] = fh.TimeDateStamp
|
||||
fts['PointerToSymbolTable'] = fh.PointerToSymbolTable
|
||||
fts['NumberOfSymbols'] = fh.NumberOfSymbols
|
||||
fts['SizeOfOptionalHeader'] = fh.SizeOfOptionalHeader
|
||||
fts['Characteristics'] = fh.Characteristics
|
||||
|
||||
curr_ts = datetime.datetime.utcnow().timestamp()
|
||||
fts['HasInvalidTimestamp'] = 1 if (fh.TimeDateStamp < 631152000 or fh.TimeDateStamp > curr_ts + 2592000) else 0
|
||||
|
||||
if hasattr(pe, 'OPTIONAL_HEADER'):
|
||||
op = pe.OPTIONAL_HEADER
|
||||
fts['Magic'] = op.Magic
|
||||
fts['MajorLinkerVersion'] = op.MajorLinkerVersion
|
||||
fts['MinorLinkerVersion'] = op.MinorLinkerVersion
|
||||
fts['SizeOfCode'] = op.SizeOfCode
|
||||
fts['SizeOfInitializedData'] = op.SizeOfInitializedData
|
||||
fts['SizeOfUninitializedData'] = op.SizeOfUninitializedData
|
||||
fts['AddressOfEntryPoint'] = op.AddressOfEntryPoint
|
||||
fts['BaseOfCode'] = op.BaseOfCode
|
||||
fts['ImageBase'] = op.ImageBase
|
||||
fts['SectionAlignment'] = op.SectionAlignment
|
||||
fts['FileAlignment'] = op.FileAlignment
|
||||
fts['MajorOperatingSystemVersion'] = op.MajorOperatingSystemVersion
|
||||
fts['MinorOperatingSystemVersion'] = op.MinorOperatingSystemVersion
|
||||
fts['MajorImageVersion'] = op.MajorImageVersion
|
||||
fts['MinorImageVersion'] = op.MinorImageVersion
|
||||
fts['MajorSubsystemVersion'] = op.MajorSubsystemVersion
|
||||
fts['MinorSubsystemVersion'] = op.MinorSubsystemVersion
|
||||
fts['SizeOfImage'] = op.SizeOfImage
|
||||
fts['SizeOfHeaders'] = op.SizeOfHeaders
|
||||
fts['CheckSum'] = op.CheckSum
|
||||
fts['Subsystem'] = op.Subsystem
|
||||
fts['DllCharacteristics'] = op.DllCharacteristics
|
||||
fts['SizeOfStackReserve'] = op.SizeOfStackReserve
|
||||
fts['SizeOfStackCommit'] = op.SizeOfStackCommit
|
||||
fts['SizeOfHeapReserve'] = op.SizeOfHeapReserve
|
||||
fts['SizeOfHeapCommit'] = op.SizeOfHeapCommit
|
||||
fts['LoaderFlags'] = op.LoaderFlags
|
||||
fts['NumberOfRvaAndSizes'] = op.NumberOfRvaAndSizes
|
||||
|
||||
fts['Is64Bit'] = 1 if fh.Machine == 0x8664 else 0
|
||||
fts['IsExe'] = 1 if pe.is_exe() else 0
|
||||
fts['IsDll'] = 1 if pe.is_dll() else 0
|
||||
fts['IsDriver'] = 1 if pe.is_driver() else 0
|
||||
|
||||
if len(op.DATA_DIRECTORY) > 9 and op.DATA_DIRECTORY[9].VirtualAddress > 0:
|
||||
fts['HasTlsCallbacks'] = 1
|
||||
if len(op.DATA_DIRECTORY) > 5 and op.DATA_DIRECTORY[5].Size > 0:
|
||||
fts['HasRelocationDirectory'] = 1
|
||||
|
||||
pe.parse_sections(pe.FILE_HEADER.NumberOfSections)
|
||||
|
||||
file_data = pe.get_memory_mapped_image()
|
||||
fts['FileEntropy'] = _calc_entropy(file_data)
|
||||
|
||||
fts['SectionCount'] = len(pe.sections)
|
||||
for section in pe.sections:
|
||||
try:
|
||||
name = section.Name.decode('ascii', 'ignore').strip('\x00')
|
||||
except:
|
||||
name = ""
|
||||
s_data = section.get_data()
|
||||
s_entropy = _calc_entropy(s_data)
|
||||
|
||||
if any(p in name.lower() for p in Features.PACKERS):
|
||||
fts['HasPacked'] = 1
|
||||
if section.Characteristics & 0x20000000:
|
||||
fts['ExecutableSections'] += 1
|
||||
if section.Characteristics & 0x80000000:
|
||||
fts['WritableSections'] += 1
|
||||
if section.Characteristics & 0x40000000:
|
||||
fts['ReadableSections'] += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize:
|
||||
fts['SectionException'] = 1
|
||||
|
||||
ratio = _safe_div(section.SizeOfRawData, fsize)
|
||||
if name.startswith('.text'):
|
||||
fts['TextSection'] = s_entropy
|
||||
fts['TextSizeRatio'] = ratio
|
||||
elif name.startswith('.data'):
|
||||
fts['DataSection'] = s_entropy
|
||||
fts['DataSizeRatio'] = ratio
|
||||
elif name.startswith('.rsrc'):
|
||||
fts['RsrcSection'] = s_entropy
|
||||
fts['RsrcSizeRatio'] = ratio
|
||||
if b"requireAdministrator" in s_data:
|
||||
fts['IsAdmin'] = 1
|
||||
|
||||
for sig in Features.INSTALLER_SIGS:
|
||||
if sig in s_data:
|
||||
fts['IsInstall'] = 1
|
||||
break
|
||||
|
||||
pe.parse_data_directories(directories=[
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG']
|
||||
])
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
for imp in entry.imports:
|
||||
fts['ApiCount'] += 1
|
||||
if not imp or not hasattr(imp, 'name') or not imp.name:
|
||||
continue
|
||||
try:
|
||||
name = imp.name.decode('ascii', 'ignore')
|
||||
if name in Features.SCHEMA_SET:
|
||||
fts[name] = 1
|
||||
except:
|
||||
pass
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
|
||||
fts['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols)
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
|
||||
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||
if entry.id == 3 and hasattr(entry, 'directory'):
|
||||
fts['IconCount'] = len(entry.directory.entries)
|
||||
break
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'):
|
||||
fts['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG)
|
||||
|
||||
for k in fts:
|
||||
fts[k] = safe_float(fts[k])
|
||||
|
||||
return fts
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
if pe:
|
||||
pe.close()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def init_db():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.execute("PRAGMA synchronous = OFF")
|
||||
conn.execute("PRAGMA journal_mode = WAL")
|
||||
conn.execute("PRAGMA cache_size = 10000")
|
||||
cols = ", ".join([f"{c} REAL" for c in Features.SCHEMA])
|
||||
conn.execute(f"CREATE TABLE IF NOT EXISTS PeData ({cols}, Label INTEGER)")
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
def prune_db_duplicates(conn):
|
||||
try:
|
||||
print("\n[*] Post-processing: Checking for and removing duplicate entries...")
|
||||
all_cols = ", ".join(Features.SCHEMA + ["Label"])
|
||||
|
||||
sql_count_before = "SELECT COUNT(*) FROM PeData"
|
||||
before = conn.execute(sql_count_before).fetchone()[0]
|
||||
|
||||
sql_dedup = f"""
|
||||
DELETE FROM PeData
|
||||
WHERE rowid NOT IN (
|
||||
SELECT MIN(rowid)
|
||||
FROM PeData
|
||||
GROUP BY {all_cols}
|
||||
)
|
||||
"""
|
||||
conn.execute(sql_dedup)
|
||||
conn.commit()
|
||||
|
||||
after = conn.execute(sql_count_before).fetchone()[0]
|
||||
removed = before - after
|
||||
|
||||
if removed > 0:
|
||||
print(f"[+] Deduplication complete. Removed {removed} duplicate rows.")
|
||||
print(" (Duplicates occur if the same file is scanned multiple times)")
|
||||
else:
|
||||
print("[+] Deduplication complete. No duplicates found.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[-] Deduplication Warning: {e}")
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def scan_and_save(conn, path, label):
|
||||
print(f"\n[*] Scanning: {path} (Label={label})")
|
||||
print(f"[*] Config: Max File Size = {MAX_FILE_SIZE/1024/1024:.0f} MB")
|
||||
|
||||
files = []
|
||||
print("[*] Walking directory tree...")
|
||||
if os.path.isfile(path):
|
||||
files.append(path)
|
||||
else:
|
||||
for r, _, fs in os.walk(path):
|
||||
for f in fs:
|
||||
if os.path.splitext(f)[1].lower() in TARGET_EXTENSIONS:
|
||||
files.append(os.path.join(r, f))
|
||||
|
||||
total = len(files)
|
||||
if total == 0:
|
||||
print("[-] No valid PE files found in this path.")
|
||||
return
|
||||
|
||||
print(f"[*] Found {total} files. Processing...")
|
||||
|
||||
batch = []
|
||||
count = 0
|
||||
errors = 0
|
||||
start_time = time.time()
|
||||
|
||||
placeholders = ",".join(["?"] * (len(Features.SCHEMA) + 1))
|
||||
sql = f"INSERT INTO PeData VALUES ({placeholders})"
|
||||
|
||||
try:
|
||||
for i, file_path in enumerate(files):
|
||||
res = process_single_file(file_path)
|
||||
|
||||
if res:
|
||||
vals = [res.get(c, 0.0) for c in Features.SCHEMA]
|
||||
vals.append(float(label))
|
||||
batch.append(tuple(vals))
|
||||
count += 1
|
||||
else:
|
||||
errors += 1
|
||||
|
||||
if len(batch) >= BATCH_SIZE:
|
||||
try:
|
||||
conn.executemany(sql, batch)
|
||||
conn.commit()
|
||||
batch = []
|
||||
except Exception as batch_err:
|
||||
print(f"\n[-] Batch Error: {batch_err}. Retrying strictly...")
|
||||
for row in batch:
|
||||
try:
|
||||
conn.execute(sql, row)
|
||||
except Exception:
|
||||
errors += 1
|
||||
count -= 1
|
||||
conn.commit()
|
||||
batch = []
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
rate = (i + 1) / elapsed if elapsed > 0 else 0
|
||||
sys.stdout.write(f"\r[{i+1}/{total}] OK: {count} | Skip/Err: {errors} | {rate:.1f} files/s")
|
||||
sys.stdout.flush()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[-] Interrupted by user.")
|
||||
except Exception as e:
|
||||
print(f"\n[-] Unexpected Error: {e}")
|
||||
finally:
|
||||
if batch:
|
||||
print(f"\n[*] Saving remaining {len(batch)} items...")
|
||||
try:
|
||||
conn.executemany(sql, batch)
|
||||
conn.commit()
|
||||
except Exception as final_err:
|
||||
print(f"[-] Final Batch Error: {final_err}. Retrying strictly...")
|
||||
for row in batch:
|
||||
try:
|
||||
conn.execute(sql, row)
|
||||
except Exception:
|
||||
pass
|
||||
conn.commit()
|
||||
|
||||
print(f"\n[+] Scan finished/stopped. Total added: {count}")
|
||||
prune_db_duplicates(conn)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n---------------- PE Dataset Builder v2.6 ----------------\n")
|
||||
print("Type 'exit' or 'q' at any prompt to quit.")
|
||||
|
||||
if os.path.exists(DB_PATH):
|
||||
print(f"[-] Note: If SCHEMA changed, please delete {DB_PATH} before running.")
|
||||
|
||||
conn = init_db()
|
||||
try:
|
||||
while True:
|
||||
print("\n---------------------------------------------------------\n")
|
||||
raw_path = input("[*] Enter Path (File or Folder): ").strip()
|
||||
target_path = raw_path.strip('"').strip("'")
|
||||
|
||||
if target_path.lower() in ['exit', 'q']:
|
||||
break
|
||||
|
||||
if not target_path or not os.path.exists(target_path):
|
||||
print("[-] Invalid path. Please try again.")
|
||||
continue
|
||||
|
||||
while True:
|
||||
label_input = input("[*] Enter Label (0=Safe, 1=Malware): ").strip()
|
||||
if label_input.lower() in ['exit', 'q']:
|
||||
conn.close()
|
||||
sys.exit()
|
||||
|
||||
if label_input in ['0', '1']:
|
||||
label = int(label_input)
|
||||
break
|
||||
else:
|
||||
print("[-] Please enter 0 or 1.")
|
||||
|
||||
scan_and_save(conn, target_path, label)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[-] Interrupted.")
|
||||
finally:
|
||||
conn.close()
|
||||
print(f"\n[+] Database saved to: {os.path.abspath(DB_PATH)}")
|
||||
@@ -0,0 +1,195 @@
|
||||
import os, sys, time, sqlite3, re, json
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import lightgbm as lgb
|
||||
import onnxmltools
|
||||
from onnxmltools.convert.common.data_types import FloatTensorType
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.metrics import classification_report, accuracy_score, roc_auc_score
|
||||
|
||||
####################################################################################################
|
||||
|
||||
DB_PATH = "pe_features.db"
|
||||
MODEL_FILE = "pe_malware_lgbm.txt"
|
||||
ONNX_FILE = "pe_malware.onnx"
|
||||
FEATURE_FILE = "features.json"
|
||||
TEST_SIZE = 0.0001
|
||||
RANDOM_SEED = 42
|
||||
|
||||
LGBM_PARAMS = {
|
||||
'objective': 'binary',
|
||||
'metric': ['binary_logloss', 'auc'],
|
||||
'boosting_type': 'gbdt',
|
||||
'num_leaves': 63,
|
||||
'learning_rate': 0.05,
|
||||
'feature_fraction': 0.8,
|
||||
'bagging_fraction': 0.8,
|
||||
'bagging_freq': 5,
|
||||
'verbose': -1,
|
||||
'seed': RANDOM_SEED,
|
||||
'n_jobs': -1,
|
||||
'max_depth': -1,
|
||||
'min_data_in_leaf': 20,
|
||||
'lambda_l1': 0.1,
|
||||
'lambda_l2': 0.1
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def get_raw_schema(db_path):
|
||||
if not os.path.exists(db_path):
|
||||
return None
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("PRAGMA table_info(PeData)")
|
||||
columns = [row[1] for row in cursor.fetchall()]
|
||||
exclude_lower = {'label', 'id', 'filename', 'filelength', 'rowid', 'probability', 'predictedlabel', 'score'}
|
||||
return [c for c in columns if c.lower().strip() not in exclude_lower]
|
||||
except:
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def load_and_clean_data(db_path, raw_cols):
|
||||
print(f"[*] Connecting to database: {db_path}")
|
||||
conn = sqlite3.connect(db_path)
|
||||
req_cols = list(set(raw_cols + ['Label']))
|
||||
sql = f"SELECT {', '.join(req_cols)} FROM PeData"
|
||||
|
||||
try:
|
||||
df = pd.read_sql_query(sql, conn)
|
||||
if df.empty: return None, None, None
|
||||
|
||||
label_col = next((c for c in df.columns if c.lower().strip() == 'label'), None)
|
||||
if not label_col:
|
||||
print("[-] Critical Error: Label column not found.")
|
||||
return None, None, None
|
||||
|
||||
y = df[label_col].astype(int)
|
||||
X = df.drop(columns=[label_col], errors='ignore')
|
||||
|
||||
valid_features = [c for c in raw_cols if c in X.columns]
|
||||
X = X[valid_features].copy()
|
||||
|
||||
X = X.rename(columns=lambda x: re.sub('[^A-Za-z0-9_]+', '', x))
|
||||
|
||||
if X.isnull().values.any():
|
||||
X = X.fillna(X.median(numeric_only=True))
|
||||
|
||||
final_features = X.columns.tolist()
|
||||
return X, y, final_features
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def train_process(X, y):
|
||||
print(f"[*] Dataset shape: {X.shape}")
|
||||
|
||||
X_train, X_test, y_train, y_test = train_test_split(
|
||||
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
|
||||
)
|
||||
|
||||
n_pos = (y_train == 1).sum()
|
||||
n_neg = (y_train == 0).sum()
|
||||
|
||||
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
|
||||
|
||||
weight_ratio_base = 1.0
|
||||
weight_ratio_target = 0.1
|
||||
final_pos_weight = base_weight * (weight_ratio_target / weight_ratio_base)
|
||||
|
||||
print(f"[*] Balance Report: Safe={n_neg}, Malware={n_pos}")
|
||||
print(f"[*] Weight Config: Base Balanced={base_weight:.4f}, Final Adjusted (Favor Safe)={final_pos_weight:.4f}")
|
||||
|
||||
train_data = lgb.Dataset(X_train, label=y_train)
|
||||
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data)
|
||||
|
||||
params = LGBM_PARAMS.copy()
|
||||
params['scale_pos_weight'] = final_pos_weight
|
||||
|
||||
print("\n[*] Starting LightGBM training (Full Force, No Early Stopping)...")
|
||||
start_time = time.time()
|
||||
|
||||
model = lgb.train(
|
||||
params,
|
||||
train_data,
|
||||
valid_sets=[valid_data],
|
||||
num_boost_round=1000,
|
||||
callbacks=[
|
||||
lgb.log_evaluation(period=100)
|
||||
]
|
||||
)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
print(f"\n[+] Training finished in {elapsed:.2f}s")
|
||||
|
||||
return model, X_test, y_test
|
||||
|
||||
def export_onnx_model(model, feature_names):
|
||||
print(f"[*] Exporting ONNX model to {ONNX_FILE}...")
|
||||
try:
|
||||
initial_type = [('float_input', FloatTensorType([None, len(feature_names)]))]
|
||||
onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_type)
|
||||
onnxmltools.utils.save_model(onnx_model, ONNX_FILE)
|
||||
|
||||
with open(FEATURE_FILE, 'w') as f:
|
||||
json.dump(feature_names, f)
|
||||
|
||||
print(f"[+] ONNX export success. Features synced to {FEATURE_FILE}")
|
||||
except Exception as e:
|
||||
print(f"[-] ONNX export failed: {e}")
|
||||
|
||||
def evaluate_and_save(model, X_test, y_test):
|
||||
print("\n------------------- Evaluation Report -------------------\n")
|
||||
y_prob = model.predict(X_test, num_iteration=model.best_iteration)
|
||||
y_pred = (y_prob > 0.5).astype(int)
|
||||
|
||||
acc = accuracy_score(y_test, y_pred)
|
||||
auc = roc_auc_score(y_test, y_prob)
|
||||
|
||||
print(f"Accuracy : {acc:.4f}")
|
||||
print(f"ROC AUC : {auc:.4f}")
|
||||
print("\n" + classification_report(y_test, y_pred, zero_division=0))
|
||||
|
||||
print("---------------------------------------------------------\n")
|
||||
print("[*] Top 20 Features (Importance by Gain):")
|
||||
|
||||
feature_names = model.feature_name()
|
||||
importance = model.feature_importance(importance_type='gain')
|
||||
feature_imp = pd.DataFrame(sorted(zip(importance, feature_names), reverse=True), columns=['Value', 'Feature'])
|
||||
print(feature_imp.head(20).to_string(index=False))
|
||||
|
||||
print("\n---------------------------------------------------------\n")
|
||||
|
||||
model.save_model(MODEL_FILE)
|
||||
print(f"[+] Native model saved to: {os.path.abspath(MODEL_FILE)}")
|
||||
|
||||
export_onnx_model(model, feature_names)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n---------------- PE Malware Trainer v3.0 ----------------\n")
|
||||
|
||||
if not os.path.exists(DB_PATH):
|
||||
print(f"[-] Error: Database {DB_PATH} not found.")
|
||||
sys.exit(1)
|
||||
|
||||
raw_schema = get_raw_schema(DB_PATH)
|
||||
if not raw_schema:
|
||||
print("[-] Error: Could not read schema.")
|
||||
sys.exit(1)
|
||||
|
||||
X, y, _ = load_and_clean_data(DB_PATH, raw_schema)
|
||||
if X is None:
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
model, X_test, y_test = train_process(X, y)
|
||||
evaluate_and_save(model, X_test, y_test)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[-] Training interrupted.")
|
||||
except Exception as e:
|
||||
print(f"\n[-] Critical Error: {e}")
|
||||
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -24,10 +24,10 @@ translate_dict = {
|
||||
|
||||
"english_switch": {"開啟":"On","關閉":"Off","开启":"On","关闭":"Off",",":", ",
|
||||
"立即刪除":"Delete Now","安全模式":"Safe Mode","官方網站":"Official Website ",
|
||||
"腳本":"Script ","鏈式":"Chain ","記憶體":"Memory ","加載":"Load ",
|
||||
"腳本":"Script ","鏈式":"Chain ","記憶體":"Memory ","正在加載":"Loading ","加載":"Load ",
|
||||
"行為":"Behavior ","可疑":"Suspicious ","勒索":"Ransom ","主要":"Main ",
|
||||
"許可條款":"License Terms ","惡意":"Malicious", "開發":"Developers ",
|
||||
"啟用":"Enable ","禁用":"Disable ","正在初始化中":"Initializing ",
|
||||
"啟用":"Enable ","禁用":"Disable ","正在初始化中":"Initializing ","初始化":"initialize ",
|
||||
"正在掃描":"Scanning ","正在刪除":"Deleting ","正在追蹤":"Tracking ",
|
||||
"最小化到":"Minimize To ","靈敏度":"Sensitivity ","正在檢查":"Checking ",
|
||||
"確定要":"Sure Want To ","引擎靈敏度":"Sensitivity ","更新":"Update ",
|
||||
|
||||
+526
-89
@@ -1,5 +1,6 @@
|
||||
import os, yara, numpy, base64, onnxruntime
|
||||
import ctypes, ctypes.wintypes, pefile
|
||||
import os, yara, time, numpy, base64, requests, pefile, math
|
||||
import ctypes, ctypes.wintypes, hashlib, onnxruntime
|
||||
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
|
||||
@@ -45,129 +46,168 @@ class sign_scanner:
|
||||
for name in path:
|
||||
try:
|
||||
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.WinVerifyTrust = self.wintrust.WinVerifyTrust
|
||||
self.WinVerifyTrust.restype = ctypes.wintypes.LONG
|
||||
self.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.c_void_p]
|
||||
|
||||
def is_sign(self, file_path):
|
||||
with pefile.PE(file_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
|
||||
try:
|
||||
self.WinVerifyTrust = self.wintrust.WinVerifyTrust
|
||||
self.WinVerifyTrust.restype = ctypes.wintypes.LONG
|
||||
self.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.c_void_p]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def sign_verify(self, file_path):
|
||||
fi = WINTRUST_FILE_INFO(ctypes.sizeof(WINTRUST_FILE_INFO), file_path, None, None)
|
||||
data = WINTRUST_DATA(ctypes.sizeof(WINTRUST_DATA), None, None, 2, 0, 1,
|
||||
ctypes.pointer(fi), 1, None, None, 0, 0, None)
|
||||
s = self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
data.dwStateAction = 2
|
||||
self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
return s == 0
|
||||
try:
|
||||
fi = WINTRUST_FILE_INFO(ctypes.sizeof(WINTRUST_FILE_INFO), file_path, None, None)
|
||||
data = WINTRUST_DATA(ctypes.sizeof(WINTRUST_DATA), None, None, 2, 0, 1,
|
||||
ctypes.pointer(fi), 1, None, None, 0, 0, None)
|
||||
s = self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
data.dwStateAction = 2
|
||||
self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
return s == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class rule_scanner:
|
||||
def __init__(self):
|
||||
self.rules = {}
|
||||
self.rules = None
|
||||
self.network = []
|
||||
|
||||
def load_path(self, path):
|
||||
def load_path(self, path, callback=None):
|
||||
yara_files = {}
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
self.load_file(os.path.join(root, file))
|
||||
full_path = os.path.join(root, file)
|
||||
if callback:
|
||||
callback(full_path)
|
||||
ext = os.path.splitext(file)[1].lower()
|
||||
if ext in ('.yara', '.yar'):
|
||||
namespace = os.path.relpath(full_path, path).replace(os.sep, '_')
|
||||
yara_files[namespace] = full_path
|
||||
elif ext in ('.yc', '.yrc'):
|
||||
self.load_compiled_rule(full_path)
|
||||
elif ext in ('.ip', '.txt'):
|
||||
self.load_network_list(full_path)
|
||||
|
||||
def load_file(self, file):
|
||||
ext = os.path.splitext(file)[1].lower()
|
||||
if yara_files:
|
||||
self.compile_all_rules(yara_files)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def load_compiled_rule(self, file):
|
||||
try:
|
||||
if ext in ('.yara', '.yar'):
|
||||
self.rules[file] = yara.compile(file)
|
||||
elif ext in ('.yc', '.yrc'):
|
||||
self.rules[file] = yara.load(file)
|
||||
elif ext in ('.ip', '.txt'):
|
||||
with open(file, "r", encoding="utf-8", errors="ignore") as f:
|
||||
self.network.extend(line.strip() for line in f if line.strip())
|
||||
self.rules = yara.load(file)
|
||||
except Exception:
|
||||
return False
|
||||
pass
|
||||
|
||||
def load_network_list(self, file):
|
||||
try:
|
||||
with open(file, "r", encoding="utf-8", errors="ignore") as f:
|
||||
self.network.extend(line.strip() for line in f if line.strip())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def compile_all_rules(self, file_map):
|
||||
try:
|
||||
self.rules = yara.compile(filepaths=file_map)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def yara_scan(self, file_path):
|
||||
try:
|
||||
for rules in self.rules.values():
|
||||
matches = rules.match(filepath=file_path)
|
||||
if matches:
|
||||
rule_name = str(matches[0])
|
||||
label = rule_name.split("_")[0]
|
||||
level = rule_name.split("_")[-1]
|
||||
return f"Rules/{label}", level
|
||||
if not self.rules:
|
||||
return False, False
|
||||
|
||||
matches = self.rules.match(filepath=file_path)
|
||||
if matches:
|
||||
rule_name = str(matches[0])
|
||||
label = rule_name.split("_")[0]
|
||||
level = rule_name.split("_")[-1]
|
||||
return f"{label}:WinPE/Unknown.A!rb", level
|
||||
return False, False
|
||||
except Exception:
|
||||
return False, False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class model_scanner:
|
||||
class cnn_scanner:
|
||||
def __init__(self):
|
||||
self.model = None
|
||||
self.suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys"}
|
||||
self.labels = ["Pefile/White", "Pefile/General"]
|
||||
self.detect_set = {"Pefile/General"}
|
||||
self.models = []
|
||||
self.labels = ["White:WinPE/Unknown", "General:WinPE/Unknown"]
|
||||
self.detect_set = {"General:WinPE/Unknown"}
|
||||
self.resize = (224, 224)
|
||||
|
||||
def load_path(self, path):
|
||||
def load_path(self, path, callback=None):
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
self.load_file(os.path.join(root, file))
|
||||
full_path = os.path.join(root, file)
|
||||
if callback:
|
||||
callback(full_path)
|
||||
self.load_file(full_path)
|
||||
|
||||
def load_file(self, file):
|
||||
if file.lower().endswith('.onnx'):
|
||||
try:
|
||||
self.model = onnxruntime.InferenceSession(file)
|
||||
session = onnxruntime.InferenceSession(file, providers=['CPUExecutionProvider'])
|
||||
self.models.append(session)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def model_scan(self, file_path, full_output=False):
|
||||
if not self.model:
|
||||
if not self.models:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
sections = self.extract_sections(file_path)
|
||||
if not sections:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
images = [self.preprocess_image(data, self.resize) for data in sections.values()]
|
||||
if not images:
|
||||
data = self.get_data(file_path)
|
||||
if not data:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
arr = numpy.stack([numpy.asarray(img).astype('float32') / 255.0 for img in images])
|
||||
image = self.preprocess_image(data, self.resize)
|
||||
if not image:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
arr = numpy.asarray(image).astype('float32') / 255.0
|
||||
arr = numpy.expand_dims(arr, axis=0)
|
||||
if arr.ndim == 3:
|
||||
arr = numpy.expand_dims(arr, axis=-1)
|
||||
|
||||
input_meta = self.model.get_inputs()[0]
|
||||
input_name = input_meta.name
|
||||
input_shape = input_meta.shape
|
||||
|
||||
if len(input_shape) == 4:
|
||||
if input_shape[1] in (1, 3) and input_shape[3] not in (1, 3):
|
||||
arr = arr.transpose(0, 3, 1, 2)
|
||||
try:
|
||||
probs = self.model.run(None, {input_name: arr})[0]
|
||||
except Exception:
|
||||
return (False, False) if not full_output else ([], str(file_path), None)
|
||||
|
||||
results = []
|
||||
best_malicious = None
|
||||
|
||||
for name, pred, img in zip(sections.keys(), probs, images):
|
||||
idx = int(numpy.argmax(pred))
|
||||
label = self.labels[idx] if idx < len(self.labels) else f"Class_{idx}"
|
||||
conf = round(float(pred[idx]) * 100, 2)
|
||||
print(f"Section: {name} | Label: {label} | Confidence: {conf}%")
|
||||
for model in self.models:
|
||||
input_meta = model.get_inputs()[0]
|
||||
input_name = input_meta.name
|
||||
input_shape = input_meta.shape
|
||||
|
||||
curr_arr = arr.copy()
|
||||
if len(input_shape) == 4:
|
||||
if input_shape[1] in (1, 3) and input_shape[3] not in (1, 3):
|
||||
curr_arr = curr_arr.transpose(0, 3, 1, 2)
|
||||
|
||||
try:
|
||||
probs = model.run(None, {input_name: curr_arr})[0]
|
||||
pred_prob = float(probs[0][0]) if hasattr(probs[0], '__len__') else float(probs[0])
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
is_malicious = pred_prob > 0.5
|
||||
idx = 1 if is_malicious else 0
|
||||
label = self.labels[idx]
|
||||
|
||||
conf = pred_prob if is_malicious else (1.0 - pred_prob)
|
||||
conf = round(conf * 100, 2)
|
||||
|
||||
if full_output:
|
||||
results.append((name, label, conf, self.pil_to_base64(img)))
|
||||
results.append(("Whole File", label, conf, self.pil_to_base64(image)))
|
||||
|
||||
if label in self.detect_set:
|
||||
if best_malicious is None or conf > best_malicious[1]:
|
||||
best_malicious = (label, int(conf))
|
||||
best_malicious = (f"{label}.{int(conf)}!dl", int(conf))
|
||||
|
||||
if full_output:
|
||||
results.sort(key=lambda x: (x[1] not in self.detect_set, -x[2]))
|
||||
@@ -177,6 +217,8 @@ class model_scanner:
|
||||
|
||||
return best_malicious if best_malicious else (False, False)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def pil_to_base64(self, img):
|
||||
buf = BytesIO()
|
||||
img.save(buf, format='PNG')
|
||||
@@ -191,18 +233,413 @@ class model_scanner:
|
||||
image = Image.fromarray(imgbuf.reshape((wah, wah)), 'L')
|
||||
return image.resize((width, height), Image.Resampling.NEAREST)
|
||||
|
||||
def extract_sections(self, file_path):
|
||||
match_data = {}
|
||||
fpath_str = str(file_path)
|
||||
ext = os.path.splitext(fpath_str)[-1].lower()
|
||||
if ext in self.suffix:
|
||||
try:
|
||||
with pefile.PE(fpath_str, fast_load=True) as pe:
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1', errors='ignore').lower()
|
||||
data = section.get_data()
|
||||
if data:
|
||||
match_data[name] = data
|
||||
except Exception:
|
||||
pass
|
||||
return match_data
|
||||
def get_data(self, file_path):
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
return f.read()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class pe_scanner:
|
||||
def __init__(self):
|
||||
self.model = None
|
||||
self.input_name = None
|
||||
self.signer = sign_scanner()
|
||||
self.signer.init_windll(["wintrust"])
|
||||
self.SCHEMA = [
|
||||
"FileEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit", "Machine", "Magic",
|
||||
"TimeDateStamp", "CheckSum", "ImageBase", "SizeOfImage", "SizeOfHeaders",
|
||||
"Characteristics", "DllCharacteristics", "Subsystem", "LoaderFlags",
|
||||
"MajorLinkerVersion", "MinorLinkerVersion", "SizeOfCode",
|
||||
"SizeOfInitializedData", "SizeOfUninitializedData", "AddressOfEntryPoint",
|
||||
"BaseOfCode", "SectionAlignment", "FileAlignment",
|
||||
"MajorOperatingSystemVersion", "MinorOperatingSystemVersion",
|
||||
"MajorImageVersion", "MinorImageVersion",
|
||||
"MajorSubsystemVersion", "MinorSubsystemVersion",
|
||||
"SizeOfStackReserve", "SizeOfStackCommit", "SizeOfHeapReserve", "SizeOfHeapCommit",
|
||||
"NumberOfSections", "NumberOfRvaAndSizes", "PointerToSymbolTable", "NumberOfSymbols",
|
||||
"SizeOfOptionalHeader",
|
||||
"TextSection", "TextSizeRatio", "DataSection", "DataSizeRatio",
|
||||
"RsrcSection", "RsrcSizeRatio", "SectionCount", "ExecutableSections",
|
||||
"WritableSections", "ReadableSections", "SectionException",
|
||||
"IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount",
|
||||
"FileDescriptionLength", "FileVersionLength", "ProductNameLength",
|
||||
"ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength",
|
||||
"CommentsLength", "InternalNameLength", "LegalTrademarksLength",
|
||||
"SpecialBuildLength", "PrivateBuildLength",
|
||||
"TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease",
|
||||
"IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks",
|
||||
"HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException",
|
||||
"_CorExeMain", "_CorDllMain",
|
||||
"GetDC", "CreateDCW", "CreateDCA", "BitBlt", "StretchBlt", "CreateCompatibleDC",
|
||||
"CreateCompatibleBitmap", "SelectObject", "DeleteDC", "DeleteObject", "GetDeviceCaps",
|
||||
"GetSystemMetrics",
|
||||
"SetWindowsHookExA", "SetWindowsHookExW", "UnhookWindowsHookEx",
|
||||
"GetAsyncKeyState", "GetKeyState", "GetKeyboardState",
|
||||
"MapVirtualKeyA", "MapVirtualKeyW", "MapVirtualKeyExA", "MapVirtualKeyExW",
|
||||
"ToAscii", "ToAsciiEx", "ToUnicode", "ToUnicodeEx", "KeybdEvent", "SendInput",
|
||||
"GetCursorPos", "SetCursorPos", "MouseEvent", "GetDoubleClickTime",
|
||||
"GetCapture", "SetCapture",
|
||||
"WaveInOpen", "WaveInClose", "WaveInStart", "WaveInStop",
|
||||
"CapCreateCaptureWindowA", "CapCreateCaptureWindowW",
|
||||
"OpenClipboard", "CloseClipboard", "GetClipboardData", "SetClipboardData",
|
||||
"GetDesktopWindow", "GetForegroundWindow", "GetWindowDC", "GetWindowRect",
|
||||
"GetClientRect", "PrintWindow", "SetWindowDisplayAffinity", "FindWindowA", "FindWindowW",
|
||||
"EnumWindows", "EnumChildWindows",
|
||||
"Direct3DCreate9", "D3D11CreateDevice", "GdipSaveImageToFile",
|
||||
"CreateProcessA", "CreateProcessW", "CreateThread", "ResumeThread", "SuspendThread",
|
||||
"OpenProcess", "TerminateProcess", "GetCurrentProcess", "GetCurrentProcessId",
|
||||
"ExitProcess", "WinExec", "ShellExecuteA", "ShellExecuteW",
|
||||
"CreateRemoteThread", "QueueUserAPC", "VirtualAlloc", "VirtualAllocEx",
|
||||
"VirtualProtect", "VirtualProtectEx", "WriteProcessMemory", "ReadProcessMemory",
|
||||
"NtUnmapViewOfSection", "SetThreadContext", "GetThreadContext", "Wow64SetThreadContext",
|
||||
"ZwUnmapViewOfSection", "CreateFileMappingA", "CreateFileMappingW", "MapViewOfFile",
|
||||
"UnmapViewOfSection",
|
||||
"LoadLibraryA", "LoadLibraryW", "LoadLibraryExA", "LoadLibraryExW",
|
||||
"GetProcAddress", "GetModuleHandleA", "GetModuleHandleW", "FreeLibrary",
|
||||
"CreateFileA", "CreateFileW", "WriteFile", "ReadFile", "DeleteFileA", "DeleteFileW",
|
||||
"CopyFileA", "CopyFileW", "MoveFileA", "MoveFileW", "FindFirstFileA", "FindNextFileA",
|
||||
"GetTempPathA", "GetTempPathW", "GetTempFileNameA", "GetTempFileNameW",
|
||||
"SetFileAttributesA", "SetFileAttributesW", "DeviceIoControl", "SetFileTime",
|
||||
"RegOpenKeyExA", "RegOpenKeyExW", "RegSetValueExA", "RegSetValueExW",
|
||||
"RegCreateKeyExA", "RegCreateKeyExW", "RegDeleteKeyA", "RegDeleteKeyW",
|
||||
"RegEnumValueA", "RegEnumValueW", "RegQueryValueExA", "RegQueryValueExW",
|
||||
"RegDeleteValueA", "RegDeleteValueW",
|
||||
"OpenSCManagerA", "OpenSCManagerW", "CreateServiceA", "CreateServiceW",
|
||||
"StartServiceA", "StartServiceW", "ControlService", "DeleteService",
|
||||
"AdjustTokenPrivileges", "LookupPrivilegeValueA", "LookupPrivilegeValueW",
|
||||
"OpenProcessToken", "NetUserAdd", "NetLocalGroupAddMembers",
|
||||
"Socket", "Connect", "Send", "Recv", "WSAStartup", "gethostbyname", "getaddrinfo",
|
||||
"WSAIoctl", "WSASocketA", "WSASocketW",
|
||||
"URLDownloadToFileA", "URLDownloadToFileW",
|
||||
"InternetOpenA", "InternetOpenW", "InternetConnectA", "InternetConnectW",
|
||||
"InternetOpenUrlA", "InternetOpenUrlW",
|
||||
"HttpOpenRequestA", "HttpOpenRequestW", "HttpSendRequestA", "HttpSendRequestW",
|
||||
"InternetReadFile", "DnsQuery_A", "DnsQuery_W",
|
||||
"IsDebuggerPresent", "CheckRemoteDebuggerPresent", "OutputDebugStringA", "OutputDebugStringW",
|
||||
"GetTickCount", "QueryPerformanceCounter", "Sleep", "GetSystemTimeAsFileTime",
|
||||
"GetLocalTime", "GlobalMemoryStatus", "GetVersionExA", "GetVersionExW",
|
||||
"GetComputerNameA", "GetComputerNameW", "GetUserNameA", "GetUserNameW",
|
||||
"CryptAcquireContextA", "CryptAcquireContextW", "CryptCreateHash", "CryptHashData",
|
||||
"CryptDeriveKey", "CryptEncrypt", "CryptDecrypt", "CryptDestroyKey", "CryptDestroyHash",
|
||||
"CryptReleaseContext", "CryptGenKey", "CryptImportKey", "CryptExportKey",
|
||||
"RtlDecompressBuffer", "ConnectNamedPipe", "PeekNamedPipe"
|
||||
]
|
||||
self.PACKERS = {
|
||||
'upx', 'aspack', 'asprotect', 'pecompact', 'upack', 'fsg', 'mew',
|
||||
'mpress', 'ezip', 'pklt', 'shrink', 'petite', 'telock',
|
||||
'themida', 'winlicense', 'tmd', 'vmp', 'enigma', 'obsidium',
|
||||
'pelock', 'exestealth', 'yoda', 'armadillo', 'zprotect',
|
||||
'sforce', 'starforce', 'qihoo', 'wisevec', 'megastop',
|
||||
}
|
||||
self.INSTALLER_SIGS = [b"Nullsoft.NSIS", b"Inno Setup", b"7-Zip.7zip", b"InstallShield"]
|
||||
self.SCHEMA_SET = set(self.SCHEMA)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def load_path(self, path, callback=None):
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
full_path = os.path.join(root, file)
|
||||
if callback:
|
||||
callback(full_path)
|
||||
self.load_model(full_path)
|
||||
|
||||
def load_model(self, model_path):
|
||||
if not os.path.exists(model_path) or not model_path.endswith('.onnx'):
|
||||
return
|
||||
try:
|
||||
self.model = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
|
||||
self.input_name = self.model.get_inputs()[0].name
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def _safe_float(self, val):
|
||||
try:
|
||||
f = float(val)
|
||||
if math.isinf(f) or math.isnan(f): return 0.0
|
||||
return f
|
||||
except:
|
||||
return 0.0
|
||||
|
||||
def _calc_entropy(self, data):
|
||||
if not data: return 0.0
|
||||
arr = numpy.frombuffer(data, dtype=numpy.uint8)
|
||||
counts = numpy.bincount(arr, minlength=256)
|
||||
probs = counts[counts > 0] / len(arr)
|
||||
return float(-numpy.sum(probs * numpy.log2(probs)))
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def extract_features(self, file_path):
|
||||
fts = {k: 0 for k in self.SCHEMA}
|
||||
pe = None
|
||||
try:
|
||||
fsize = os.path.getsize(file_path)
|
||||
if fsize == 0: return None
|
||||
|
||||
pe = pefile.PE(file_path, fast_load=True)
|
||||
fts['TrustSigned'] = 1 if self.signer.sign_verify(file_path) else 0
|
||||
|
||||
fh = pe.FILE_HEADER
|
||||
fts['Machine'] = fh.Machine
|
||||
fts['NumberOfSections'] = fh.NumberOfSections
|
||||
fts['TimeDateStamp'] = fh.TimeDateStamp
|
||||
fts['PointerToSymbolTable'] = fh.PointerToSymbolTable
|
||||
fts['NumberOfSymbols'] = fh.NumberOfSymbols
|
||||
fts['SizeOfOptionalHeader'] = fh.SizeOfOptionalHeader
|
||||
fts['Characteristics'] = fh.Characteristics
|
||||
|
||||
curr_ts = time.time()
|
||||
fts['HasInvalidTimestamp'] = 1 if (fh.TimeDateStamp < 631152000 or fh.TimeDateStamp > curr_ts + 2592000) else 0
|
||||
|
||||
if hasattr(pe, 'OPTIONAL_HEADER'):
|
||||
op = pe.OPTIONAL_HEADER
|
||||
fts['Magic'] = op.Magic
|
||||
fts['MajorLinkerVersion'] = op.MajorLinkerVersion
|
||||
fts['MinorLinkerVersion'] = op.MinorLinkerVersion
|
||||
fts['SizeOfCode'] = op.SizeOfCode
|
||||
fts['SizeOfInitializedData'] = op.SizeOfInitializedData
|
||||
fts['SizeOfUninitializedData'] = op.SizeOfUninitializedData
|
||||
fts['AddressOfEntryPoint'] = op.AddressOfEntryPoint
|
||||
fts['BaseOfCode'] = op.BaseOfCode
|
||||
fts['ImageBase'] = op.ImageBase
|
||||
fts['SectionAlignment'] = op.SectionAlignment
|
||||
fts['FileAlignment'] = op.FileAlignment
|
||||
fts['MajorOperatingSystemVersion'] = op.MajorOperatingSystemVersion
|
||||
fts['MinorOperatingSystemVersion'] = op.MinorOperatingSystemVersion
|
||||
fts['MajorImageVersion'] = op.MajorImageVersion
|
||||
fts['MinorImageVersion'] = op.MinorImageVersion
|
||||
fts['MajorSubsystemVersion'] = op.MajorSubsystemVersion
|
||||
fts['MinorSubsystemVersion'] = op.MinorSubsystemVersion
|
||||
fts['SizeOfImage'] = op.SizeOfImage
|
||||
fts['SizeOfHeaders'] = op.SizeOfHeaders
|
||||
fts['CheckSum'] = op.CheckSum
|
||||
fts['Subsystem'] = op.Subsystem
|
||||
fts['DllCharacteristics'] = op.DllCharacteristics
|
||||
fts['SizeOfStackReserve'] = op.SizeOfStackReserve
|
||||
fts['SizeOfStackCommit'] = op.SizeOfStackCommit
|
||||
fts['SizeOfHeapReserve'] = op.SizeOfHeapReserve
|
||||
fts['SizeOfHeapCommit'] = op.SizeOfHeapCommit
|
||||
fts['LoaderFlags'] = op.LoaderFlags
|
||||
fts['NumberOfRvaAndSizes'] = op.NumberOfRvaAndSizes
|
||||
|
||||
fts['Is64Bit'] = 1 if fh.Machine == 0x8664 else 0
|
||||
fts['IsExe'] = 1 if pe.is_exe() else 0
|
||||
fts['IsDll'] = 1 if pe.is_dll() else 0
|
||||
fts['IsDriver'] = 1 if pe.is_driver() else 0
|
||||
|
||||
if len(op.DATA_DIRECTORY) > 9 and op.DATA_DIRECTORY[9].VirtualAddress > 0:
|
||||
fts['HasTlsCallbacks'] = 1
|
||||
if len(op.DATA_DIRECTORY) > 5 and op.DATA_DIRECTORY[5].Size > 0:
|
||||
fts['HasRelocationDirectory'] = 1
|
||||
|
||||
pe.parse_sections(pe.FILE_HEADER.NumberOfSections)
|
||||
file_data = pe.get_memory_mapped_image()
|
||||
fts['FileEntropy'] = self._calc_entropy(file_data)
|
||||
fts['SectionCount'] = len(pe.sections)
|
||||
|
||||
for section in pe.sections:
|
||||
try:
|
||||
name = section.Name.decode('ascii', 'ignore').strip('\x00')
|
||||
except:
|
||||
name = ""
|
||||
s_data = section.get_data()
|
||||
s_entropy = self._calc_entropy(s_data)
|
||||
|
||||
if any(p in name.lower() for p in self.PACKERS):
|
||||
fts['HasPacked'] = 1
|
||||
if section.Characteristics & 0x20000000:
|
||||
fts['ExecutableSections'] += 1
|
||||
if section.Characteristics & 0x80000000:
|
||||
fts['WritableSections'] += 1
|
||||
if section.Characteristics & 0x40000000:
|
||||
fts['ReadableSections'] += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize:
|
||||
fts['SectionException'] = 1
|
||||
|
||||
ratio = section.SizeOfRawData / fsize if fsize > 0 else 0
|
||||
if name.startswith('.text'):
|
||||
fts['TextSection'] = s_entropy
|
||||
fts['TextSizeRatio'] = ratio
|
||||
elif name.startswith('.data'):
|
||||
fts['DataSection'] = s_entropy
|
||||
fts['DataSizeRatio'] = ratio
|
||||
elif name.startswith('.rsrc'):
|
||||
fts['RsrcSection'] = s_entropy
|
||||
fts['RsrcSizeRatio'] = ratio
|
||||
if b"requireAdministrator" in s_data:
|
||||
fts['IsAdmin'] = 1
|
||||
for sig in self.INSTALLER_SIGS:
|
||||
if sig in s_data:
|
||||
fts['IsInstall'] = 1
|
||||
break
|
||||
|
||||
pe.parse_data_directories(directories=[
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG']
|
||||
])
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
for imp in entry.imports:
|
||||
fts['ApiCount'] += 1
|
||||
if not imp or not hasattr(imp, 'name') or not imp.name:
|
||||
continue
|
||||
try:
|
||||
name = imp.name.decode('ascii', 'ignore')
|
||||
if name in self.SCHEMA_SET:
|
||||
fts[name] = 1
|
||||
except:
|
||||
pass
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
|
||||
fts['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols)
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
|
||||
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||
if entry.id == 3 and hasattr(entry, 'directory'):
|
||||
fts['IconCount'] = len(entry.directory.entries)
|
||||
break
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'):
|
||||
fts['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG)
|
||||
|
||||
return {k: self._safe_float(v) for k, v in fts.items()}
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
if pe: pe.close()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def pe_scan(self, file_path):
|
||||
if not self.model:
|
||||
return False, False
|
||||
|
||||
try:
|
||||
data = self.extract_features(file_path)
|
||||
if not data:
|
||||
return False, False
|
||||
|
||||
vec = numpy.array(
|
||||
[data.get(feat, 0.0) for feat in self.SCHEMA],
|
||||
dtype=numpy.float32
|
||||
).reshape(1, -1)
|
||||
|
||||
outputs = self.model.run(None, {self.input_name: vec})
|
||||
result = outputs[1]
|
||||
prob = 0.0
|
||||
if isinstance(result, list):
|
||||
prob = float(result[0].get(1, 0.0))
|
||||
|
||||
score = int(prob * 100)
|
||||
if prob > 0.5:
|
||||
return f"General:WinPE/Unknown.{score}!ml", score
|
||||
|
||||
return False, False
|
||||
|
||||
except Exception:
|
||||
return False, False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class cloud_scanner:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def calc_hash(self, file_path, block_size=65536):
|
||||
try:
|
||||
h = hashlib.sha256()
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(block_size), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def upload_file(self, file_path, api_host, api_key):
|
||||
try:
|
||||
sha256 = self.calc_hash(file_path)
|
||||
if not sha256:
|
||||
return False, None
|
||||
|
||||
base = api_host.rstrip('/')
|
||||
base_headers = {"X-API-Key": api_key, "User-Agent": "PYAS-Client/1.0"}
|
||||
|
||||
def _req(m, ep, files=None, **k):
|
||||
try:
|
||||
return requests.request(m, f"{base}{ep}", headers=base_headers, files=files, timeout=120, **k)
|
||||
except:
|
||||
return None
|
||||
|
||||
r = _req("GET", f"/api/processing_status/{sha256}")
|
||||
status = r.json().get('status') if r and r.status_code == 200 else 'missing'
|
||||
if status in ['failed', 'error']:
|
||||
status = 'missing'
|
||||
|
||||
if status == 'missing':
|
||||
with open(file_path, 'rb') as f:
|
||||
r = _req("POST", "/api/upload", files={'file': f})
|
||||
if not r or r.status_code != 200:
|
||||
return False, sha256
|
||||
|
||||
return True, sha256
|
||||
except Exception:
|
||||
return False, None
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def get_result(self, sha256, api_host, api_key):
|
||||
try:
|
||||
if not sha256:
|
||||
return False
|
||||
|
||||
base = api_host.rstrip('/')
|
||||
headers = {"X-API-Key": api_key, "User-Agent": "PYAS-Client/1.0"}
|
||||
|
||||
def _req(m, ep, **k):
|
||||
try:
|
||||
return requests.request(m, f"{base}{ep}", headers=headers, timeout=10, **k)
|
||||
except:
|
||||
return None
|
||||
|
||||
for _ in range(30):
|
||||
r = _req("GET", f"/api/processing_status/{sha256}")
|
||||
st = r.json().get('status') if r else 'error'
|
||||
if st == 'done':
|
||||
break
|
||||
if st in ['error', 'failed']:
|
||||
return False
|
||||
time.sleep(1)
|
||||
|
||||
r = _req("GET", f"/api/report/{sha256}")
|
||||
if r and r.status_code == 200:
|
||||
data = r.json().get('data', {})
|
||||
res = data.get('detection', {}).get('results', {}).get('PYAS', {})
|
||||
label = res.get('label', 'Unsupport')
|
||||
sims = data.get('similar', [])
|
||||
|
||||
is_malicious = 'General' in label
|
||||
sim_malicious_count = 0
|
||||
valid_sim_count = 0
|
||||
for s in sims:
|
||||
if s.get('similarity', 0) > 80:
|
||||
valid_sim_count += 1
|
||||
if "General" in s.get('label'):
|
||||
sim_malicious_count += 1
|
||||
|
||||
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
|
||||
return f"General:WinPE/Unknown.{score}!cl"
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
+16
-13
@@ -363,6 +363,7 @@ class Ui_main_window(object):
|
||||
self.english_switch.setLayoutDirection(Qt.LeftToRight)
|
||||
self.english_switch.setStyleSheet(u"QRadioButton { background-color: transparent; color: rgb(0, 0, 0); }\n"
|
||||
"")
|
||||
self.english_switch.setChecked(True)
|
||||
self.extension_widget = QWidget(self.setting_window)
|
||||
self.extension_widget.setObjectName(u"extension_widget")
|
||||
self.extension_widget.setGeometry(QRect(30, 100, 750, 80))
|
||||
@@ -463,6 +464,7 @@ class Ui_main_window(object):
|
||||
self.white_switch.setLayoutDirection(Qt.LeftToRight)
|
||||
self.white_switch.setStyleSheet(u"QRadioButton { background-color: transparent; color: rgb(0, 0, 0); }\n"
|
||||
"")
|
||||
self.white_switch.setChecked(True)
|
||||
self.cloud_widget = QWidget(self.setting_window)
|
||||
self.cloud_widget.setObjectName(u"cloud_widget")
|
||||
self.cloud_widget.setGeometry(QRect(30, 180, 750, 80))
|
||||
@@ -488,18 +490,19 @@ class Ui_main_window(object):
|
||||
self.cloud_text.setScaledContents(False)
|
||||
self.cloud_text.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
|
||||
self.cloud_text.setWordWrap(False)
|
||||
self.cloud_button = QPushButton(self.cloud_widget)
|
||||
self.cloud_button.setObjectName(u"cloud_button")
|
||||
self.cloud_button.setGeometry(QRect(660, 20, 80, 30))
|
||||
sizePolicy1.setHeightForWidth(self.cloud_button.sizePolicy().hasHeightForWidth())
|
||||
self.cloud_button.setSizePolicy(sizePolicy1)
|
||||
self.cloud_button.setFont(font)
|
||||
self.cloud_button.setFocusPolicy(Qt.StrongFocus)
|
||||
self.cloud_button.setLayoutDirection(Qt.LeftToRight)
|
||||
self.cloud_button.setStyleSheet(u"QPushButton { background-color: rgb(235, 235, 235); color: rgb(0, 0, 0); border-radius: 10px; } QPushButton:hover { background-color: rgb(240, 240, 240); }\n"
|
||||
self.cloud_switch = QPushButton(self.cloud_widget)
|
||||
self.cloud_switch.setObjectName(u"cloud_switch")
|
||||
self.cloud_switch.setGeometry(QRect(660, 20, 80, 30))
|
||||
sizePolicy1.setHeightForWidth(self.cloud_switch.sizePolicy().hasHeightForWidth())
|
||||
self.cloud_switch.setSizePolicy(sizePolicy1)
|
||||
self.cloud_switch.setFont(font)
|
||||
self.cloud_switch.setFocusPolicy(Qt.StrongFocus)
|
||||
self.cloud_switch.setLayoutDirection(Qt.LeftToRight)
|
||||
self.cloud_switch.setStyleSheet(u"QPushButton { background-color: rgb(235, 235, 235); color: rgb(0, 0, 0); border-radius: 10px; } QPushButton:hover { background-color: rgb(240, 240, 240); } QPushButton:checked { background-color: rgb(210, 255, 210); } QPushButton:checked:hover { background-color: rgb(220, 255, 220); }\n"
|
||||
"")
|
||||
self.cloud_button.setIconSize(QSize(16, 16))
|
||||
self.cloud_button.setCheckable(False)
|
||||
self.cloud_switch.setIconSize(QSize(16, 16))
|
||||
self.cloud_switch.setCheckable(True)
|
||||
self.cloud_switch.setChecked(True)
|
||||
self.reset_widget = QWidget(self.setting_window)
|
||||
self.reset_widget.setObjectName(u"reset_widget")
|
||||
self.reset_widget.setGeometry(QRect(30, 260, 750, 80))
|
||||
@@ -1413,10 +1416,10 @@ class Ui_main_window(object):
|
||||
self.blank_window.raise_()
|
||||
self.tool_window.raise_()
|
||||
self.taskmgr_window.raise_()
|
||||
self.setting_window.raise_()
|
||||
self.home_window.raise_()
|
||||
self.navigate_window.raise_()
|
||||
self.options_window.raise_()
|
||||
self.setting_window.raise_()
|
||||
|
||||
self.retranslateUi(main_window)
|
||||
|
||||
@@ -1460,7 +1463,7 @@ class Ui_main_window(object):
|
||||
self.white_switch.setText(QCoreApplication.translate("main_window", u"\u767d\u8272\u4e3b\u984c", None))
|
||||
self.cloud_title.setText(QCoreApplication.translate("main_window", u"\u96f2\u7aef\u670d\u52d9", None))
|
||||
self.cloud_text.setText(QCoreApplication.translate("main_window", u"\u6b64\u9078\u9805\u53ef\u4ee5\u4e0a\u5831\u96f2\u7aef\u5f15\u64ce", None))
|
||||
self.cloud_button.setText(QCoreApplication.translate("main_window", u"\u9078\u64c7", None))
|
||||
self.cloud_switch.setText(QCoreApplication.translate("main_window", u"\u958b\u555f", None))
|
||||
self.reset_title.setText(QCoreApplication.translate("main_window", u"\u91cd\u7f6e\u9078\u9805", None))
|
||||
self.reset_text.setText(QCoreApplication.translate("main_window", u"\u6b64\u9078\u9805\u53ef\u4ee5\u91cd\u7f6e\u6240\u6709\u8a2d\u5b9a", None))
|
||||
self.reset_button.setText(QCoreApplication.translate("main_window", u"\u9078\u64c7", None))
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.3.7',
|
||||
version='3.4.0',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
#include <fltKernel.h>
|
||||
#include <ntddk.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <ntstrsafe.h>
|
||||
|
||||
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
|
||||
constexpr auto MAX_PATH_LEN = 1024;
|
||||
constexpr auto PYAS_POOL_TAG = 'SAYP';
|
||||
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD (0x0002)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION (0x0008)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ (0x0010)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE (0x0020)
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE (0x0040)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS (0x0080)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION (0x0200)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_INFORMATION
|
||||
#define PROCESS_QUERY_INFORMATION (0x0400)
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME (0x0800)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA (0x0100)
|
||||
#endif
|
||||
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
|
||||
#endif
|
||||
|
||||
#ifndef FSCTL_MANAGE_BYPASS_IO
|
||||
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
typedef enum _FS_BPIO_OPERATIONS {
|
||||
FS_BPIO_OP_ENABLE = 1,
|
||||
FS_BPIO_OP_DISABLE = 2,
|
||||
FS_BPIO_OP_QUERY = 3,
|
||||
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
|
||||
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
|
||||
FS_BPIO_OP_STREAM_PAUSE = 6,
|
||||
FS_BPIO_OP_STREAM_RESUME = 7,
|
||||
FS_BPIO_OP_GET_INFO = 8
|
||||
} FS_BPIO_OPERATIONS;
|
||||
|
||||
typedef struct _FS_BPIO_INPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG InFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
|
||||
|
||||
typedef struct _FS_BPIO_OUTPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG OutFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
NTSTATUS Status;
|
||||
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct _PYAS_MESSAGE {
|
||||
ULONG MessageCode;
|
||||
ULONG ProcessId;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_MESSAGE, * PPYAS_MESSAGE;
|
||||
|
||||
typedef struct _DRIVER_DATA {
|
||||
PDRIVER_OBJECT DriverObject;
|
||||
PFLT_FILTER FilterHandle;
|
||||
PFLT_PORT ServerPort;
|
||||
PFLT_PORT ClientPort;
|
||||
PEPROCESS PyasProcess;
|
||||
ULONG PyasPid;
|
||||
LARGE_INTEGER Cookie;
|
||||
FAST_MUTEX PortMutex;
|
||||
FAST_MUTEX TrackerMutex;
|
||||
EX_RUNDOWN_REF PortRundown;
|
||||
} DRIVER_DATA, * PDRIVER_DATA;
|
||||
|
||||
typedef struct _RULE_NODE {
|
||||
struct _RULE_NODE* Next;
|
||||
UNICODE_STRING Pattern;
|
||||
} RULE_NODE, * PRULE_NODE;
|
||||
|
||||
extern DRIVER_DATA GlobalData;
|
||||
|
||||
//
|
||||
// Rule Lists Externs (Added for Exception Path support)
|
||||
//
|
||||
extern PRULE_NODE g_RegistryBlockList;
|
||||
extern PRULE_NODE g_RegistryTrustedList;
|
||||
extern PRULE_NODE g_ProcessTrustedPaths;
|
||||
extern PRULE_NODE g_ProcessExploitable;
|
||||
extern PRULE_NODE g_FileProtectedPaths;
|
||||
extern PRULE_NODE g_FileExceptionPaths; // [Added] Exception List
|
||||
extern PRULE_NODE g_FileRansomExts;
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
|
||||
NTSTATUS InitializeProcessProtection();
|
||||
VOID UninitializeProcessProtection();
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
|
||||
VOID UninitializeRegistryProtection();
|
||||
|
||||
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
|
||||
VOID UnloadRules();
|
||||
|
||||
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
|
||||
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
|
||||
BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId);
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId);
|
||||
BOOLEAN IsInstallerProcess(HANDLE ProcessId);
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
|
||||
|
||||
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
|
||||
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
|
||||
|
||||
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
|
||||
|
||||
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
|
||||
#if (NTDDI_VERSION >= NTDDI_WIN10_VB)
|
||||
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
|
||||
#else
|
||||
PVOID Buffer = ExAllocatePoolWithTag(NonPagedPoolNx, Size, PYAS_POOL_TAG);
|
||||
if (Buffer) RtlZeroMemory(Buffer, Size);
|
||||
return Buffer;
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCEINLINE VOID PyasFree(PVOID Ptr) {
|
||||
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
|
||||
LARGE_INTEGER g_CmRegHandle;
|
||||
PDEVICE_OBJECT g_ControlDeviceObject;
|
||||
KSPIN_LOCK g_ProtectLock;
|
||||
FAST_MUTEX HookMutex;
|
||||
PDRIVER_OBJECT DiskDrvObj = NULL;
|
||||
KEVENT g_LogDrainEvent;
|
||||
BOOLEAN g_Unloading = FALSE;
|
||||
BOOLEAN g_CmRegActive = FALSE;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
PDEVICE_OBJECT g_GuardDevice = NULL;
|
||||
PDRIVER_OBJECT g_DriverObject = NULL;
|
||||
|
||||
PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS] = { 0 };
|
||||
ULONG g_MbrFilterCount = 0;
|
||||
|
||||
volatile LONG g_LogWorkCount = 0;
|
||||
static UNICODE_STRING g_DeviceName;
|
||||
static WCHAR g_DeviceNameBuf[64];
|
||||
static UNICODE_STRING g_SymLink = RTL_CONSTANT_STRING(L"\\??\\PYAS_Driver");
|
||||
static BOOLEAN g_SymLinkCreated = FALSE;
|
||||
|
||||
static NTSTATUS CombinedDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
if (DeviceObject == g_ControlDeviceObject) {
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (s->MajorFunction == IRP_MJ_CREATE || s->MajorFunction == IRP_MJ_CLOSE || s->MajorFunction == IRP_MJ_CLEANUP) {
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (s->MajorFunction == IRP_MJ_PNP) {
|
||||
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
|
||||
if (s->MinorFunction == IRP_MN_REMOVE_DEVICE) {
|
||||
NTSTATUS st;
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
st = lower ? IoCallDriver(lower, Irp) : STATUS_SUCCESS;
|
||||
if (lower) IoDetachDevice(lower);
|
||||
IoDeleteDevice(DeviceObject);
|
||||
return st;
|
||||
}
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
|
||||
return BootProtectDispatch(DeviceObject, Irp);
|
||||
}
|
||||
|
||||
static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\Driver\\Disk");
|
||||
UNICODE_STRING sym = RTL_CONSTANT_STRING(L"IoDriverObjectType");
|
||||
POBJECT_TYPE* pType = (POBJECT_TYPE*)MmGetSystemRoutineAddress(&sym);
|
||||
|
||||
if (!pType || !*pType)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
NTSTATUS status = ObReferenceObjectByName(&name, OBJ_CASE_INSENSITIVE, NULL, 0, *pType, KernelMode, NULL, (PVOID*)&DiskDrvObj);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
ULONG done = 0;
|
||||
status = IoEnumerateDeviceObjectList(DiskDrvObj, NULL, 0, &done);
|
||||
if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_SUCCESS) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
PDEVICE_OBJECT* list = ExAllocatePool2(POOL_FLAG_NON_PAGED, done * sizeof(PDEVICE_OBJECT), 'dskT');
|
||||
if (!list) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
status = IoEnumerateDeviceObjectList(DiskDrvObj, list, done * sizeof(PDEVICE_OBJECT), &done);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
ExFreePool2(list, 'dskT', NULL, 0);
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < done && g_MbrFilterCount < MAX_MBR_TARGETS; ++i) {
|
||||
PDEVICE_OBJECT lower = list[i], filter, attached = NULL;
|
||||
|
||||
status = IoCreateDevice(DriverObject, 0, NULL, lower->DeviceType, lower->Characteristics, FALSE, &filter);
|
||||
if (!NT_SUCCESS(status))
|
||||
continue;
|
||||
|
||||
filter->Flags |= (lower->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE));
|
||||
|
||||
status = IoAttachDeviceToDeviceStackSafe(filter, lower, &attached);
|
||||
if (!NT_SUCCESS(status) || !attached) {
|
||||
IoDeleteDevice(filter);
|
||||
continue;
|
||||
}
|
||||
|
||||
filter->StackSize = lower->StackSize + 1;
|
||||
filter->AlignmentRequirement = lower->AlignmentRequirement;
|
||||
filter->DeviceExtension = attached;
|
||||
filter->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
g_MbrFilterTargets[g_MbrFilterCount++] = filter;
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < done; ++i)
|
||||
ObDereferenceObject(list[i]);
|
||||
ExFreePool2(list, 'dskT', NULL, 0);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
g_DriverObject = DriverObject;
|
||||
|
||||
KeInitializeSpinLock(&g_ProtectLock);
|
||||
ExInitializeFastMutex(&HookMutex);
|
||||
KeInitializeEvent(&g_LogDrainEvent, NotificationEvent, TRUE);
|
||||
g_Unloading = FALSE;
|
||||
g_CmRegActive = FALSE;
|
||||
|
||||
InterlockedExchange(&g_LogWorkCount, 0);
|
||||
ExInitializeRundownProtection(&g_Rundown);
|
||||
|
||||
{
|
||||
UNICODE_STRING guardName = RTL_CONSTANT_STRING(L"\\Device\\PYAS_Driver_Guard");
|
||||
NTSTATUS gs = IoCreateDevice(DriverObject, 0, &guardName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_GuardDevice);
|
||||
if (!NT_SUCCESS(gs))
|
||||
return STATUS_DEVICE_BUSY;
|
||||
}
|
||||
|
||||
{
|
||||
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
|
||||
g_DeviceName.Buffer = g_DeviceNameBuf;
|
||||
g_DeviceName.MaximumLength = sizeof(g_DeviceNameBuf);
|
||||
RtlStringCchPrintfW(g_DeviceName.Buffer, RTL_NUMBER_OF(g_DeviceNameBuf), L"\\Device\\PYAS_Driver_%p_%I64x", DriverObject, t.QuadPart);
|
||||
g_DeviceName.Length = (USHORT)(wcslen(g_DeviceName.Buffer) * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
NTSTATUS status = IoCreateDevice(DriverObject, 0, &g_DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_ControlDeviceObject);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
DriverObject->DriverUnload = DriverUnload;
|
||||
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
|
||||
DriverObject->MajorFunction[i] = CombinedDispatch;
|
||||
|
||||
DriverObject->Flags |= 0x20;
|
||||
g_ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
{
|
||||
UINT i = 0;
|
||||
for (; i < 50; ++i) {
|
||||
status = IoCreateSymbolicLink(&g_SymLink, &g_DeviceName);
|
||||
if (NT_SUCCESS(status))
|
||||
break;
|
||||
LARGE_INTEGER d = { 0 };
|
||||
d.QuadPart = -10LL * 1000LL * 10LL;
|
||||
KeDelayExecutionThread(KernelMode, FALSE, &d);
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
}
|
||||
if (!NT_SUCCESS(status)) {
|
||||
IoDeleteDevice(g_ControlDeviceObject);
|
||||
g_ControlDeviceObject = NULL;
|
||||
if (g_GuardDevice) { IoDeleteDevice(g_GuardDevice); g_GuardDevice = NULL; }
|
||||
return status;
|
||||
}
|
||||
}
|
||||
g_SymLinkCreated = TRUE;
|
||||
|
||||
{
|
||||
static WCHAR regAltBuf[32];
|
||||
static UNICODE_STRING regAlt;
|
||||
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
|
||||
RtlStringCchPrintfW(regAltBuf, RTL_NUMBER_OF(regAltBuf), L"385100.%I64x", t.QuadPart);
|
||||
RtlInitUnicodeString(®Alt, regAltBuf);
|
||||
|
||||
status = CmRegisterCallbackEx(RegistryProtectCallback, ®Alt, DriverObject, NULL, &g_CmRegHandle, NULL);
|
||||
if (NT_SUCCESS(status))
|
||||
g_CmRegActive = TRUE;
|
||||
}
|
||||
|
||||
InitImageProtect();
|
||||
InitInjectProtect();
|
||||
InitRemoteProtect();
|
||||
InitScreenProtect();
|
||||
InitProcessProtect();
|
||||
|
||||
InitFileProtect();
|
||||
AttachDiskFilters(DriverObject);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
g_Unloading = TRUE;
|
||||
|
||||
if (g_SymLinkCreated) {
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
g_SymLinkCreated = FALSE;
|
||||
}
|
||||
|
||||
if (g_CmRegActive) {
|
||||
CmUnRegisterCallback(g_CmRegHandle);
|
||||
g_CmRegActive = FALSE;
|
||||
}
|
||||
|
||||
UninitFileProtect();
|
||||
UninitInjectProtect();
|
||||
UninitImageProtect();
|
||||
UninitRemoteProtect();
|
||||
UninitScreenProtect();
|
||||
UninitProcessProtect();
|
||||
|
||||
ExWaitForRundownProtectionRelease(&g_Rundown);
|
||||
|
||||
if (InterlockedCompareExchange((volatile LONG*)&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
if (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeResetEvent(&g_LogDrainEvent);
|
||||
while (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0)
|
||||
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < g_MbrFilterCount; ++i) {
|
||||
if (g_MbrFilterTargets[i]) {
|
||||
IoDetachDevice((PDEVICE_OBJECT)g_MbrFilterTargets[i]->DeviceExtension);
|
||||
IoDeleteDevice(g_MbrFilterTargets[i]);
|
||||
g_MbrFilterTargets[i] = NULL;
|
||||
}
|
||||
}
|
||||
g_MbrFilterCount = 0;
|
||||
|
||||
if (DiskDrvObj) {
|
||||
ObDereferenceObject(DiskDrvObj);
|
||||
DiskDrvObj = NULL;
|
||||
}
|
||||
if (g_ControlDeviceObject) {
|
||||
IoDeleteDevice(g_ControlDeviceObject);
|
||||
g_ControlDeviceObject = NULL;
|
||||
}
|
||||
if (g_GuardDevice) {
|
||||
IoDeleteDevice(g_GuardDevice);
|
||||
g_GuardDevice = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
DRIVER_DATA GlobalData;
|
||||
|
||||
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InstanceQueryTeardown(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownStart(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
|
||||
if (!(Flags & FLTFL_FILTER_UNLOAD_MANDATORY)) {
|
||||
ExAcquireFastMutex(&GlobalData.PortMutex);
|
||||
BOOLEAN IsConnected = (GlobalData.ClientPort != NULL);
|
||||
ExReleaseFastMutex(&GlobalData.PortMutex);
|
||||
|
||||
if (IsConnected) {
|
||||
return STATUS_FLT_DO_NOT_DETACH;
|
||||
}
|
||||
}
|
||||
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
UnloadRules(); // Release JSON rules memory
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
}
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
|
||||
UNREFERENCED_PARAMETER(ServerPortCookie);
|
||||
UNREFERENCED_PARAMETER(ConnectionContext);
|
||||
UNREFERENCED_PARAMETER(SizeOfContext);
|
||||
UNREFERENCED_PARAMETER(ConnectionPortCookie);
|
||||
|
||||
ExAcquireFastMutex(&GlobalData.PortMutex);
|
||||
|
||||
GlobalData.ClientPort = ClientPort;
|
||||
GlobalData.PyasPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
|
||||
GlobalData.PyasProcess = PsGetCurrentProcess();
|
||||
|
||||
ExReInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
ExReleaseFastMutex(&GlobalData.PortMutex);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID PortDisconnect(PVOID ConnectionCookie) {
|
||||
UNREFERENCED_PARAMETER(ConnectionCookie);
|
||||
|
||||
ExAcquireFastMutex(&GlobalData.PortMutex);
|
||||
GlobalData.ClientPort = NULL;
|
||||
GlobalData.PyasPid = 0;
|
||||
if (GlobalData.PyasProcess) {
|
||||
GlobalData.PyasProcess = NULL;
|
||||
}
|
||||
ExReleaseFastMutex(&GlobalData.PortMutex);
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
}
|
||||
|
||||
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
|
||||
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
|
||||
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
|
||||
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
|
||||
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
|
||||
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
|
||||
{ IRP_MJ_OPERATION_END }
|
||||
};
|
||||
|
||||
CONST FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
Callbacks,
|
||||
DriverUnload,
|
||||
InstanceSetup,
|
||||
InstanceQueryTeardown,
|
||||
InstanceTeardownStart,
|
||||
InstanceTeardownComplete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
PSECURITY_DESCRIPTOR sd = NULL;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
UNICODE_STRING name = { 0 };
|
||||
|
||||
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
|
||||
GlobalData.DriverObject = DriverObject;
|
||||
ExInitializeFastMutex(&GlobalData.PortMutex);
|
||||
ExInitializeFastMutex(&GlobalData.TrackerMutex);
|
||||
|
||||
ExInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
status = LoadRulesFromDisk(RegistryPath);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
// Optional: Fail load if rules missing, or continue with empty rules
|
||||
// return status;
|
||||
}
|
||||
|
||||
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) return status;
|
||||
|
||||
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
|
||||
if (NT_SUCCESS(status)) {
|
||||
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
|
||||
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, NULL, 1);
|
||||
FltFreeSecurityDescriptor(sd);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
InitializeProcessProtection();
|
||||
InitializeRegistryProtection(DriverObject);
|
||||
PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
|
||||
return FltStartFiltering(GlobalData.FilterHandle);
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <fltKernel.h>
|
||||
|
||||
#define PIPE_NAME L"\\??\\pipe\\PYAS_Output_Pipe"
|
||||
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x00000001
|
||||
#endif
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE 0x0001
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD 0x0002
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION 0x0008
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE 0x0020
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ 0x0010
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE 0x0040
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS 0x0080
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUota 0x0100
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION 0x0200
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME 0x0800
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
|
||||
#endif
|
||||
#ifndef PROCESS_SET_LIMITED_INFORMATION
|
||||
#define PROCESS_SET_LIMITED_INFORMATION 0x2000
|
||||
#endif
|
||||
#ifndef THREAD_TERMINATE
|
||||
#define THREAD_TERMINATE 0x0001
|
||||
#endif
|
||||
#ifndef THREAD_SUSPEND_RESUME
|
||||
#define THREAD_SUSPEND_RESUME 0x0002
|
||||
#endif
|
||||
#ifndef THREAD_SET_CONTEXT
|
||||
#define THREAD_SET_CONTEXT 0x0010
|
||||
#endif
|
||||
#ifndef THREAD_IMPERSONATE
|
||||
#define THREAD_IMPERSONATE 0x0100
|
||||
#endif
|
||||
#ifndef THREAD_DIRECT_IMPERSONATION
|
||||
#define THREAD_DIRECT_IMPERSONATION 0x0200
|
||||
#endif
|
||||
#ifndef JOB_OBJECT_ASSIGN_PROCESS
|
||||
#define JOB_OBJECT_ASSIGN_PROCESS 0x0001
|
||||
#endif
|
||||
#ifndef JOB_OBJECT_TERMINATE
|
||||
#define JOB_OBJECT_TERMINATE 0x0008
|
||||
#endif
|
||||
#ifndef FILE_DISPOSITION_DELETE
|
||||
#define FILE_DISPOSITION_DELETE 0x00000001
|
||||
#endif
|
||||
#ifndef UINT
|
||||
typedef unsigned int UINT;
|
||||
#endif
|
||||
#define MAX_MBR_TARGETS 64
|
||||
|
||||
extern POBJECT_TYPE* PsJobType;
|
||||
extern volatile LONG g_LogWorkCount;
|
||||
extern BOOLEAN g_Unloading;
|
||||
extern KEVENT g_LogDrainEvent;
|
||||
extern KSPIN_LOCK g_ProtectLock;
|
||||
extern LARGE_INTEGER g_CmRegHandle;
|
||||
extern POBJECT_TYPE* IoDriverObjectType;
|
||||
extern POBJECT_TYPE* IoFileObjectType;
|
||||
extern FAST_MUTEX HookMutex;
|
||||
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);
|
||||
typedef PVOID(NTAPI* _pyas_ExAllocatePoolWithTag)(POOL_TYPE, SIZE_T, ULONG);
|
||||
typedef VOID(NTAPI* _pyas_ExFreePoolWithTag)(PVOID, ULONG);
|
||||
|
||||
static __forceinline PVOID PYAS_ExAllocatePool2(ULONG f, SIZE_T s, ULONG t) {
|
||||
static _pyas_ExAllocatePool2 p2 = 0;
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePool2");
|
||||
p2 = (_pyas_ExAllocatePool2)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p2)
|
||||
return p2(f, s, t);
|
||||
static _pyas_ExAllocatePoolWithTag p1 = 0;
|
||||
if (!p1) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePoolWithTag");
|
||||
p1 = (_pyas_ExAllocatePoolWithTag)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p1)
|
||||
return p1((f & POOL_FLAG_NON_PAGED) ? NonPagedPoolNx : PagedPool, s, t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static __forceinline VOID PYAS_ExFreePool2(PVOID a, ULONG t, PVOID h, ULONG f) {
|
||||
UNREFERENCED_PARAMETER(h);
|
||||
UNREFERENCED_PARAMETER(f);
|
||||
|
||||
static _pyas_ExFreePool2 p2 = 0;
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePool2");
|
||||
p2 = (_pyas_ExFreePool2)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p2) {
|
||||
p2(a, t, h, f);
|
||||
return;
|
||||
}
|
||||
static _pyas_ExFreePoolWithTag p1 = 0;
|
||||
if (!p1) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePoolWithTag");
|
||||
p1 = (_pyas_ExFreePoolWithTag)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p1) {
|
||||
p1(a, t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define ExAllocatePool2 PYAS_ExAllocatePool2
|
||||
#define ExFreePool2 PYAS_ExFreePool2
|
||||
|
||||
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
|
||||
VOID SendPipeLog(PCSTR msg, SIZE_T len);
|
||||
VOID UninitImageProtect(VOID);
|
||||
VOID UninitInjectProtect(VOID);
|
||||
VOID UninitRemoteProtect(VOID);
|
||||
VOID UninitScreenProtect(VOID);
|
||||
VOID UninitProcessProtect(VOID);
|
||||
VOID UninitFileProtect(VOID);
|
||||
|
||||
VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2);
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(
|
||||
_In_ HANDLE ProcessHandle,
|
||||
_In_ PROCESSINFOCLASS ProcessInformationClass,
|
||||
_Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
|
||||
_In_ ULONG ProcessInformationLength,
|
||||
_Out_opt_ PULONG ReturnLength
|
||||
);
|
||||
|
||||
NTSTATUS ObReferenceObjectByName(
|
||||
IN PUNICODE_STRING ObjectName,
|
||||
IN ULONG Attributes,
|
||||
IN PACCESS_STATE PassedAccessState OPTIONAL,
|
||||
IN ACCESS_MASK DesiredAccess OPTIONAL,
|
||||
IN POBJECT_TYPE ObjectType,
|
||||
IN KPROCESSOR_MODE AccessMode,
|
||||
IN OUT PVOID ParseContext OPTIONAL,
|
||||
OUT PVOID* Object
|
||||
);
|
||||
|
||||
NTSTATUS 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,80 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
extern PDEVICE_OBJECT g_ControlDeviceObject;
|
||||
|
||||
typedef struct _PIPE_LOG_CTX {
|
||||
PIO_WORKITEM Item;
|
||||
SIZE_T Len;
|
||||
PCHAR Buf;
|
||||
}PIPE_LOG_CTX, * PPIPE_LOG_CTX;
|
||||
|
||||
static VOID PipeLogWork(PDEVICE_OBJECT DeviceObject, PVOID Context)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)Context;
|
||||
HANDLE h = NULL;
|
||||
UNICODE_STRING name;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
RtlInitUnicodeString(&name, PIPE_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
if (NT_SUCCESS(ZwCreateFile(&h, FILE_GENERIC_WRITE, &oa, &iosb, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) {
|
||||
ZwWriteFile(h, NULL, NULL, NULL, &iosb, ctx->Buf, (ULONG)(ctx->Len > 0xFFFFFFFF ? 0xFFFFFFFF : ctx->Len), NULL, NULL);
|
||||
ZwClose(h);
|
||||
}
|
||||
IoFreeWorkItem(ctx->Item);
|
||||
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
|
||||
if (InterlockedDecrement(&g_LogWorkCount) == 0) {
|
||||
KeSetEvent(&g_LogDrainEvent, IO_NO_INCREMENT, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
VOID SendPipeLog(PCSTR msg, SIZE_T len)
|
||||
{
|
||||
if (!msg || len == 0)
|
||||
return;
|
||||
if (!g_ControlDeviceObject)
|
||||
return;
|
||||
if (g_Unloading)
|
||||
return;
|
||||
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PIPE_LOG_CTX), 'golP');
|
||||
if (!ctx)
|
||||
return;
|
||||
ctx->Len = len;
|
||||
ctx->Buf = (PCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, ctx->Len, 'golP');
|
||||
if (!ctx->Buf) {
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
return;
|
||||
}
|
||||
RtlCopyMemory(ctx->Buf, msg, ctx->Len);
|
||||
ctx->Item = IoAllocateWorkItem(g_ControlDeviceObject);
|
||||
if (!ctx->Item) {
|
||||
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
return;
|
||||
}
|
||||
InterlockedIncrement(&g_LogWorkCount);
|
||||
IoQueueWorkItem(ctx->Item, PipeLogWork, DelayedWorkQueue, ctx);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,27 +1,52 @@
|
||||
[Version]
|
||||
Signature = "$WINDOWS NT$"
|
||||
Class = System
|
||||
ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318}
|
||||
Class = ActivityMonitor
|
||||
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
|
||||
Provider = %ManufacturerName%
|
||||
DriverVer = 12/29/2025,1.0.0.0
|
||||
CatalogFile = PYAS_Driver.cat
|
||||
PnpLockdown = 1
|
||||
DriverVer =
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir = 13
|
||||
|
||||
[DefaultInstall.ntamd64]
|
||||
CopyFiles = @PYAS_Driver.sys
|
||||
AddService = PYAS_Driver,0x00000002
|
||||
|
||||
[SourceDisksFiles]
|
||||
PYAS_Driver.sys = 1
|
||||
DefaultDestDir = 12
|
||||
PYAS_Driver.DriverFiles = 12
|
||||
|
||||
[SourceDisksNames]
|
||||
1 = %DiskName%,,,""
|
||||
|
||||
[SourceDisksFiles]
|
||||
PYAS_Driver.sys = 1,,
|
||||
|
||||
[DefaultInstall.NTamd64]
|
||||
OptionDesc = %ServiceDescription%
|
||||
CopyFiles = PYAS_Driver.DriverFiles
|
||||
|
||||
[DefaultInstall.NTamd64.Services]
|
||||
AddService = %ServiceName%,,PYAS_Driver.Service
|
||||
|
||||
[PYAS_Driver.DriverFiles]
|
||||
PYAS_Driver.sys
|
||||
|
||||
[PYAS_Driver.Service]
|
||||
DisplayName = %ServiceName%
|
||||
Description = %ServiceDescription%
|
||||
ServiceBinary = %12%\PYAS_Driver.sys
|
||||
Dependencies = FltMgr
|
||||
ServiceType = 2
|
||||
StartType = 3
|
||||
ErrorControl = 1
|
||||
LoadOrderGroup = "FSFilter Activity Monitor"
|
||||
AddReg = PYAS_Driver.AddReg
|
||||
|
||||
[PYAS_Driver.AddReg]
|
||||
HKR,"Parameters","DebugFlags",0x00010001,0x0
|
||||
HKR,"Parameters","SupportedFeatures",0x00010001,0x3
|
||||
HKR,"Parameters\Instances","DefaultInstance",0x00000000,"PYAS Instance"
|
||||
HKR,"Parameters\Instances\PYAS Instance","Altitude",0x00000000,"320000"
|
||||
HKR,"Parameters\Instances\PYAS Instance","Flags",0x00010001,0x0
|
||||
|
||||
[Strings]
|
||||
ManufacturerName = "PYAS_Driver"
|
||||
DiskName = "PYAS_Driver"
|
||||
ServiceName="PYAS_Driver"
|
||||
ServiceDisplayName="PYAS_Driver"
|
||||
ManufacturerName = "PYAS Security"
|
||||
DiskName = "PYAS_Driver Source Disk"
|
||||
ServiceName = "PYAS_Driver"
|
||||
ServiceDescription = "PYAS Security Driver"
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36518.9 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PYAS_Driver", "PYAS_Driver.vcxproj", "{699C79A8-A904-1976-F72A-CDD7E811CFD2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.Build.0 = Debug|x64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.ActiveCfg = Release|x64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.Build.0 = Release|x64
|
||||
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.Deploy.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D166699A-50D5-470E-9559-39B8E842528D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{699C79A8-A904-1976-F72A-CDD7E811CFD2}</ProjectGuid>
|
||||
<TemplateGuid>{dd38f7fc-d7bd-488b-9242-7d8754cde80d}</TemplateGuid>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">x64</Platform>
|
||||
<RootNamespace>PYAS_Driver</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>WDM</DriverType>
|
||||
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>WDM</DriverType>
|
||||
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>WDM</DriverType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>WDM</DriverType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<EnableInf2cat>false</EnableInf2cat>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DriverSign>
|
||||
<FileDigestAlgorithm>sha256</FileDigestAlgorithm>
|
||||
</DriverSign>
|
||||
<ClCompile>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DriverSign>
|
||||
<FileDigestAlgorithm>sha256</FileDigestAlgorithm>
|
||||
</DriverSign>
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="PYAS_Driver.inf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FilesToPackage Include="$(TargetPath)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DriverCommon.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DriverEntry.cpp" />
|
||||
<ClCompile Include="ProtectBoot.cpp" />
|
||||
<ClCompile Include="ProtectFile.cpp" />
|
||||
<ClCompile Include="ProtectProcess.cpp" />
|
||||
<ClCompile Include="ProtectRegistry.cpp" />
|
||||
<ClCompile Include="ProtectRules.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Driver Files">
|
||||
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
|
||||
<Extensions>inf;inv;inx;mof;mc;</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="PYAS_Driver.inf">
|
||||
<Filter>Driver Files</Filter>
|
||||
</Inf>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DriverCommon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ProtectRules.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProtectFile.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProtectBoot.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProtectRegistry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProtectProcess.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DriverEntry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,175 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
#define PROTECTED_BOOT_AREA_SIZE (34 * 512)
|
||||
|
||||
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || exe->MaximumLength == 0)
|
||||
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 (KeGetCurrentIrql() > APC_LEVEL)
|
||||
return FALSE;
|
||||
|
||||
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 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_DISK_SET_DRIVE_LAYOUT || code == IOCTL_DISK_SET_DRIVE_LAYOUT_EX) return TRUE;
|
||||
if (code == IOCTL_DISK_DELETE_DRIVE_LAYOUT) return TRUE;
|
||||
if (code == IOCTL_DISK_FORMAT_TRACKS || code == IOCTL_DISK_FORMAT_TRACKS_EX) return TRUE;
|
||||
if (code == IOCTL_DISK_SET_PARTITION_INFO || code == IOCTL_DISK_SET_PARTITION_INFO_EX) return TRUE;
|
||||
if (code == IOCTL_SCSI_PASS_THROUGH || code == IOCTL_SCSI_PASS_THROUGH_DIRECT) return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsBootSectorWrite(PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (s->MajorFunction == IRP_MJ_WRITE) {
|
||||
LARGE_INTEGER offset = s->Parameters.Write.ByteOffset;
|
||||
if (offset.QuadPart < PROTECTED_BOOT_AREA_SIZE) {
|
||||
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)) {
|
||||
blocked = TRUE;
|
||||
}
|
||||
|
||||
if (!blocked && IsBootSectorWrite(s)) {
|
||||
blocked = TRUE;
|
||||
}
|
||||
|
||||
if (blocked) {
|
||||
if (KeGetCurrentIrql() <= APC_LEVEL) {
|
||||
WCHAR db[512] = { 0 };
|
||||
UNICODE_STRING dev = { 0 };
|
||||
dev.Buffer = db;
|
||||
dev.MaximumLength = sizeof(db);
|
||||
QueryDevName(lower, &dev);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
#ifndef IOCTL_DISK_FORMAT_TRACKS
|
||||
#define IOCTL_DISK_FORMAT_TRACKS CTL_CODE(IOCTL_DISK_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#endif
|
||||
#ifndef IOCTL_DISK_FORMAT_TRACKS_EX
|
||||
#define IOCTL_DISK_FORMAT_TRACKS_EX CTL_CODE(IOCTL_DISK_BASE, 0x000b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#endif
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
ULONG IoControlCode = Data->Iopb->Parameters.DeviceIoControl.Common.IoControlCode;
|
||||
|
||||
if (IoControlCode == IOCTL_DISK_SET_DRIVE_LAYOUT_EX ||
|
||||
IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT ||
|
||||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS ||
|
||||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) {
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
|
||||
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include <fltKernel.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
#pragma comment(lib, "FltMgr.lib")
|
||||
|
||||
PFLT_FILTER g_FilterHandle = NULL;
|
||||
extern PDRIVER_OBJECT g_DriverObject;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
|
||||
// 定義標誌以便在不同 IRQL 下安全操作
|
||||
#define MAX_PATH_LEN 512
|
||||
|
||||
// 獲取進程名稱 (僅在確定需要攔截時調用,減少性能開銷)
|
||||
static VOID GetExeNameSafe(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || !exe->MaximumLength)
|
||||
return;
|
||||
|
||||
exe->Length = 0;
|
||||
|
||||
// 嘗試獲取短名稱 (最安全)
|
||||
PEPROCESS cur = PsGetCurrentProcess();
|
||||
PCSTR n = NULL;
|
||||
if (cur) n = PsGetProcessImageFileName(cur);
|
||||
|
||||
// 如果在 PASSIVE_LEVEL,嘗試獲取完整路徑 (用於精確白名單)
|
||||
// 注意:這裡依然有風險,但僅在攔截路徑執行
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, exe)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 降級方案:使用短名稱
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
|
||||
exe->Buffer[i] = (WCHAR)n[i];
|
||||
exe->Buffer[l] = 0;
|
||||
exe->Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
static BOOLEAN PreOpCheck(VOID)
|
||||
{
|
||||
if (g_Unloading)
|
||||
return FALSE;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static VOID PostOpRelease(VOID)
|
||||
{
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
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 BOOLEAN IsWriteAccess(PFLT_CALLBACK_DATA Data)
|
||||
{
|
||||
// 如果是寫入操作,直接返回
|
||||
if (Data->Iopb->MajorFunction == IRP_MJ_WRITE)
|
||||
return TRUE;
|
||||
|
||||
// 如果是設置信息 (已經通過 IsDeleteOrRename 過濾),視為寫入(修改元數據)
|
||||
if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION)
|
||||
return TRUE;
|
||||
|
||||
// 如果是創建/打開文件,檢查權限
|
||||
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
|
||||
ULONG access = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
|
||||
// 檢查是否包含寫入數據、追加數據、刪除、修改屬性等權限
|
||||
if (access & (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE |
|
||||
WRITE_DAC | WRITE_OWNER | GENERIC_WRITE | GENERIC_ALL)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 1. 基礎檢查 (Paging IO 不攔截)
|
||||
if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO)) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
if (!PreOpCheck())
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
// 2. 獲取 PID
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
ULONG upid = (ULONG)(ULONG_PTR)pid;
|
||||
|
||||
// 忽略 System (4) 和 Idle (0) 進程,避免系統崩潰
|
||||
if (upid == 0 || upid == 4) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// 3. 檢查操作類型 (僅關心 Create, Write, SetInfo)
|
||||
BOOLEAN checkFile = (Data->Iopb->MajorFunction == IRP_MJ_CREATE || Data->Iopb->MajorFunction == IRP_MJ_WRITE);
|
||||
BOOLEAN checkSetInfo = (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION);
|
||||
|
||||
if (!checkFile && !checkSetInfo) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
if (checkSetInfo && !IsDeleteOrRename(Data)) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// 4. 獲取文件名 (這一步最關鍵)
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
// 使用 QUERY_DEFAULT | NORMALIZED 確保獲取標準化路徑
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// 5. 邏輯判斷 (優化順序:文件名白名單 -> 黑名單 -> 操作檢查 -> 進程白名單)
|
||||
|
||||
// A. 全局文件白名單 (例如 Windows 目錄下的某些操作)
|
||||
if (IsWhitelist(&nameInfo->Name)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// B. 黑名單匹配
|
||||
BOOLEAN hitFile = MatchBlockFile(&nameInfo->Name);
|
||||
// 勒索防護:必須在敏感目錄 且 有敏感後綴 (如 .txt, .doc)
|
||||
// 註:Updater.bat 產生 .txt,應命中這裡
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&nameInfo->Name) && HasBlockedSuffix(&nameInfo->Name);
|
||||
|
||||
// 如果沒有命中任何規則,直接放行,節省資源
|
||||
if (!hitFile && !hitRansom) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// C. 檢查是否為寫入行為 (讀取不攔截)
|
||||
if (!IsWriteAccess(Data)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// D. 到了這裡,說明命中規則且正在寫入,準備攔截
|
||||
// 這時候才去獲取進程名稱,檢查進程白名單
|
||||
WCHAR exe_buffer[MAX_PATH_LEN] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = exe_buffer;
|
||||
exe.MaximumLength = sizeof(exe_buffer);
|
||||
|
||||
GetExeNameSafe(pid, &exe); // 獲取進程名
|
||||
|
||||
if (IsWhitelistExcept(&exe)) {
|
||||
// 進程在白名單中 (如 ntoskrnl, explorer 等)
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
// E. 執行攔截
|
||||
if (hitRansom) {
|
||||
LogAnsi3("RANSOM_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
if (hitFile) {
|
||||
LogAnsi3("FILE_BLOCK", upid, &exe, &nameInfo->Name);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Data->IoStatus.Information = 0;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
// 默認放行
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
PostOpRelease();
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!g_DriverObject)
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
static BOOLEAN IsHoneyToken(PUNICODE_STRING Path) {
|
||||
if (!Path || !Path->Buffer) return FALSE;
|
||||
if (WildcardMatch(L"*PYAS_Honey*", Path->Buffer, Path->Length) ||
|
||||
WildcardMatch(L"*Backup_Secret*", Path->Buffer, Path->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
UNICODE_STRING FileName = { 0 };
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
FileName = nameInfo->Name;
|
||||
if (!FileName.Buffer || FileName.Length == 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
BOOLEAN Trusted = IsProcessTrusted(Pid);
|
||||
|
||||
ULONG CreateDisposition = (Data->Iopb->Parameters.Create.Options >> 24) & 0xFF;
|
||||
BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE ||
|
||||
CreateDisposition == FILE_SUPERSEDE ||
|
||||
CreateDisposition == FILE_OVERWRITE ||
|
||||
CreateDisposition == FILE_OVERWRITE_IF ||
|
||||
CreateDisposition == FILE_OPEN_IF);
|
||||
|
||||
if (IsHoneyToken(&FileName)) {
|
||||
if (!Trusted) {
|
||||
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (FILE_WRITE_DATA | DELETE)) || IsCreateAction) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WildcardMatch(L"\\Device\\PhysicalDrive*", FileName.Buffer, FileName.Length) ||
|
||||
(WildcardMatch(L"\\Device\\Harddisk*", FileName.Buffer, FileName.Length) && !WildcardMatch(L"*Volume*", FileName.Buffer, FileName.Length))) {
|
||||
|
||||
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)) {
|
||||
|
||||
if (!Trusted && !IsInstallerProcess(Pid)) {
|
||||
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
|
||||
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckProtectedPathRule(&FileName)) {
|
||||
if (!Trusted) {
|
||||
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_DAC | GENERIC_WRITE)) || IsCreateAction) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckFileExtensionRule(&FileName)) {
|
||||
if (!Trusted) {
|
||||
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (DELETE | FILE_DELETE_CHILD)) {
|
||||
if (CheckRansomActivity(Pid, &FileName, NULL, 0, FALSE)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (nameInfo) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
return callbackStatus;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
|
||||
|
||||
if (infoClass != FileDispositionInformation &&
|
||||
infoClass != FileRenameInformation &&
|
||||
infoClass != FileRenameInformationEx) {
|
||||
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) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
|
||||
if (CheckProtectedPathRule(&nameInfo->Name)) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
if (CheckFileExtensionRule(&nameInfo->Name)) {
|
||||
if (CheckRansomActivity(Pid, &nameInfo->Name, NULL, 0, FALSE)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameInfo) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
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) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
SECURITY_INFORMATION SecurityInfo = Data->Iopb->Parameters.SetSecurity.SecurityInformation;
|
||||
|
||||
if (SecurityInfo & (OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) {
|
||||
|
||||
if (CheckProtectedPathRule(&nameInfo->Name)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
ULONG WriteLength = Data->Iopb->Parameters.Write.Length;
|
||||
if (WriteLength == 0) 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);
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (NT_SUCCESS(status) && nameInfo) {
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (NT_SUCCESS(status) && nameInfo->Name.Buffer) {
|
||||
|
||||
if (CheckFileExtensionRule(&nameInfo->Name)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
|
||||
PVOID SystemBuffer = NULL;
|
||||
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
|
||||
if (!Mdl) {
|
||||
if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
|
||||
if (NT_SUCCESS(FltLockUserBuffer(Data))) {
|
||||
Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Mdl) {
|
||||
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
|
||||
}
|
||||
|
||||
if (SystemBuffer) {
|
||||
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
|
||||
if (!IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
return callbackStatus;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode != FSCTL_MANAGE_BYPASS_IO) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
PFS_BPIO_INPUT InputBuffer = (PFS_BPIO_INPUT)Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer;
|
||||
if (!InputBuffer) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
__try {
|
||||
if (InputBuffer->Operation == FS_BPIO_OP_ENABLE) {
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) {
|
||||
if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) {
|
||||
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
|
||||
|
||||
Data->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_ImageNotifyEnabled = FALSE;
|
||||
|
||||
static BOOLEAN CheckVadExec(PVOID a)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
SIZE_T ret = 0;
|
||||
NTSTATUS s = ZwQueryVirtualMemory(NtCurrentProcess(), a, MemoryBasicInformation, &mbi, sizeof(mbi), &ret);
|
||||
|
||||
if (!NT_SUCCESS(s))
|
||||
return FALSE;
|
||||
if (mbi.State != MEM_COMMIT)
|
||||
return FALSE;
|
||||
if (mbi.Type == SEC_IMAGE)
|
||||
return FALSE;
|
||||
if (mbi.Type != MEM_PRIVATE && mbi.Type != MEM_MAPPED)
|
||||
return FALSE;
|
||||
return (mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
|
||||
}
|
||||
|
||||
static BOOLEAN WalkForShellcode(ULONG h)
|
||||
{
|
||||
#define MAXWALK 20
|
||||
PVOID frames[MAXWALK] = { 0 };
|
||||
ULONG c = RtlWalkFrameChain(frames, MAXWALK, 1);
|
||||
if (c == 0)
|
||||
return FALSE;
|
||||
|
||||
ULONG limit = RTL_NUMBER_OF(frames);
|
||||
ULONG count = c < limit ? c : limit;
|
||||
ULONG d = h < count ? h : count;
|
||||
|
||||
for (ULONG i = 0; i < d; i++) {
|
||||
PVOID p = frames[i];
|
||||
if (p && CheckVadExec(p))
|
||||
return TRUE;
|
||||
}
|
||||
for (ULONG i = 0; i < d; i++) {
|
||||
ULONG idx = count - 1 - i;
|
||||
if (idx >= d) {
|
||||
PVOID p = frames[idx];
|
||||
if (p && CheckVadExec(p))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static VOID ImageLoadNotify(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (ProcessId == (HANDLE)0 || ProcessId == (HANDLE)4)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = eb;
|
||||
exe.MaximumLength = sizeof(eb);
|
||||
exe.Length = 0;
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &exe)) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(eb) - 1; i++)
|
||||
eb[i] = (WCHAR)n[i];
|
||||
eb[l] = 0;
|
||||
exe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
if (exe.Length & 1) exe.Length--;
|
||||
|
||||
{
|
||||
size_t elen = exe.Length / sizeof(WCHAR);
|
||||
if ((elen >= 14 && _wcsnicmp(exe.Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
|
||||
(elen >= 8 && _wcsnicmp(exe.Buffer + elen - 8, L"pwsh.exe", 8) == 0) ||
|
||||
(elen >= 10 && _wcsnicmp(exe.Buffer + elen - 10, L"sppsvc.exe", 10) == 0)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (IsWhitelist(&exe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
if (FullImageName && FullImageName->Buffer) {
|
||||
if (MatchClrFromNonFramework(FullImageName)) {
|
||||
LogAnsi3("CLR_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, FullImageName);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (WalkForShellcode(8)) {
|
||||
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("SHELLCODE_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, &none);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitImageProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(s))
|
||||
g_ImageNotifyEnabled = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitImageProtect(VOID)
|
||||
{
|
||||
if (g_ImageNotifyEnabled) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static PVOID g_ObRegHandle = NULL;
|
||||
static POB_OPERATION_REGISTRATION g_ObOps = NULL;
|
||||
static OB_CALLBACK_REGISTRATION g_ObReg = { 0 };
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
if (g_Unloading)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info->Object)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE cur = PsGetCurrentProcessId();
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
HANDLE target = PsGetProcessId((PEPROCESS)Info->Object);
|
||||
if (target == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
ACCESS_MASK mask = PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE;
|
||||
PCSTR tn = PsGetProcessImageFileName((PEPROCESS)Info->Object);
|
||||
if (tn && (_stricmp(tn, "lsass.exe") == 0))
|
||||
mask |= PROCESS_VM_READ;
|
||||
|
||||
if ((*desired & mask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
WCHAR cbuf[512] = { 0 }, tbuf[512] = { 0 };
|
||||
UNICODE_STRING cexe = { 0 }, texe = { 0 };
|
||||
cexe.Buffer = cbuf; cexe.MaximumLength = sizeof(cbuf);
|
||||
texe.Buffer = tbuf; texe.MaximumLength = sizeof(tbuf);
|
||||
|
||||
if (logok)
|
||||
GetProcessImagePathByPid(cur, &cexe);
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK old = *desired;
|
||||
*desired &= ~mask;
|
||||
if ((*desired) != old && logok) {
|
||||
GetProcessImagePathByPid(target, &texe);
|
||||
if (cexe.Length == 0) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
cbuf[i] = (WCHAR)n[i];
|
||||
cbuf[l] = 0;
|
||||
cexe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
LogAnsi3("INJECT_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &texe);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreThreadCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Object == NULL)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE cur = PsGetCurrentProcessId();
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
HANDLE owner = PsGetThreadProcessId((PETHREAD)Info->Object);
|
||||
if (owner == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK tmask = THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
|
||||
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
if ((*desired & tmask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
WCHAR cbuf[512] = { 0 };
|
||||
UNICODE_STRING cexe = { 0 };
|
||||
cexe.Buffer = cbuf;
|
||||
cexe.MaximumLength = sizeof(cbuf);
|
||||
|
||||
if (logok)
|
||||
GetProcessImagePathByPid(cur, &cexe);
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK old = *desired;
|
||||
*desired &= ~tmask;
|
||||
if ((*desired) != old && logok) {
|
||||
if (cexe.Length == 0) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
cbuf[i] = (WCHAR)n[i];
|
||||
cbuf[l] = 0;
|
||||
cexe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
UNICODE_STRING tnone = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("THREAD_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &tnone);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitInjectProtect(VOID)
|
||||
{
|
||||
static UNICODE_STRING obAlt;
|
||||
RtlInitUnicodeString(&obAlt, L"385000");
|
||||
|
||||
g_ObOps = (POB_OPERATION_REGISTRATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OB_OPERATION_REGISTRATION) * 2, 'bOpr');
|
||||
if (!g_ObOps)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
RtlZeroMemory(g_ObOps, sizeof(OB_OPERATION_REGISTRATION) * 2);
|
||||
g_ObOps[0].ObjectType = PsProcessType;
|
||||
g_ObOps[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
g_ObOps[0].PreOperation = PreProcessCallback;
|
||||
g_ObOps[1].ObjectType = PsThreadType;
|
||||
g_ObOps[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
g_ObOps[1].PreOperation = PreThreadCallback;
|
||||
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
g_ObReg.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
g_ObReg.OperationRegistrationCount = 2;
|
||||
g_ObReg.Altitude = obAlt;
|
||||
g_ObReg.RegistrationContext = NULL;
|
||||
g_ObReg.OperationRegistration = g_ObOps;
|
||||
|
||||
NTSTATUS s = ObRegisterCallbacks(&g_ObReg, &g_ObRegHandle);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
|
||||
g_ObOps = NULL;
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitInjectProtect(VOID)
|
||||
{
|
||||
if (g_ObRegHandle) {
|
||||
ObUnRegisterCallbacks(g_ObRegHandle);
|
||||
g_ObRegHandle = NULL;
|
||||
}
|
||||
if (g_ObOps) {
|
||||
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
|
||||
g_ObOps = NULL;
|
||||
}
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#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)
|
||||
if (WildMatchN(str + i, n - i, pat + 2))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
SIZE_T i = 0;
|
||||
for (; i < n && str[i] != L'\\'; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 1))
|
||||
return TRUE;
|
||||
return WildMatchN(str + i, n - i, pat + 1);
|
||||
}
|
||||
if (*pat == L'?')
|
||||
return n > 0 && str[0] != L'\\' && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
if (n == 0)
|
||||
return FALSE;
|
||||
|
||||
WCHAR sc = str[0], pc = *pat;
|
||||
if (sc >= L'A' && sc <= L'Z')
|
||||
sc += 32;
|
||||
if (pc >= L'A' && pc <= L'Z')
|
||||
pc += 32;
|
||||
return sc == pc && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
}
|
||||
|
||||
static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!USOK(s) || !list)
|
||||
return FALSE;
|
||||
|
||||
SIZE_T s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
|
||||
static wchar_t* MatchListGetRule(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!USOK(s) || !list)
|
||||
return 0;
|
||||
|
||||
SIZE_T s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return pat;
|
||||
}
|
||||
else {
|
||||
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 BOOLEAN StrStrIW(const wchar_t* s, const wchar_t* sub)
|
||||
{
|
||||
if (!s || !sub)
|
||||
return FALSE;
|
||||
|
||||
SIZE_T n = wcslen(s), m = wcslen(sub);
|
||||
if (m == 0 || n < m)
|
||||
return FALSE;
|
||||
|
||||
for (SIZE_T i = 0; i + m <= n; i++) {
|
||||
if (_wcsnicmp(s + i, sub, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN EndsWithNameUS(PUNICODE_STRING s, const wchar_t* name)
|
||||
{
|
||||
if (!USOK(s) || !name)
|
||||
return FALSE;
|
||||
|
||||
SIZE_T n = s->Length / sizeof(WCHAR);
|
||||
SIZE_T m = wcslen(name);
|
||||
return n >= m && _wcsnicmp(s->Buffer + (n - m), name, m) == 0;
|
||||
}
|
||||
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
{
|
||||
if (USOK(key) && WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
|
||||
return FALSE;
|
||||
|
||||
if (USOK(exe) && IsWhitelist(exe))
|
||||
return FALSE;
|
||||
|
||||
if (USOK(exe) && USOK(key)) {
|
||||
if ((EndsWithNameUS(exe, L"services.exe") || EndsWithNameUS(exe, L"svchost.exe") || EndsWithNameUS(exe, L"sc.exe")) &&
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
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\\Internet Settings\\ZoneMap") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data")))
|
||||
return FALSE;
|
||||
|
||||
if ((EndsWithNameUS(exe, L"msiexec.exe") || EndsWithNameUS(exe, L"TrustedInstaller.exe")) &&
|
||||
(WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, key->Length / sizeof(WCHAR), L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**")))
|
||||
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)) {
|
||||
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'?') && _wcsnicmp(valueName->Buffer, pat, wcslen(pat)) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
|
||||
{
|
||||
PEPROCESS process = NULL;
|
||||
PVOID buf = NULL;
|
||||
HANDLE h = NULL;
|
||||
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)
|
||||
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;
|
||||
if (image->Buffer && image->Length > 0 && ProcessImagePath->MaximumLength >= image->Length + sizeof(WCHAR)) {
|
||||
RtlCopyMemory(ProcessImagePath->Buffer, image->Buffer, image->Length);
|
||||
ProcessImagePath->Length = image->Length;
|
||||
ProcessImagePath->Buffer[image->Length / sizeof(WCHAR)] = 0;
|
||||
status = TRUE;
|
||||
}
|
||||
else {
|
||||
status = 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))
|
||||
return FALSE;
|
||||
return MatchList(s, g_Whitelist);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockReg);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockReg);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockFile(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockRansom(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockRansom);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s)
|
||||
{
|
||||
if (!USOK(s)) {
|
||||
return FALSE;
|
||||
}
|
||||
for (SIZE_T i = 0; g_Blocksuffix[i]; ++i) {
|
||||
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;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(cmd))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = cmd->Buffer;
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsHttp[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i]))
|
||||
return TRUE;
|
||||
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsGeneric[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsGeneric[i]))
|
||||
return TRUE;
|
||||
|
||||
if (USOK(img) && EndsWithNameUS(img, L"cmd.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromCmdExe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromCmdExe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"rundll32.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromRundll32Exe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromRundll32Exe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"regsvr32.exe") && 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]))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(img))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = img->Buffer;
|
||||
if (StrStrIW(s, L"\\Users\\")) {
|
||||
for (SIZE_T i = 0; g_ScreenUserSubdirs[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenUserSubdirs[i]))
|
||||
return TRUE;
|
||||
}
|
||||
for (SIZE_T i = 0; g_ScreenOtherProcNeedles[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenOtherProcNeedles[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName)
|
||||
{
|
||||
if (!USOK(fullImageName))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = fullImageName->Buffer;
|
||||
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;
|
||||
|
||||
return MatchList(img, g_WhitelistExcept);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
|
||||
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 IsWhitelistExcept(PUNICODE_STRING img);
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s);
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe);
|
||||
|
||||
BOOLEAN MatchBlockFile(PUNICODE_STRING s);
|
||||
BOOLEAN MatchBlockRansom(PUNICODE_STRING s);
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s);
|
||||
|
||||
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img);
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img);
|
||||
|
||||
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod);
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img);
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName);
|
||||
@@ -1,125 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
typedef union _PS_PROTECTION {
|
||||
UCHAR Level;
|
||||
struct {
|
||||
UCHAR Type : 3;
|
||||
UCHAR Audit : 1;
|
||||
UCHAR Signer : 4;
|
||||
} Flags;
|
||||
} PS_PROTECTION, * PPS_PROTECTION;
|
||||
|
||||
static PVOID g_PsSetProcessProtection = NULL;
|
||||
static HANDLE g_ProtectedPid = NULL;
|
||||
static PVOID g_ObCookie = NULL;
|
||||
|
||||
static NTSTATUS SetPPL31(_In_ PEPROCESS proc)
|
||||
{
|
||||
if (!proc || !g_PsSetProcessProtection)
|
||||
return STATUS_PROCEDURE_NOT_FOUND;
|
||||
|
||||
PS_PROTECTION p = { 0 };
|
||||
p.Level = 0x31;
|
||||
((VOID(NTAPI*)(PEPROCESS, PS_PROTECTION))g_PsSetProcessProtection)(proc, p);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS ObPreOperation(
|
||||
_In_ PVOID RegistrationContext,
|
||||
_Inout_ POB_PRE_OPERATION_INFORMATION info
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (!info || info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID ProcessNotify(
|
||||
_In_opt_ PEPROCESS Process,
|
||||
_In_ HANDLE Pid,
|
||||
_In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
|
||||
)
|
||||
{
|
||||
UNICODE_STRING target = RTL_CONSTANT_STRING(L"\\??\\C:\\PYAS.exe");
|
||||
if (CreateInfo) {
|
||||
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 && Pid == g_ProtectedPid) {
|
||||
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS InitProcessProtect(VOID)
|
||||
{
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"PsSetProcessProtection");
|
||||
g_PsSetProcessProtection = MmGetSystemRoutineAddress(&n);
|
||||
|
||||
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(ProcessNotify, FALSE);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ObUnRegisterCallbacks(g_ObCookie);
|
||||
g_ObCookie = NULL;
|
||||
return s;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID UninitProcessProtect(VOID)
|
||||
{
|
||||
if (g_ObCookie) {
|
||||
ObUnRegisterCallbacks(g_ObCookie);
|
||||
g_ObCookie = NULL;
|
||||
}
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotify, TRUE);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
PVOID ObRegistrationHandle = NULL;
|
||||
OB_CALLBACK_REGISTRATION ObRegistration;
|
||||
OB_OPERATION_REGISTRATION ObCallbacks[1];
|
||||
|
||||
static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
|
||||
if (!FullImageName || !FullImageName->Buffer) return FALSE;
|
||||
|
||||
if (WildcardMatch(L"*\\Windows\\System32\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\SysWOW64\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\WinSxS\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\Microsoft.NET\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Common Files\\Microsoft Shared\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Program Files*\\*", FullImageName->Buffer, FullImageName->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) {
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return;
|
||||
if (!FullImageName || !FullImageName->Buffer) return;
|
||||
if (ProcessId == (HANDLE)0) return;
|
||||
|
||||
if (IsSystemImage(FullImageName)) return;
|
||||
|
||||
if (IsTargetProtected(ProcessId)) {
|
||||
if (CheckFileExtensionRule(FullImageName)) {
|
||||
SendMessageToUser(6001, (ULONG)(ULONG_PTR)ProcessId, FullImageName->Buffer, FullImageName->Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
|
||||
NTSTATUS status;
|
||||
BOOLEAN isBlacklisted = FALSE;
|
||||
PEPROCESS Process = NULL;
|
||||
PUNICODE_STRING imageFileName = NULL;
|
||||
|
||||
status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(status)) return FALSE;
|
||||
|
||||
status = SeLocateProcessImageName(Process, &imageFileName);
|
||||
|
||||
if (NT_SUCCESS(status) && imageFileName) {
|
||||
if (imageFileName->Buffer) {
|
||||
if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) {
|
||||
isBlacklisted = TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool(imageFileName);
|
||||
}
|
||||
|
||||
ObDereferenceObject(Process);
|
||||
return isBlacklisted;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
|
||||
|
||||
PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;
|
||||
if (!TargetProcess) return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE TargetPid = PsGetProcessId(TargetProcess);
|
||||
HANDLE SourcePid = PsGetCurrentProcessId();
|
||||
|
||||
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
|
||||
|
||||
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
|
||||
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
|
||||
|
||||
BOOLEAN bProtected = IsTargetProtected(TargetPid);
|
||||
|
||||
if (bProtected) {
|
||||
BOOLEAN bIsInstaller = IsInstallerProcess(SourcePid);
|
||||
|
||||
if (bIsInstaller) {
|
||||
if (IsBlacklistedAdminTool(SourcePid)) {
|
||||
bIsInstaller = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsInstaller) return OB_PREOP_SUCCESS;
|
||||
|
||||
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
|
||||
PROCESS_VM_OPERATION |
|
||||
PROCESS_VM_WRITE |
|
||||
PROCESS_CREATE_THREAD |
|
||||
PROCESS_VM_READ |
|
||||
PROCESS_DUP_HANDLE |
|
||||
PROCESS_SUSPEND_RESUME |
|
||||
PROCESS_SET_INFORMATION |
|
||||
PROCESS_SET_QUOTA;
|
||||
|
||||
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~DenyMask;
|
||||
OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~DenyMask;
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
|
||||
}
|
||||
}
|
||||
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitializeProcessProtection() {
|
||||
static UNICODE_STRING Altitude = { 0 };
|
||||
RtlInitUnicodeString(&Altitude, L"320000");
|
||||
|
||||
ObCallbacks[0].ObjectType = PsProcessType;
|
||||
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ObCallbacks[0].PreOperation = PreOpenProcess;
|
||||
ObCallbacks[0].PostOperation = NULL;
|
||||
|
||||
ObRegistration.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
ObRegistration.OperationRegistrationCount = 1;
|
||||
ObRegistration.Altitude = Altitude;
|
||||
ObRegistration.RegistrationContext = NULL;
|
||||
ObRegistration.OperationRegistration = ObCallbacks;
|
||||
|
||||
return ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
|
||||
}
|
||||
|
||||
VOID UninitializeProcessProtection() {
|
||||
if (ObRegistrationHandle) {
|
||||
ObUnRegisterCallbacks(ObRegistrationHandle);
|
||||
ObRegistrationHandle = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath, POBJECT_NAME_INFORMATION* OutInfo)
|
||||
{
|
||||
ULONG len = 0;
|
||||
NTSTATUS s = ObQueryNameString(KeyObject, NULL, 0, &len);
|
||||
if (s != STATUS_INFO_LENGTH_MISMATCH)
|
||||
return FALSE;
|
||||
|
||||
POBJECT_NAME_INFORMATION p = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'rgeR');
|
||||
if (!p)
|
||||
return FALSE;
|
||||
|
||||
s = ObQueryNameString(KeyObject, p, len, &len);
|
||||
if (!NT_SUCCESS(s) || !p->Name.Buffer || p->Name.Length == 0) {
|
||||
ExFreePool2(p, 'rgeR', NULL, 0);
|
||||
return FALSE;
|
||||
}
|
||||
*OutInfo = p;
|
||||
OutPath->Buffer = p->Name.Buffer;
|
||||
OutPath->Length = p->Name.Length;
|
||||
OutPath->MaximumLength = p->Name.Length;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ctx);
|
||||
if (g_Unloading)
|
||||
return STATUS_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
REG_NOTIFY_CLASS type = (REG_NOTIFY_CLASS)(ULONG_PTR)arg1;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
UNICODE_STRING checkReg = { 0 };
|
||||
POBJECT_NAME_INFORMATION nameInfo = NULL;
|
||||
|
||||
UNICODE_STRING exe = { 0 };
|
||||
WCHAR exeBuf[260] = { 0 };
|
||||
exe.Buffer = exeBuf;
|
||||
exe.MaximumLength = sizeof(exeBuf);
|
||||
exe.Length = 0;
|
||||
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
if (pid == (HANDLE)0 || pid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
PUNICODE_STRING valueName = NULL;
|
||||
BOOLEAN need_log = FALSE;
|
||||
BOOLEAN canQuery = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
if (type == RegNtPreSetValueKey) {
|
||||
PREG_SET_VALUE_KEY_INFORMATION s = (PREG_SET_VALUE_KEY_INFORMATION)arg2;
|
||||
valueName = s->ValueName;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(s->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreDeleteValueKey) {
|
||||
PREG_DELETE_VALUE_KEY_INFORMATION d = (PREG_DELETE_VALUE_KEY_INFORMATION)arg2;
|
||||
valueName = d->ValueName;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreDeleteKey) {
|
||||
PREG_DELETE_KEY_INFORMATION d = (PREG_DELETE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreRenameKey) {
|
||||
PREG_RENAME_KEY_INFORMATION r = (PREG_RENAME_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(r->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreCreateKeyEx) {
|
||||
PREG_CREATE_KEY_INFORMATION c = (PREG_CREATE_KEY_INFORMATION)arg2;
|
||||
if (c->CompleteName)
|
||||
checkReg = *c->CompleteName;
|
||||
}
|
||||
else if (type == RegNtPreReplaceKey) {
|
||||
PREG_REPLACE_KEY_INFORMATION rk = (PREG_REPLACE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(rk->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreSaveKey) {
|
||||
PREG_SAVE_KEY_INFORMATION sk = (PREG_SAVE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(sk->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreRestoreKey) {
|
||||
PREG_RESTORE_KEY_INFORMATION res = (PREG_RESTORE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(res->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreLoadKey) {
|
||||
PREG_LOAD_KEY_INFORMATION lk = (PREG_LOAD_KEY_INFORMATION)arg2;
|
||||
if (lk->KeyName)
|
||||
checkReg = *lk->KeyName;
|
||||
}
|
||||
|
||||
if (checkReg.Buffer || (valueName && valueName->Buffer)) {
|
||||
if (canQuery)
|
||||
GetProcessImagePathByPid(pid, &exe);
|
||||
if (IsRegistryBlock(&checkReg, valueName, &exe)) {
|
||||
need_log = TRUE;
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
if (nameInfo)
|
||||
ExFreePool2(nameInfo, 'rgeR', NULL, 0);
|
||||
if (need_log)
|
||||
LogAnsi3("REG_BLOCK", (ULONG)(ULONG_PTR)pid, &exe, &checkReg);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
LARGE_INTEGER Cookie;
|
||||
|
||||
static BOOLEAN IsBamRegistryPath(PCUNICODE_STRING KeyName) {
|
||||
if (!KeyName || !KeyName->Buffer) return FALSE;
|
||||
if (WildcardMatch(L"*\\Services\\bam\\*", KeyName->Buffer, KeyName->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsWriteAccess(ACCESS_MASK Access) {
|
||||
if (Access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK |
|
||||
DELETE | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static PWCHAR GetFullPath(PVOID RootObject, PUNICODE_STRING CompleteName, PULONG OutLength) {
|
||||
PWCHAR Buffer = NULL;
|
||||
ULONG TotalSize = 0;
|
||||
PCUNICODE_STRING RootName = NULL;
|
||||
BOOLEAN NeedFreeRootName = FALSE;
|
||||
|
||||
if (RootObject) {
|
||||
NTSTATUS status = CmCallbackGetKeyObjectIDEx(&Cookie, RootObject, NULL, &RootName, 0);
|
||||
if (NT_SUCCESS(status) && RootName) {
|
||||
NeedFreeRootName = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
ULONG RootLen = (RootName && RootName->Buffer) ? RootName->Length : 0;
|
||||
ULONG RelLen = (CompleteName && CompleteName->Buffer) ? CompleteName->Length : 0;
|
||||
|
||||
TotalSize = RootLen + sizeof(WCHAR) + RelLen + sizeof(WCHAR);
|
||||
|
||||
Buffer = (PWCHAR)PyasAllocate(TotalSize);
|
||||
if (!Buffer) {
|
||||
if (NeedFreeRootName) CmCallbackReleaseKeyObjectIDEx(RootName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RtlZeroMemory(Buffer, TotalSize);
|
||||
|
||||
PWCHAR Current = Buffer;
|
||||
if (RootLen > 0) {
|
||||
RtlCopyMemory(Current, RootName->Buffer, RootLen);
|
||||
Current += (RootLen / sizeof(WCHAR));
|
||||
if (Current > Buffer && *(Current - 1) != L'\\') {
|
||||
*Current = L'\\';
|
||||
Current++;
|
||||
}
|
||||
}
|
||||
|
||||
if (RelLen > 0) {
|
||||
if (RootLen > 0 && CompleteName->Buffer[0] == L'\\') {
|
||||
RtlCopyMemory(Current, CompleteName->Buffer + 1, RelLen - sizeof(WCHAR));
|
||||
}
|
||||
else {
|
||||
RtlCopyMemory(Current, CompleteName->Buffer, RelLen);
|
||||
}
|
||||
}
|
||||
|
||||
if (NeedFreeRootName) CmCallbackReleaseKeyObjectIDEx(RootName);
|
||||
|
||||
if (OutLength) *OutLength = (ULONG)wcslen(Buffer) * sizeof(WCHAR);
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) {
|
||||
UNREFERENCED_PARAMETER(CallbackContext);
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_SUCCESS;
|
||||
|
||||
REG_NOTIFY_CLASS Class = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
|
||||
if (Class == RegNtPreCreateKey || Class == RegNtPreOpenKey || Class == RegNtPreOpenKeyEx) {
|
||||
PVOID RootObject = NULL;
|
||||
PUNICODE_STRING CompleteName = NULL;
|
||||
ACCESS_MASK DesiredAccess = 0;
|
||||
|
||||
if (Class == RegNtPreCreateKey) {
|
||||
REG_CREATE_KEY_INFORMATION* Info = (REG_CREATE_KEY_INFORMATION*)Argument2;
|
||||
RootObject = Info->RootObject;
|
||||
CompleteName = Info->CompleteName;
|
||||
DesiredAccess = Info->DesiredAccess;
|
||||
}
|
||||
else {
|
||||
REG_OPEN_KEY_INFORMATION* Info = (REG_OPEN_KEY_INFORMATION*)Argument2;
|
||||
RootObject = Info->RootObject;
|
||||
CompleteName = Info->CompleteName;
|
||||
DesiredAccess = Info->DesiredAccess;
|
||||
}
|
||||
|
||||
if (!IsWriteAccess(DesiredAccess)) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ULONG PathLen = 0;
|
||||
PWCHAR FullPathBuffer = GetFullPath(RootObject, CompleteName, &PathLen);
|
||||
|
||||
if (FullPathBuffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
RtlInitUnicodeString(&FullPath, FullPathBuffer);
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
PyasFree(FullPathBuffer);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (Class == RegNtPreDeleteKey) {
|
||||
REG_DELETE_KEY_INFORMATION* Info = (REG_DELETE_KEY_INFORMATION*)Argument2;
|
||||
PCUNICODE_STRING KeyName = NULL;
|
||||
|
||||
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
|
||||
if (KeyName) {
|
||||
if (CheckRegistryRule(KeyName)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (Class == RegNtPreSetValueKey) {
|
||||
REG_SET_VALUE_KEY_INFORMATION* Info = (REG_SET_VALUE_KEY_INFORMATION*)Argument2;
|
||||
PCUNICODE_STRING KeyName = NULL;
|
||||
|
||||
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
|
||||
if (KeyName && KeyName->Buffer) {
|
||||
if (!IsBamRegistryPath(KeyName)) {
|
||||
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
|
||||
(Info->ValueName ? Info->ValueName->Length : 0) +
|
||||
sizeof(WCHAR);
|
||||
|
||||
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
|
||||
if (Buffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
FullPath.Buffer = Buffer;
|
||||
FullPath.Length = 0;
|
||||
FullPath.MaximumLength = (USHORT)FullSize;
|
||||
|
||||
RtlCopyUnicodeString(&FullPath, KeyName);
|
||||
|
||||
if (Info->ValueName && Info->ValueName->Length > 0) {
|
||||
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
|
||||
RtlAppendUnicodeToString(&FullPath, L"\\");
|
||||
}
|
||||
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
|
||||
}
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
|
||||
BOOLEAN IsCriticalReg = FALSE;
|
||||
if (WildcardMatch(L"*\\MACHINE\\SAM*", FullPath.Buffer, FullPath.Length) ||
|
||||
WildcardMatch(L"*\\MACHINE\\SECURITY*", FullPath.Buffer, FullPath.Length) ||
|
||||
WildcardMatch(L"*\\MACHINE\\SYSTEM*", FullPath.Buffer, FullPath.Length) ||
|
||||
WildcardMatch(L"*\\MACHINE\\BCD00000000*", FullPath.Buffer, FullPath.Length)) {
|
||||
if (IsCriticalSystemProcess(Pid)) {
|
||||
IsCriticalReg = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsCriticalReg) {
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
}
|
||||
}
|
||||
CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (Class == RegNtPreDeleteValueKey) {
|
||||
REG_DELETE_VALUE_KEY_INFORMATION* Info = (REG_DELETE_VALUE_KEY_INFORMATION*)Argument2;
|
||||
PCUNICODE_STRING KeyName = NULL;
|
||||
|
||||
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
|
||||
if (KeyName && KeyName->Buffer) {
|
||||
if (!IsBamRegistryPath(KeyName)) {
|
||||
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
|
||||
(Info->ValueName ? Info->ValueName->Length : 0) +
|
||||
sizeof(WCHAR);
|
||||
|
||||
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
|
||||
if (Buffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
FullPath.Buffer = Buffer;
|
||||
FullPath.Length = 0;
|
||||
FullPath.MaximumLength = (USHORT)FullSize;
|
||||
|
||||
RtlCopyUnicodeString(&FullPath, KeyName);
|
||||
|
||||
if (Info->ValueName && Info->ValueName->Length > 0) {
|
||||
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
|
||||
RtlAppendUnicodeToString(&FullPath, L"\\");
|
||||
}
|
||||
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
|
||||
}
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
}
|
||||
}
|
||||
CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject) {
|
||||
UNICODE_STRING Altitude;
|
||||
RtlInitUnicodeString(&Altitude, L"320000");
|
||||
return CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
|
||||
}
|
||||
|
||||
VOID UninitializeRegistryProtection() {
|
||||
if (Cookie.QuadPart != 0) {
|
||||
CmUnRegisterCallback(Cookie);
|
||||
Cookie.QuadPart = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_RemoteNotifyEnabled = FALSE;
|
||||
|
||||
static VOID ProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Process);
|
||||
UNREFERENCED_PARAMETER(ProcessId);
|
||||
|
||||
if (!CreateInfo)
|
||||
return;
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
|
||||
HANDLE ppid = CreateInfo->CreatingThreadId.UniqueProcess;
|
||||
if (ppid == (HANDLE)0 || ppid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
UNICODE_STRING parent = { 0 };
|
||||
WCHAR pbuf[512] = { 0 };
|
||||
parent.Buffer = pbuf;
|
||||
parent.MaximumLength = sizeof(pbuf);
|
||||
|
||||
if (!GetProcessImagePathByPid(ppid, &parent)) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
|
||||
pbuf[i] = (WCHAR)n[i];
|
||||
pbuf[l] = 0;
|
||||
parent.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
if (IsWhitelist(&parent)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
UNICODE_STRING img = { 0 };
|
||||
if (CreateInfo->ImageFileName)
|
||||
img = *CreateInfo->ImageFileName;
|
||||
UNICODE_STRING cmd = { 0 };
|
||||
if (CreateInfo->CommandLine)
|
||||
cmd = *CreateInfo->CommandLine;
|
||||
|
||||
BOOLEAN suspect = FALSE;
|
||||
if (MatchRemoteSuspectBin(&img) && MatchRemoteCommand(&cmd, &img))
|
||||
suspect = TRUE;
|
||||
|
||||
if (suspect) {
|
||||
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
|
||||
if (cmd.Buffer && cmd.Length)
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &cmd);
|
||||
else if (img.Buffer && img.Length)
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &img);
|
||||
else {
|
||||
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &none);
|
||||
}
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitRemoteProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
|
||||
if (NT_SUCCESS(s))
|
||||
g_RemoteNotifyEnabled = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitRemoteProtect(VOID)
|
||||
{
|
||||
if (g_RemoteNotifyEnabled) {
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, TRUE);
|
||||
g_RemoteNotifyEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "ProtectRules.h"
|
||||
|
||||
wchar_t* g_Whitelist[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Program*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\**",
|
||||
L"**\\PYAS*.exe",
|
||||
L"Registry",
|
||||
L"vmmem*",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_WhitelistExcept[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\vds.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdedit.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\schtasks.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\ntoskrnl.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wbadmin.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bcdboot.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\dism.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\bootsect.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\net1.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\msiexec.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\lsass.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\csrss.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\smss.exe",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\Sys*\\wininit.exe",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_AttachDisk[] = {
|
||||
L"\\FileSystem\\Ntfs",
|
||||
L"\\FileSystem\\Refs",
|
||||
L"\\FileSystem\\Fastfat",
|
||||
L"\\FileSystem\\exfat",
|
||||
L"\\FileSystem\\Udfs",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_BlockFile[] = {
|
||||
L"\\Device\\Harddisk*\\DR*",
|
||||
L"\\Device\\Harddisk*\\Partition\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Boot\\**",
|
||||
L"\\Device\\HarddiskVolume*\\EFI\\**",
|
||||
L"\\Device\\HarddiskVolume*\\bootmgr",
|
||||
L"\\Device\\HarddiskVolume*\\Recovery\\**",
|
||||
L"\\Device\\HarddiskVolume*\\System Volume Information\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\**",
|
||||
L"\\Device\\HarddiskVolume*\\$*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\ProgramData\\PYAS\\**",
|
||||
L"**\\PYAS.exe",
|
||||
L"**\\CON**",
|
||||
L"**\\PRN**",
|
||||
L"**\\AUX**",
|
||||
L"**\\NUL**",
|
||||
L"**\\COM*\\**",
|
||||
L"**\\LPT*\\**",
|
||||
L"**\\evil*.exe",
|
||||
L"**\\OSDATA**",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_BlockRansom[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Desktop\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Downloads\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Documents\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Music\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Pictures\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\Videos\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Users\\*\\OneDrive\\**",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_Blocksuffix[] = {
|
||||
L".exe", L".dll", L".sys", L".com", L".scr",
|
||||
L".zip", L".7z", L".rar", L".tar", L".gz",
|
||||
L".js", L".bat", L".cmd", L".ps1", L".vbs",
|
||||
L".ppt", L".pptx", L".wps", L".txt", L".rtf",
|
||||
L".pdf", L".xls", L".xlsx", L".doc", L".docx",
|
||||
L".jpg", L".jpeg", L".png", L".webp", L".gif",
|
||||
L".mp3", L".wav", L".aac", L".ogg", L".flac",
|
||||
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
|
||||
L".aux", L".cur", L".mui", L".ttf", L".efi",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_BlockReg[] = {
|
||||
L"\\REGISTRY\\MACHINE\\BCD00000000\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SAM\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SECURITY\\**",
|
||||
L"\\REGISTRY\\USER\\*_Classes\\*\\shell\\open\\command\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\NetWire\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\Remcos*\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\DC3_FEXEC\\**",
|
||||
L"DisableAntiSpyware",
|
||||
L"DisableWindowsUpdateAccess",
|
||||
L"EnableLUA",
|
||||
L"ConsentPromptBehaviorAdmin",
|
||||
L"NoControlPanel",
|
||||
L"NoDrives",
|
||||
L"NoFileMenu",
|
||||
L"NoFind",
|
||||
L"NoStartMenuPinnedList",
|
||||
L"NoSetFolders",
|
||||
L"NoSetFolderOptions",
|
||||
L"NoViewOnDrive",
|
||||
L"NoClose",
|
||||
L"NoDesktop",
|
||||
L"NoLogoff",
|
||||
L"NoFolderOptions",
|
||||
L"RestrictRun",
|
||||
L"NoViewContextMenu",
|
||||
L"HideClock",
|
||||
L"NoStartMenuMyGames",
|
||||
L"NoStartMenuMyMusic",
|
||||
L"DisableCMD",
|
||||
L"NoAddingComponents",
|
||||
L"NoWinKeys",
|
||||
L"NoStartMenuLogOff",
|
||||
L"NoSimpleNetIDList",
|
||||
L"NoLowDiskSpaceChecks",
|
||||
L"DisableLockWorkstation",
|
||||
L"Restrict_Run",
|
||||
L"DisableTaskMgr",
|
||||
L"DisableRegistryTools",
|
||||
L"DisableChangePassword",
|
||||
L"Wallpaper",
|
||||
L"NoComponents",
|
||||
L"NoStartMenuMorePrograms",
|
||||
L"NoActiveDesktop",
|
||||
L"NoSetActiveDesktop",
|
||||
L"NoRecentDocsMenu",
|
||||
L"NoWindowsUpdate",
|
||||
L"NoChangeStartMenu",
|
||||
L"NoFavoritesMenu",
|
||||
L"NoRecentDocsHistory",
|
||||
L"NoSetTaskbar",
|
||||
L"NoSMHelp",
|
||||
L"NoTrayContextMenu",
|
||||
L"NoManageMyComputerVerb",
|
||||
L"NoRealMode",
|
||||
L"NoRun",
|
||||
L"ClearRecentDocsOnExit",
|
||||
L"NoActiveDesktopChanges",
|
||||
L"NoStartMenuNetworkPlaces",
|
||||
L"NoLockScreen",
|
||||
L"HideFastUserSwitching",
|
||||
L"ConsentPromptBehaviorUser",
|
||||
L"EnableSecureUIAPaths",
|
||||
L"NoSRM",
|
||||
L"NoInteractiveServices",
|
||||
L"ShutdownWithoutLogon",
|
||||
L"AutoAdminLogon",
|
||||
L"ImagePath",
|
||||
L"BootExecute",
|
||||
L"SubSystems",
|
||||
L"FirmwareBootDevice",
|
||||
L"BootDriverFlags",
|
||||
L"SystemStartOptions",
|
||||
L"CrashDumpEnabled",
|
||||
L"DisableAutomaticRestartOnFailure",
|
||||
L"SystemBiosVersion",
|
||||
L"NoTheme",
|
||||
L"PendingFileRenameOperations",
|
||||
L"SystemSetupInProgress",
|
||||
L"CmdLine",
|
||||
L"SetupType",
|
||||
L"AutoReboot",
|
||||
L"Userinit",
|
||||
L"UIHost",
|
||||
L"Debugger",
|
||||
L"DefaultUserName",
|
||||
L"DefaultPassword",
|
||||
L"AltDefaultUserName",
|
||||
L"AltDefaultPassword",
|
||||
L"DisableCAD",
|
||||
L"LegalNoticeCaption",
|
||||
L"LegalNoticeText",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteSuspectBins[] = {
|
||||
L"powershell.exe",
|
||||
L"pwsh.exe",
|
||||
L"wscript.exe",
|
||||
L"cscript.exe",
|
||||
L"mshta.exe",
|
||||
L"rundll32.exe",
|
||||
L"regsvr32.exe",
|
||||
L"cmd.exe",
|
||||
L"bitsadmin.exe",
|
||||
L"msbuild.exe",
|
||||
L"installutil.exe",
|
||||
L"wmic.exe",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdIndicatorsHttp[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdIndicatorsGeneric[] = {
|
||||
L"-enc",
|
||||
L"-e",
|
||||
L"-encodedcommand",
|
||||
L"frombase64string(",
|
||||
L"iex(",
|
||||
L"downloadstring(",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdFromCmdExe[] = {
|
||||
L"powershell",
|
||||
L"pwsh",
|
||||
L"mshta",
|
||||
L"rundll32",
|
||||
L"wscript",
|
||||
L"cscript",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdFromRundll32Exe[] = {
|
||||
L".dll,",
|
||||
L"javascript:",
|
||||
L"http",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdRegsvr32Need[] = {
|
||||
L"/i:",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_RemoteCmdFromMshtaExe[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_ScreenCapModules[] = {
|
||||
L"windows.graphics.capture.dll",
|
||||
L"dxgi.dll",
|
||||
L"d3d11.dll",
|
||||
L"dwmapi.dll",
|
||||
L"dcomp.dll",
|
||||
L"mfreadwrite.dll",
|
||||
L"mfplat.dll",
|
||||
L"opengl32.dll",
|
||||
L"gdi32full.dll",
|
||||
L"win32u.dll",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_ScreenUserSubdirs[] = {
|
||||
L"\\AppData\\",
|
||||
L"\\Temp\\",
|
||||
L"\\Downloads\\",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_ScreenOtherProcNeedles[] = {
|
||||
L"\\ProgramData\\",
|
||||
NULL
|
||||
};
|
||||
@@ -0,0 +1,791 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
constexpr auto MAX_TRACKERS = 64;
|
||||
constexpr auto RANSOM_TIME_WINDOW_MS = 3000;
|
||||
constexpr auto RANSOM_COUNT_THRESHOLD = 5;
|
||||
constexpr auto HIGH_ENTROPY_THRESHOLD = 10;
|
||||
|
||||
constexpr auto TRUST_CACHE_SIZE = 128;
|
||||
constexpr auto TRUST_CACHE_TTL_SEC = 300;
|
||||
|
||||
typedef struct _RANSOM_TRACKER {
|
||||
HANDLE ProcessId;
|
||||
ULONG ActivityCount;
|
||||
LARGE_INTEGER LastActivityTime;
|
||||
} RANSOM_TRACKER, * PRANSOM_TRACKER;
|
||||
|
||||
typedef struct _TRUST_CACHE_ENTRY {
|
||||
HANDLE ProcessId;
|
||||
BOOLEAN IsTrusted;
|
||||
LARGE_INTEGER CacheTime;
|
||||
} TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY;
|
||||
|
||||
RANSOM_TRACKER RansomTrackers[MAX_TRACKERS];
|
||||
TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE];
|
||||
FAST_MUTEX TrustCacheLock;
|
||||
|
||||
static BOOLEAN g_CacheInitialized = FALSE;
|
||||
|
||||
ERESOURCE RuleLock;
|
||||
PRULE_NODE g_RegistryBlockList = NULL;
|
||||
PRULE_NODE g_RegistryTrustedList = NULL;
|
||||
PRULE_NODE g_ProcessTrustedPaths = NULL;
|
||||
PRULE_NODE g_ProcessExploitable = NULL;
|
||||
PRULE_NODE g_FileProtectedPaths = NULL;
|
||||
PRULE_NODE g_FileExceptionPaths = NULL;
|
||||
PRULE_NODE g_FileRansomExts = NULL;
|
||||
|
||||
const PCWSTR Helper_NaturallyCompressedExtensions[] = {
|
||||
L".zip", L".7z", L".rar", L".tar", L".gz",
|
||||
L".jpg", L".jpeg", L".png", L".webp", L".gif",
|
||||
L".mp3", L".wav", L".aac", L".ogg", L".flac",
|
||||
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
|
||||
L".docx", L".xlsx", L".pptx", L".pdf", L".wps",
|
||||
L".apk", L".jar", L".class", L".db", L".sqlite"
|
||||
};
|
||||
|
||||
static VOID FreeList(PRULE_NODE* Head) {
|
||||
PRULE_NODE Current = *Head;
|
||||
while (Current) {
|
||||
PRULE_NODE Next = Current->Next;
|
||||
if (Current->Pattern.Buffer) PyasFree(Current->Pattern.Buffer);
|
||||
PyasFree(Current);
|
||||
Current = Next;
|
||||
}
|
||||
*Head = NULL;
|
||||
}
|
||||
|
||||
static VOID AddRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
|
||||
if (!RuleStr || !RuleStr->Buffer) return;
|
||||
|
||||
PRULE_NODE Node = (PRULE_NODE)PyasAllocate(sizeof(RULE_NODE));
|
||||
if (!Node) return;
|
||||
|
||||
SIZE_T Size = RuleStr->Length + sizeof(WCHAR);
|
||||
Node->Pattern.Buffer = (PWCHAR)PyasAllocate(Size);
|
||||
if (!Node->Pattern.Buffer) {
|
||||
PyasFree(Node);
|
||||
return;
|
||||
}
|
||||
|
||||
RtlCopyMemory(Node->Pattern.Buffer, RuleStr->Buffer, RuleStr->Length);
|
||||
Node->Pattern.Buffer[RuleStr->Length / sizeof(WCHAR)] = L'\0';
|
||||
Node->Pattern.Length = RuleStr->Length;
|
||||
Node->Pattern.MaximumLength = (USHORT)Size;
|
||||
|
||||
Node->Next = *Head;
|
||||
*Head = Node;
|
||||
}
|
||||
|
||||
static BOOLEAN HasSuffix(PCUNICODE_STRING String, PCWSTR Suffix) {
|
||||
if (!String || !String->Buffer || !Suffix) return FALSE;
|
||||
|
||||
SIZE_T StringLenChars = String->Length / sizeof(WCHAR);
|
||||
SIZE_T SuffixLenChars = 0;
|
||||
|
||||
while (Suffix[SuffixLenChars] != L'\0') {
|
||||
SuffixLenChars++;
|
||||
}
|
||||
|
||||
if (StringLenChars < SuffixLenChars) return FALSE;
|
||||
|
||||
PCWSTR Ptr = String->Buffer + (StringLenChars - SuffixLenChars);
|
||||
|
||||
for (SIZE_T i = 0; i < SuffixLenChars; i++) {
|
||||
if (RtlDowncaseUnicodeChar(Ptr[i]) != RtlDowncaseUnicodeChar(Suffix[i])) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
|
||||
if (!Pattern || !String) return FALSE;
|
||||
|
||||
PCWSTR mp = NULL;
|
||||
PCWSTR cp = NULL;
|
||||
PCWSTR StringEnd = (PCWSTR)((PUCHAR)String + StringLengthBytes);
|
||||
|
||||
while (String < StringEnd) {
|
||||
if (*Pattern == L'*') {
|
||||
mp = ++Pattern;
|
||||
cp = String + 1;
|
||||
}
|
||||
else if (*Pattern == L'?' || (RtlDowncaseUnicodeChar(*Pattern) == RtlDowncaseUnicodeChar(*String))) {
|
||||
Pattern++;
|
||||
String++;
|
||||
}
|
||||
else if (mp) {
|
||||
Pattern = mp;
|
||||
String = cp++;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
while (*Pattern == L'*') {
|
||||
Pattern++;
|
||||
}
|
||||
return !*Pattern;
|
||||
}
|
||||
|
||||
static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) {
|
||||
if (!Buffer || LengthChars == 0) return 0;
|
||||
|
||||
ULONG WriteIdx = 0;
|
||||
ULONG ReadIdx = 0;
|
||||
|
||||
while (ReadIdx < LengthChars) {
|
||||
if (Buffer[ReadIdx] == L'\\' && (ReadIdx + 1 < LengthChars)) {
|
||||
WCHAR NextChar = Buffer[ReadIdx + 1];
|
||||
if (NextChar == L'\\' || NextChar == L'"' || NextChar == L'/') {
|
||||
Buffer[WriteIdx++] = NextChar;
|
||||
ReadIdx += 2;
|
||||
}
|
||||
else if (NextChar == L'n') { Buffer[WriteIdx++] = L'\n'; ReadIdx += 2; }
|
||||
else if (NextChar == L'r') { Buffer[WriteIdx++] = L'\r'; ReadIdx += 2; }
|
||||
else if (NextChar == L't') { Buffer[WriteIdx++] = L'\t'; ReadIdx += 2; }
|
||||
else {
|
||||
Buffer[WriteIdx++] = Buffer[ReadIdx++];
|
||||
}
|
||||
}
|
||||
else {
|
||||
Buffer[WriteIdx++] = Buffer[ReadIdx++];
|
||||
}
|
||||
}
|
||||
|
||||
Buffer[WriteIdx] = L'\0';
|
||||
return WriteIdx * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
static VOID SkipWhitespace(PCHAR* Ptr, PCHAR End) {
|
||||
while (*Ptr < End) {
|
||||
char c = **Ptr;
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||
(*Ptr)++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyName, PRULE_NODE* ListHead) {
|
||||
if (!JsonContent || !KeyName || !ListHead) return;
|
||||
|
||||
PCHAR Ptr = JsonContent;
|
||||
PCHAR End = JsonContent + ContentLength;
|
||||
SIZE_T KeyLen = 0;
|
||||
while (KeyName[KeyLen] != '\0') KeyLen++;
|
||||
|
||||
while (Ptr < End) {
|
||||
if (*Ptr == '"') {
|
||||
if ((SIZE_T)(End - Ptr) > KeyLen && RtlCompareMemory(Ptr + 1, KeyName, KeyLen) == KeyLen) {
|
||||
if (*(Ptr + 1 + KeyLen) == '"') {
|
||||
Ptr += 1 + KeyLen + 1;
|
||||
|
||||
SkipWhitespace(&Ptr, End);
|
||||
if (Ptr >= End || *Ptr != ':') continue;
|
||||
Ptr++;
|
||||
|
||||
SkipWhitespace(&Ptr, End);
|
||||
if (Ptr >= End || *Ptr != '[') continue;
|
||||
Ptr++;
|
||||
|
||||
while (Ptr < End) {
|
||||
SkipWhitespace(&Ptr, End);
|
||||
if (Ptr >= End) break;
|
||||
|
||||
if (*Ptr == ']') {
|
||||
Ptr++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*Ptr == '"') {
|
||||
PCHAR StartQuote = ++Ptr;
|
||||
while (Ptr < End) {
|
||||
if (*Ptr == '"' && *(Ptr - 1) != '\\') break;
|
||||
Ptr++;
|
||||
}
|
||||
|
||||
if (Ptr < End && *Ptr == '"') {
|
||||
ULONG UTF8Len = (ULONG)(Ptr - StartQuote);
|
||||
if (UTF8Len > 0) {
|
||||
ULONG WideSize = 0;
|
||||
RtlUTF8ToUnicodeN(NULL, 0, &WideSize, StartQuote, UTF8Len);
|
||||
|
||||
if (WideSize > 0) {
|
||||
PWCHAR WideBuffer = (PWCHAR)PyasAllocate(WideSize + sizeof(WCHAR));
|
||||
if (WideBuffer) {
|
||||
ULONG ResultSize = 0;
|
||||
RtlUTF8ToUnicodeN(WideBuffer, WideSize, &ResultSize, StartQuote, UTF8Len);
|
||||
ULONG FinalSize = ProcessJsonUnescape(WideBuffer, ResultSize / sizeof(WCHAR));
|
||||
|
||||
UNICODE_STRING Us;
|
||||
Us.Buffer = WideBuffer;
|
||||
Us.Length = (USHORT)FinalSize;
|
||||
Us.MaximumLength = (USHORT)(WideSize + sizeof(WCHAR));
|
||||
|
||||
AddRule(ListHead, &Us);
|
||||
PyasFree(WideBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ptr++;
|
||||
}
|
||||
}
|
||||
else if (*Ptr == ',') {
|
||||
Ptr++;
|
||||
}
|
||||
else {
|
||||
Ptr++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
HANDLE FileHandle = NULL;
|
||||
IO_STATUS_BLOCK IoStatus = { 0 };
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
UNICODE_STRING ImagePathName;
|
||||
OBJECT_ATTRIBUTES RegOa = { 0 };
|
||||
HANDLE RegHandle = NULL;
|
||||
PKEY_VALUE_PARTIAL_INFORMATION Info = NULL;
|
||||
ULONG ResultLength = 0;
|
||||
PWCHAR PathBuffer = NULL;
|
||||
SIZE_T PathBufferSize = 0;
|
||||
UNICODE_STRING FinalPath = { 0 };
|
||||
|
||||
RtlInitUnicodeString(&ImagePathName, L"ImagePath");
|
||||
ExInitializeResourceLite(&RuleLock);
|
||||
|
||||
InitializeObjectAttributes(&RegOa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
status = ZwOpenKey(&RegHandle, KEY_READ, &RegOa);
|
||||
if (!NT_SUCCESS(status)) return status;
|
||||
|
||||
status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, NULL, 0, &ResultLength);
|
||||
if (status != STATUS_BUFFER_TOO_SMALL) {
|
||||
ZwClose(RegHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
Info = (PKEY_VALUE_PARTIAL_INFORMATION)PyasAllocate(ResultLength);
|
||||
if (!Info) {
|
||||
ZwClose(RegHandle);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, Info, ResultLength, &ResultLength);
|
||||
ZwClose(RegHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
PyasFree(Info);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (Info->Type == REG_EXPAND_SZ || Info->Type == REG_SZ) {
|
||||
PathBufferSize = ResultLength + 1024;
|
||||
PathBuffer = (PWCHAR)PyasAllocate(PathBufferSize);
|
||||
|
||||
if (PathBuffer) {
|
||||
RtlZeroMemory(PathBuffer, PathBufferSize);
|
||||
PWCHAR Src = (PWCHAR)Info->Data;
|
||||
|
||||
if (NT_SUCCESS(RtlStringCbCopyW(PathBuffer, PathBufferSize, Src))) {
|
||||
PWCHAR LastSlash = NULL;
|
||||
PWCHAR Current = PathBuffer;
|
||||
while (*Current) {
|
||||
if (*Current == L'\\') LastSlash = Current;
|
||||
Current++;
|
||||
}
|
||||
|
||||
if (LastSlash) {
|
||||
*LastSlash = L'\0';
|
||||
|
||||
SIZE_T CurrentPathLen = wcslen(PathBuffer);
|
||||
const WCHAR FilterSuffix[] = L"\\Filter";
|
||||
SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1;
|
||||
|
||||
if (CurrentPathLen >= FilterLen) {
|
||||
PWCHAR SuffixStart = PathBuffer + CurrentPathLen - FilterLen;
|
||||
BOOLEAN Match = TRUE;
|
||||
for (SIZE_T i = 0; i < FilterLen; i++) {
|
||||
if (RtlDowncaseUnicodeChar(SuffixStart[i]) != RtlDowncaseUnicodeChar(FilterSuffix[i])) {
|
||||
Match = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Match) {
|
||||
*SuffixStart = L'\0';
|
||||
}
|
||||
}
|
||||
|
||||
RtlStringCbCatW(PathBuffer, PathBufferSize, L"\\Rules\\Rules_Driver_P1.json");
|
||||
|
||||
if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 &&
|
||||
wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 &&
|
||||
wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) {
|
||||
|
||||
PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16);
|
||||
if (TmpBuffer) {
|
||||
RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\");
|
||||
RtlStringCbCatW(TmpBuffer, PathBufferSize + 16, PathBuffer);
|
||||
PyasFree(PathBuffer);
|
||||
PathBuffer = TmpBuffer;
|
||||
}
|
||||
}
|
||||
RtlInitUnicodeString(&FinalPath, PathBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PyasFree(Info);
|
||||
|
||||
if (!FinalPath.Buffer) {
|
||||
if (PathBuffer) PyasFree(PathBuffer);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
InitializeObjectAttributes(&oa, &FinalPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
status = ZwCreateFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &oa, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
||||
|
||||
if (NT_SUCCESS(status)) {
|
||||
FILE_STANDARD_INFORMATION FileInfo = { 0 };
|
||||
status = ZwQueryInformationFile(FileHandle, &IoStatus, &FileInfo, sizeof(FileInfo), FileStandardInformation);
|
||||
|
||||
if (NT_SUCCESS(status) && FileInfo.EndOfFile.LowPart > 0) {
|
||||
PVOID FileBuffer = PyasAllocate(FileInfo.EndOfFile.LowPart + 1);
|
||||
if (FileBuffer) {
|
||||
status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, FileBuffer, FileInfo.EndOfFile.LowPart, NULL, NULL);
|
||||
if (NT_SUCCESS(status)) {
|
||||
((PCHAR)FileBuffer)[FileInfo.EndOfFile.LowPart] = '\0';
|
||||
|
||||
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_BlockList", &g_RegistryBlockList);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_TrustedList", &g_RegistryTrustedList);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_TrustedPaths", &g_ProcessTrustedPaths);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_ExploitableBlacklist", &g_ProcessExploitable);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ProtectedPaths", &g_FileProtectedPaths);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ExceptionPaths", &g_FileExceptionPaths);
|
||||
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_RansomwareExtensions", &g_FileRansomExts);
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
}
|
||||
PyasFree(FileBuffer);
|
||||
}
|
||||
}
|
||||
ZwClose(FileHandle);
|
||||
}
|
||||
|
||||
if (PathBuffer) PyasFree(PathBuffer);
|
||||
return status;
|
||||
}
|
||||
|
||||
VOID UnloadRules() {
|
||||
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
|
||||
FreeList(&g_RegistryBlockList);
|
||||
FreeList(&g_RegistryTrustedList);
|
||||
FreeList(&g_ProcessTrustedPaths);
|
||||
FreeList(&g_ProcessExploitable);
|
||||
FreeList(&g_FileProtectedPaths);
|
||||
FreeList(&g_FileExceptionPaths);
|
||||
FreeList(&g_FileRansomExts);
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
ExDeleteResourceLite(&RuleLock);
|
||||
}
|
||||
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) {
|
||||
NTSTATUS status;
|
||||
PEPROCESS Process = NULL;
|
||||
|
||||
*ImageName = NULL;
|
||||
status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(status)) return status;
|
||||
|
||||
status = SeLocateProcessImageName(Process, ImageName);
|
||||
ObDereferenceObject(Process);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static VOID InitTrustCache() {
|
||||
if (g_CacheInitialized) return;
|
||||
ExInitializeFastMutex(&TrustCacheLock);
|
||||
RtlZeroMemory(TrustCache, sizeof(TrustCache));
|
||||
g_CacheInitialized = TRUE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsWindowsSystemApp(PCWSTR Buffer, USHORT Length) {
|
||||
if (WildcardMatch(L"*\\Windows\\SystemApps\\*", Buffer, Length)) return TRUE;
|
||||
if (WildcardMatch(L"*\\Windows\\ImmersiveControlPanel\\*", Buffer, Length)) return TRUE;
|
||||
if (WildcardMatch(L"*\\explorer.exe", Buffer, Length)) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
|
||||
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
|
||||
if (ProcessId == (HANDLE)4) return TRUE;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
|
||||
if (g_CacheInitialized) {
|
||||
ExAcquireFastMutex(&TrustCacheLock);
|
||||
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
|
||||
if (TrustCache[Hash].ProcessId == ProcessId) {
|
||||
BOOLEAN cachedResult = TrustCache[Hash].IsTrusted;
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
return cachedResult;
|
||||
}
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
}
|
||||
else {
|
||||
InitTrustCache();
|
||||
}
|
||||
|
||||
PUNICODE_STRING imageFileName = NULL;
|
||||
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
|
||||
BOOLEAN isTrusted = FALSE;
|
||||
|
||||
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
|
||||
|
||||
ExAcquireResourceSharedLite(&RuleLock, TRUE);
|
||||
|
||||
PRULE_NODE Node = g_ProcessExploitable;
|
||||
while (Node) {
|
||||
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
goto cleanup;
|
||||
}
|
||||
Node = Node->Next;
|
||||
}
|
||||
|
||||
if (IsWindowsSystemApp(imageFileName->Buffer, imageFileName->Length)) {
|
||||
isTrusted = TRUE;
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
Node = g_ProcessTrustedPaths;
|
||||
while (Node) {
|
||||
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
|
||||
isTrusted = TRUE;
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
goto cleanup;
|
||||
}
|
||||
Node = Node->Next;
|
||||
}
|
||||
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (g_CacheInitialized) {
|
||||
ExAcquireFastMutex(&TrustCacheLock);
|
||||
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
|
||||
TrustCache[Hash].ProcessId = ProcessId;
|
||||
TrustCache[Hash].IsTrusted = isTrusted;
|
||||
KeQuerySystemTime(&TrustCache[Hash].CacheTime);
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
}
|
||||
|
||||
if (imageFileName) ExFreePool(imageFileName);
|
||||
return isTrusted;
|
||||
}
|
||||
|
||||
BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
|
||||
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
|
||||
if (ProcessId == (HANDLE)4) return TRUE;
|
||||
|
||||
return IsProcessTrusted(ProcessId);
|
||||
}
|
||||
|
||||
BOOLEAN IsInstallerProcess(HANDLE ProcessId) {
|
||||
return IsProcessTrusted(ProcessId);
|
||||
}
|
||||
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId) {
|
||||
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) {
|
||||
if (!KeyName || !KeyName->Buffer) return FALSE;
|
||||
if (KeyName->Length < 4 * sizeof(WCHAR)) return FALSE;
|
||||
|
||||
if (WildcardMatch(L"*{645FF040-5081-101B-9F08-00AA002F954E}\\DefaultIcon", KeyName->Buffer, KeyName->Length)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ExAcquireResourceSharedLite(&RuleLock, TRUE);
|
||||
|
||||
PRULE_NODE AllowNode = g_RegistryTrustedList;
|
||||
while (AllowNode) {
|
||||
if (WildcardMatch(AllowNode->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return FALSE;
|
||||
}
|
||||
AllowNode = AllowNode->Next;
|
||||
}
|
||||
|
||||
PRULE_NODE Node = g_RegistryBlockList;
|
||||
BOOLEAN Match = FALSE;
|
||||
while (Node) {
|
||||
if (WildcardMatch(Node->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
|
||||
Match = TRUE;
|
||||
break;
|
||||
}
|
||||
Node = Node->Next;
|
||||
}
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return Match;
|
||||
}
|
||||
|
||||
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName) {
|
||||
if (!FileName || !FileName->Buffer) return FALSE;
|
||||
|
||||
ExAcquireResourceSharedLite(&RuleLock, TRUE);
|
||||
|
||||
PRULE_NODE ExNode = g_FileExceptionPaths;
|
||||
while (ExNode) {
|
||||
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return FALSE;
|
||||
}
|
||||
ExNode = ExNode->Next;
|
||||
}
|
||||
|
||||
PRULE_NODE Node = g_FileRansomExts;
|
||||
BOOLEAN Match = FALSE;
|
||||
while (Node) {
|
||||
if (HasSuffix(FileName, Node->Pattern.Buffer)) {
|
||||
Match = TRUE;
|
||||
break;
|
||||
}
|
||||
Node = Node->Next;
|
||||
}
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return Match;
|
||||
}
|
||||
|
||||
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) {
|
||||
if (!FileName || !FileName->Buffer) return FALSE;
|
||||
|
||||
if (WildcardMatch(L"*\\Windows\\System32\\config\\systemprofile*", FileName->Buffer, FileName->Length)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ExAcquireResourceSharedLite(&RuleLock, TRUE);
|
||||
|
||||
PRULE_NODE ExNode = g_FileExceptionPaths;
|
||||
while (ExNode) {
|
||||
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return FALSE;
|
||||
}
|
||||
ExNode = ExNode->Next;
|
||||
}
|
||||
|
||||
PRULE_NODE Node = g_FileProtectedPaths;
|
||||
BOOLEAN Match = FALSE;
|
||||
while (Node) {
|
||||
if (WildcardMatch(Node->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
|
||||
Match = TRUE;
|
||||
break;
|
||||
}
|
||||
Node = Node->Next;
|
||||
}
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return Match;
|
||||
}
|
||||
|
||||
static BOOLEAN IsHighEntropy(PVOID Buffer, ULONG Length) {
|
||||
if (!Buffer || Length < 256) return FALSE;
|
||||
|
||||
ULONG Histogram[256] = { 0 };
|
||||
PUCHAR Ptr = (PUCHAR)Buffer;
|
||||
ULONG ScanLen = (Length > 1024) ? 1024 : Length;
|
||||
|
||||
__try {
|
||||
for (ULONG i = 0; i < ScanLen; i++) {
|
||||
Histogram[Ptr[i]]++;
|
||||
}
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ULONG MaxFreq = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (Histogram[i] > MaxFreq) MaxFreq = Histogram[i];
|
||||
}
|
||||
|
||||
ULONG ExpectedAvg = ScanLen / 256;
|
||||
return (MaxFreq < (ExpectedAvg + HIGH_ENTROPY_THRESHOLD));
|
||||
}
|
||||
|
||||
static BOOLEAN IsNaturallyCompressed(PCUNICODE_STRING FileName) {
|
||||
if (!FileName || !FileName->Buffer) return FALSE;
|
||||
for (int i = 0; i < sizeof(Helper_NaturallyCompressedExtensions) / sizeof(Helper_NaturallyCompressedExtensions[0]); i++) {
|
||||
if (HasSuffix(FileName, Helper_NaturallyCompressedExtensions[i])) return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsNoisyRansomPath(PCUNICODE_STRING FileName) {
|
||||
if (!FileName || !FileName->Buffer) return FALSE;
|
||||
|
||||
if (WildcardMatch(L"*\\Windows\\Temp\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\SystemTemp\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\Prefetch\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\Logs\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\Profiles\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\Program*\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\AppData\\Local\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*\\User Data\\*", FileName->Buffer, FileName->Length) ||
|
||||
WildcardMatch(L"*$Recycle.Bin*", FileName->Buffer, FileName->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsExplorerProcess(HANDLE ProcessId) {
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
PUNICODE_STRING imageFileName = NULL;
|
||||
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
|
||||
BOOLEAN isExplorer = FALSE;
|
||||
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
|
||||
if (HasSuffix(imageFileName, L"\\explorer.exe")) isExplorer = TRUE;
|
||||
ExFreePool(imageFileName);
|
||||
}
|
||||
return isExplorer;
|
||||
}
|
||||
|
||||
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite) {
|
||||
if (IsNoisyRansomPath(FileName)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!IsWrite) {
|
||||
if (IsExplorerProcess(ProcessId)) return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN SuspiciousWrite = FALSE;
|
||||
if (IsWrite && WriteBuffer && WriteLength > 0) {
|
||||
if (!IsNaturallyCompressed(FileName) && IsHighEntropy(WriteBuffer, WriteLength)) {
|
||||
SuspiciousWrite = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsWrite && !SuspiciousWrite) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LARGE_INTEGER Now = { 0 };
|
||||
KeQuerySystemTime(&Now);
|
||||
BOOLEAN Result = FALSE;
|
||||
|
||||
ExAcquireFastMutex(&GlobalData.TrackerMutex);
|
||||
|
||||
PRANSOM_TRACKER Tracker = NULL;
|
||||
PRANSOM_TRACKER CandidateSlot = NULL;
|
||||
PRANSOM_TRACKER LruSlot = &RansomTrackers[0];
|
||||
|
||||
for (int i = 0; i < MAX_TRACKERS; i++) {
|
||||
PRANSOM_TRACKER Current = &RansomTrackers[i];
|
||||
|
||||
if (Current->LastActivityTime.QuadPart < LruSlot->LastActivityTime.QuadPart) {
|
||||
LruSlot = Current;
|
||||
}
|
||||
|
||||
if (Current->ProcessId == ProcessId) {
|
||||
Tracker = Current;
|
||||
break;
|
||||
}
|
||||
|
||||
BOOLEAN IsExpired = FALSE;
|
||||
if (Current->ProcessId != NULL) {
|
||||
LARGE_INTEGER Diff;
|
||||
Diff.QuadPart = Now.QuadPart - Current->LastActivityTime.QuadPart;
|
||||
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
|
||||
IsExpired = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Current->ProcessId == NULL || IsExpired) && !CandidateSlot) {
|
||||
CandidateSlot = Current;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Tracker) {
|
||||
if (CandidateSlot) {
|
||||
Tracker = CandidateSlot;
|
||||
}
|
||||
else {
|
||||
Tracker = LruSlot;
|
||||
}
|
||||
|
||||
Tracker->ProcessId = ProcessId;
|
||||
Tracker->ActivityCount = 0;
|
||||
Tracker->LastActivityTime = Now;
|
||||
}
|
||||
else {
|
||||
LARGE_INTEGER Diff;
|
||||
Diff.QuadPart = Now.QuadPart - Tracker->LastActivityTime.QuadPart;
|
||||
|
||||
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
|
||||
Tracker->ActivityCount = 0;
|
||||
}
|
||||
Tracker->LastActivityTime = Now;
|
||||
}
|
||||
|
||||
ULONG Weight = 2;
|
||||
Tracker->ActivityCount += Weight;
|
||||
|
||||
if (Tracker->ActivityCount >= RANSOM_COUNT_THRESHOLD) {
|
||||
Result = TRUE;
|
||||
}
|
||||
ExReleaseFastMutex(&GlobalData.TrackerMutex);
|
||||
return Result;
|
||||
}
|
||||
|
||||
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize) {
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_UNSUCCESSFUL;
|
||||
|
||||
PYAS_MESSAGE msg;
|
||||
RtlZeroMemory(&msg, sizeof(msg));
|
||||
msg.MessageCode = Code;
|
||||
msg.ProcessId = Pid;
|
||||
|
||||
if (Path && PathSize > 0) {
|
||||
size_t MaxSize = sizeof(msg.Path) - sizeof(WCHAR);
|
||||
size_t BytesToCopy = PathSize;
|
||||
if (BytesToCopy > MaxSize) BytesToCopy = MaxSize;
|
||||
|
||||
__try {
|
||||
RtlCopyMemory(msg.Path, Path, BytesToCopy);
|
||||
msg.Path[BytesToCopy / sizeof(WCHAR)] = L'\0';
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) {
|
||||
return STATUS_PORT_DISCONNECTED;
|
||||
}
|
||||
|
||||
NTSTATUS status = STATUS_PORT_DISCONNECTED;
|
||||
if (GlobalData.ClientPort) {
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = -(5 * 10000);
|
||||
status = FltSendMessage(GlobalData.FilterHandle, &GlobalData.ClientPort, &msg, sizeof(msg), NULL, NULL, &timeout);
|
||||
}
|
||||
|
||||
ExReleaseRundownProtection(&GlobalData.PortRundown);
|
||||
return status;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
|
||||
extern wchar_t* g_Whitelist[];
|
||||
extern wchar_t* g_WhitelistExcept[];
|
||||
|
||||
extern wchar_t* g_AttachDisk[];
|
||||
extern wchar_t* g_BlockFile[];
|
||||
extern wchar_t* g_BlockRansom[];
|
||||
extern wchar_t* g_Blocksuffix[];
|
||||
|
||||
extern wchar_t* g_BlockReg[];
|
||||
|
||||
extern wchar_t* g_RemoteSuspectBins[];
|
||||
|
||||
extern wchar_t* g_RemoteCmdIndicatorsHttp[];
|
||||
extern wchar_t* g_RemoteCmdIndicatorsGeneric[];
|
||||
extern wchar_t* g_RemoteCmdFromCmdExe[];
|
||||
extern wchar_t* g_RemoteCmdFromRundll32Exe[];
|
||||
extern wchar_t* g_RemoteCmdRegsvr32Need[];
|
||||
extern wchar_t* g_RemoteCmdFromMshtaExe[];
|
||||
|
||||
extern wchar_t* g_ScreenCapModules[];
|
||||
extern wchar_t* g_ScreenUserSubdirs[];
|
||||
extern wchar_t* g_ScreenOtherProcNeedles[];
|
||||
@@ -1,67 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_ScreenImgCb = FALSE;
|
||||
|
||||
static VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
if (!FullImageName || !FullImageName->Buffer || FullImageName->Length == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
UNICODE_STRING proc = { 0 };
|
||||
WCHAR pbuf[512] = { 0 };
|
||||
proc.Buffer = pbuf;
|
||||
proc.MaximumLength = sizeof(pbuf);
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &proc)) {
|
||||
PEPROCESS eproc = NULL;
|
||||
if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &eproc))) {
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
SIZE_T l = n ? strlen(n) : 0;
|
||||
for (SIZE_T i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
|
||||
pbuf[i] = (WCHAR)n[i];
|
||||
pbuf[l] = 0;
|
||||
proc.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
ObDereferenceObject(eproc);
|
||||
}
|
||||
}
|
||||
if (proc.Length == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
if (IsWhitelist(&proc)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOLEAN suspicious = MatchSuspiciousProcPath(&proc);
|
||||
if (suspicious && MatchScreenCapModule(FullImageName))
|
||||
LogAnsi3("SCREEN_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &proc, FullImageName);
|
||||
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitScreenProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(s)) g_ScreenImgCb = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitScreenProtect(VOID)
|
||||
{
|
||||
if (g_ScreenImgCb) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ScreenImgCb = FALSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
{
|
||||
"Rule_Registry_BlockList": [
|
||||
"\\REGISTRY\\MACHINE\\BCD00000000",
|
||||
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
|
||||
"*\\Image File Execution Options\\*",
|
||||
|
||||
"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\NetWire\\*",
|
||||
"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\Remcos*\\*",
|
||||
"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\DC3_FEXEC\\*",
|
||||
|
||||
"*\\Classes\\.exe",
|
||||
"*\\Classes\\.bat",
|
||||
"*\\Classes\\.cmd",
|
||||
"*\\Classes\\.com",
|
||||
"*\\Classes\\exefile\\*",
|
||||
"*\\Classes\\batfile\\*",
|
||||
"*\\Classes\\cmdfile\\*",
|
||||
"*\\Classes\\comfile\\*",
|
||||
|
||||
"*\\DefaultIcon",
|
||||
"*\\DisableAntiSpyware",
|
||||
"*\\DisableWindowsUpdateAccess",
|
||||
"*\\EnableLUA",
|
||||
"*\\ConsentPromptBehaviorAdmin",
|
||||
"*\\NoControlPanel",
|
||||
"*\\NoDrives",
|
||||
"*\\NoFileMenu",
|
||||
"*\\NoFind",
|
||||
"*\\NoStartMenuPinnedList",
|
||||
"*\\NoSetFolders",
|
||||
"*\\NoSetFolderOptions",
|
||||
"*\\NoViewOnDrive",
|
||||
"*\\NoClose",
|
||||
"*\\NoDesktop",
|
||||
"*\\NoLogoff",
|
||||
"*\\NoFolderOptions",
|
||||
"*\\RestrictRun",
|
||||
"*\\NoViewContextMenu",
|
||||
"*\\HideClock",
|
||||
"*\\NoStartMenuMyGames",
|
||||
"*\\NoStartMenuMyMusic",
|
||||
"*\\DisableCMD",
|
||||
"*\\NoAddingComponents",
|
||||
"*\\NoWinKeys",
|
||||
"*\\NoStartMenuLogOff",
|
||||
"*\\NoSimpleNetIDList",
|
||||
"*\\NoLowDiskSpaceChecks",
|
||||
"*\\DisableLockWorkstation",
|
||||
"*\\Restrict_Run",
|
||||
"*\\DisableTaskMgr",
|
||||
"*\\DisableRegistryTools",
|
||||
"*\\DisableChangePassword",
|
||||
"*\\Wallpaper",
|
||||
"*\\NoComponents",
|
||||
"*\\NoStartMenuMorePrograms",
|
||||
"*\\NoActiveDesktop",
|
||||
"*\\NoSetActiveDesktop",
|
||||
"*\\NoRecentDocsMenu",
|
||||
"*\\NoWindowsUpdate",
|
||||
"*\\NoChangeStartMenu",
|
||||
"*\\NoFavoritesMenu",
|
||||
"*\\NoRecentDocsHistory",
|
||||
"*\\NoSetTaskbar",
|
||||
"*\\NoSMHelp",
|
||||
"*\\NoTrayContextMenu",
|
||||
"*\\NoManageMyComputerVerb",
|
||||
"*\\NoRealMode",
|
||||
"*\\NoRun",
|
||||
"*\\ClearRecentDocsOnExit",
|
||||
"*\\NoActiveDesktopChanges",
|
||||
"*\\NoStartMenuNetworkPlaces",
|
||||
"*\\NoLockScreen",
|
||||
"*\\HideFastUserSwitching",
|
||||
"*\\ConsentPromptBehaviorUser",
|
||||
"*\\EnableSecureUIAPaths",
|
||||
"*\\NoSRM",
|
||||
"*\\NoInteractiveServices",
|
||||
"*\\ShutdownWithoutLogon",
|
||||
"*\\AutoAdminLogon",
|
||||
"*\\ImagePath",
|
||||
"*\\BootExecute",
|
||||
"*\\SubSystems",
|
||||
"*\\FirmwareBootDevice",
|
||||
"*\\BootDriverFlags",
|
||||
"*\\SystemStartOptions",
|
||||
"*\\CrashDumpEnabled",
|
||||
"*\\DisableAutomaticRestartOnFailure",
|
||||
"*\\SystemBiosVersion",
|
||||
"*\\NoTheme",
|
||||
"*\\PendingFileRenameOperations",
|
||||
"*\\SystemSetupInProgress",
|
||||
"*\\CmdLine",
|
||||
"*\\SetupType",
|
||||
"*\\AutoReboot",
|
||||
"*\\Userinit",
|
||||
"*\\UIHost",
|
||||
"*\\Debugger",
|
||||
"*\\DefaultUserName",
|
||||
"*\\DefaultPassword",
|
||||
"*\\AltDefaultUserName",
|
||||
"*\\AltDefaultPassword",
|
||||
"*\\DisableCAD",
|
||||
"*\\LegalNoticeCaption",
|
||||
"*\\LegalNoticeText",
|
||||
],
|
||||
"Rule_Registry_TrustedList": [
|
||||
],
|
||||
"Rule_Process_TrustedPaths": [
|
||||
"*\\Windows\\*",
|
||||
"*\\Windows Defender\\*",
|
||||
"*\\Windows Defender Advanced Threat Protection\\*",
|
||||
"*\\Program Files\\*",
|
||||
"*\\Program Files (x86)\\*",
|
||||
"*\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"*\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"*\\Mozilla Firefox\\firefox.exe",
|
||||
"*\\Steam\\steam.exe",
|
||||
"*\\Battle.net\\*\\Battle.net.exe",
|
||||
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe"
|
||||
],
|
||||
"Rule_Process_ExploitableBlacklist": [
|
||||
"*\\cmd.exe",
|
||||
"*\\powershell.exe",
|
||||
"*\\pwsh.exe",
|
||||
"*\\wscript.exe",
|
||||
"*\\cscript.exe",
|
||||
"*\\mshta.exe",
|
||||
"*\\regsvr32.exe",
|
||||
"*\\certutil.exe",
|
||||
"*\\bitsadmin.exe",
|
||||
"*\\wmic.exe",
|
||||
"*\\hh.exe",
|
||||
"*\\cmstp.exe",
|
||||
"*\\installutil.exe",
|
||||
"*\\regasm.exe",
|
||||
"*\\regsvcs.exe",
|
||||
"*\\msbuild.exe",
|
||||
"*\\csc.exe",
|
||||
"*\\vbc.exe",
|
||||
"*\\jsc.exe",
|
||||
"*\\bash.exe",
|
||||
"*\\scrcons.exe",
|
||||
"*\\schtasks.exe",
|
||||
"*\\at.exe",
|
||||
"*\\reg.exe",
|
||||
"*\\sc.exe",
|
||||
"*\\net.exe",
|
||||
"*\\net1.exe",
|
||||
"*\\whoami.exe",
|
||||
"*\\ipconfig.exe",
|
||||
"*\\taskkill.exe",
|
||||
"*\\ProcessHacker.exe",
|
||||
"*\\procmon.exe",
|
||||
"*\\procexp.exe"
|
||||
],
|
||||
"Rule_File_ProtectedPaths": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\PYAS_Driver.sys",
|
||||
"*\\ProgramData\\PYAS\\*.json",
|
||||
|
||||
"*\\bootmgr",
|
||||
"*\\boot.ini",
|
||||
"*\\BOOTNXT",
|
||||
"*\\BCD",
|
||||
"*\\EFI\\*",
|
||||
"*\\Boot\\*",
|
||||
"*\\Recovery\\*",
|
||||
|
||||
"*\\Windows\\*.exe",
|
||||
"*\\Windows\\*.dll",
|
||||
"*\\Windows\\*.sys",
|
||||
"*\\Windows\\System32\\*.exe",
|
||||
"*\\Windows\\System32\\*.dll",
|
||||
"*\\Windows\\System32\\*.sys",
|
||||
"*\\Windows\\SysWOW64\\*.exe",
|
||||
"*\\Windows\\SysWOW64\\*.dll",
|
||||
"*\\Windows\\SysWOW64\\*.sys",
|
||||
"*\\System Volume Information\\*",
|
||||
|
||||
"*\\CON",
|
||||
"*\\CON\\*",
|
||||
"*\\PRN",
|
||||
"*\\PRN\\*",
|
||||
"*\\AUX",
|
||||
"*\\AUX\\*",
|
||||
"*\\NUL",
|
||||
"*\\NUL\\*",
|
||||
"*\\COM?",
|
||||
"*\\COM?\\*",
|
||||
"*\\LPT?",
|
||||
"*\\LPT?\\*",
|
||||
"*\\LPT?",
|
||||
"*\\LPT?\\*",
|
||||
"*\\OSDATA",
|
||||
"*\\OSDATA\\*",
|
||||
|
||||
"*\\*<*",
|
||||
"*\\*>*",
|
||||
"*\\*:*",
|
||||
],
|
||||
"Rule_File_ExceptionPaths": [
|
||||
"*\\AppData\\*"
|
||||
],
|
||||
"Rule_File_RansomwareExtensions": [
|
||||
".exe", ".dll", ".sys", ".com", ".scr",
|
||||
".zip", ".7z", ".rar", ".tar", ".gz",
|
||||
".js", ".bat", ".cmd", ".ps1", ".vbs",
|
||||
".ppt", ".pptx", ".wps", ".txt", ".rtf",
|
||||
".pdf", ".xls", ".xlsx", ".doc", ".docx",
|
||||
".jpg", ".jpeg", ".png", ".webp", ".gif",
|
||||
".mp3", ".wav", ".aac", ".ogg", ".flac",
|
||||
".mp4", ".avi", ".mov", ".wmv", ".mkv",
|
||||
".aux", ".cur", ".mui", ".ttf", ".efi",
|
||||
".cpp", ".h", ".c", ".hpp", ".cs", ".java", ".py", ".go", ".rs",
|
||||
".php", ".html", ".css", ".json", ".xml", ".sql", ".db"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# PYAS
|
||||
|
||||
Antivirus software written in Python and C that blocks threats through deep learning and behavioral monitoring!
|
||||
Antivirus software written in Python and C++ that blocks threats through deep learning and behavioral monitoring!
|
||||
|
||||
<img width="2245" height="1477" alt="PYAS_UI" src="https://github.com/user-attachments/assets/87d40261-7655-49ad-a19c-1ffcca60584f" />
|
||||
|
||||
@@ -9,14 +9,15 @@ Antivirus software written in Python and C that blocks threats through deep lear
|
||||
Python 3.10 is recommended. Other Python versions may require different pip commands.
|
||||
|
||||
```
|
||||
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 PySide6==6.9.1
|
||||
pip install yara-python==4.5.4
|
||||
pip install Pillow==11.0.0
|
||||
pip install numpy==1.26.4
|
||||
pip install tensorflow==2.10.0
|
||||
pip install tf2onnx==1.13.0
|
||||
pip install lightgbm==4.6.0
|
||||
pip install onnxruntime==1.18.1
|
||||
```
|
||||
|
||||
## File Information
|
||||
@@ -26,26 +27,25 @@ The following lists the storage locations of all relevant code and other related
|
||||
```
|
||||
PYAS/
|
||||
├── Engine/
|
||||
│ ├── Models/
|
||||
│ ├── Pattern/
|
||||
│ │ ├── convert.py # Convert executable files or other files to images
|
||||
│ │ └── train.py # TensorFlow CNN model training complete code
|
||||
│ │ ├── train.py # TensorFlow CNN model training complete code
|
||||
│ │ └── ... # Other models folders and files
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ ├── rules.yar # Yara virus signature rule matching
|
||||
│ └── rules.ips # IP network address rule matching
|
||||
│ ├── Heuristic/
|
||||
│ │ ├── rules.yar # Yara virus signature rule matching
|
||||
│ │ └── ... # Other rules folders and files
|
||||
│ └── ... # Other engine folders and files
|
||||
│
|
||||
├── Plugins/
|
||||
│ └── Filter/
|
||||
│ ├── DriverEntry.c # Main driver entry and initialization logic
|
||||
│ ├── DriverEntry.h # Global driver definitions, constants, and functions
|
||||
│ ├── DriverPipe.c # Kernel to user pipe logging implementation
|
||||
│ ├── ProtectBoot.c # Disk boot sector write protection
|
||||
│ ├── ProtectImage.c # Image load monitoring and shellcode detection
|
||||
│ ├── ProtectInject.c # Process thread handle access control to prevent injection
|
||||
│ ├── ProtectReg.c # Registry modification protection
|
||||
│ ├── ProtectRules.c # White, block list, and matching logic for files registry
|
||||
│ └── ProtectRules.h # Protection rule declarations
|
||||
│
|
||||
│ │ ├── DriverEntry.cpp # Main driver entry and initialization logic
|
||||
│ │ ├── DriverCommon.h # Global driver definitions, constants, and functions
|
||||
│ │ └── ... # Other driver folders and files
|
||||
│ │
|
||||
│ └── Rules/
|
||||
│ └── rules.json # White, block list, and matching logic for files registry
|
||||
|
|
||||
├── PYAS.py # Main application entry point and UI to engine interface
|
||||
├── PYAS_Config.py # Configuration loading, saving, and global parameters
|
||||
├── PYAS_Engine.py # Core scanning engine: YARA, IP, ONNX model execution
|
||||
@@ -62,107 +62,107 @@ PYAS Security antivirus software general architecture diagram.
|
||||
```mermaid
|
||||
graph TD
|
||||
%% Global Styles
|
||||
classDef ui fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
|
||||
classDef logic fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
|
||||
classDef engine fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
|
||||
classDef thread fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
|
||||
classDef kernel fill:#ffebee,stroke:#c62828,stroke-width:2px;
|
||||
classDef data fill:#eceff1,stroke:#455a64,stroke-width:2px,stroke-dasharray: 5 5;
|
||||
classDef userMode fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#0d47a1
|
||||
classDef kernelMode fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#e65100
|
||||
classDef storage fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
|
||||
classDef interaction fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,stroke-dasharray: 5 5,color:#1b5e20
|
||||
|
||||
User((User))
|
||||
|
||||
subgraph Presentation_Layer ["Presentation Layer (View)"]
|
||||
subgraph UserSpace [User Mode Application - PYAS.exe]
|
||||
direction TB
|
||||
UI_Static["PYAS_Interface.py<br/>(Ui_main_window Class)"]:::ui
|
||||
UI_Controller["PYAS.py<br/>(MainWindow_Controller Class)"]:::ui
|
||||
TrayIcon["System Tray Icon"]:::ui
|
||||
|
||||
User <--> UI_Controller
|
||||
UI_Static --- UI_Controller
|
||||
UI_Controller --- TrayIcon
|
||||
end
|
||||
|
||||
subgraph Core_Logic_Layer ["Core Logic & Controller"]
|
||||
direction TB
|
||||
Init_Env["Initialization<br/>(init_environ, init_variable,<br/>init_windll)"]:::logic
|
||||
Config_Mgr["Configuration Manager<br/>(load_config, save_config)"]:::logic
|
||||
GUI[MainWindow_Controller / UI]:::userMode
|
||||
ConfigMgr[Configuration & Rule Manager]:::userMode
|
||||
|
||||
subgraph System_Tools ["System Tools"]
|
||||
Sys_Repair["System Repair<br/>(repair_system_mbr,<br/>repair_system_image, etc.)"]:::logic
|
||||
Sys_Clean["System Cleaner<br/>(clean_button, traverse_temp)"]:::logic
|
||||
Proc_Mgr["Process Manager<br/>(list_process, kill_process)"]:::logic
|
||||
List_Mgr["List Manager<br/>(manage_named_list:<br/>Whitelist/Quarantine)"]:::logic
|
||||
subgraph DetectionEngine [Analysis Engine - PYAS_Engine.py]
|
||||
direction TB
|
||||
SignScanner[Digital Signature Scanner]:::userMode
|
||||
PEScanner[PE Feature & Entropy Analysis]:::userMode
|
||||
YaraScanner[Heuristic / YARA Scanner]:::userMode
|
||||
CNNScanner[AI / CNN Model Scanner]:::userMode
|
||||
CloudScanner[Cloud API / Hash Check]:::userMode
|
||||
end
|
||||
|
||||
UI_Controller --> Init_Env
|
||||
Init_Env --> Config_Mgr
|
||||
UI_Controller --> System_Tools
|
||||
end
|
||||
|
||||
subgraph Protection_Layer ["Real-time Protection Threads (Daemons)"]
|
||||
direction TB
|
||||
Thread_Proc["Process Guard Thread<br/>(protect_proc_thread)<br/>[WMI/Snapshot]"]:::thread
|
||||
Thread_File["File Guard Thread<br/>(protect_file_thread)<br/>[ReadDirectoryChangesW]"]:::thread
|
||||
Thread_Net["Network Guard Thread<br/>(protect_net_thread)<br/>[GetExtendedTcpTable]"]:::thread
|
||||
Thread_Sys["System Guard Thread<br/>(protect_system_thread)<br/>[Reg/MBR Check]"]:::thread
|
||||
Thread_Popup["Popup Blocker Thread<br/>(popup_intercept_thread)<br/>[EnumWindows]"]:::thread
|
||||
Thread_Pipe["Driver IPC Thread<br/>(pipe_server_thread)<br/>[NamedPipe Server]"]:::thread
|
||||
|
||||
Init_Env --> Thread_Proc
|
||||
Init_Env --> Thread_File
|
||||
Init_Env --> Thread_Net
|
||||
Init_Env --> Thread_Sys
|
||||
Init_Env --> Thread_Popup
|
||||
Init_Env --> Thread_Pipe
|
||||
end
|
||||
|
||||
subgraph Engine_Layer ["Detection Engine (PYAS_Engine.py)"]
|
||||
direction TB
|
||||
Scan_Worker["Scan Worker<br/>(scan_worker / scan_engine)"]:::engine
|
||||
|
||||
subgraph Scanners
|
||||
Sign_Check["Signature Scanner<br/>(sign_scanner)<br/>[WinVerifyTrust]"]:::engine
|
||||
AI_Model["AI Model Scanner<br/>(model_scanner)<br/>[ONNX/ResNet]"]:::engine
|
||||
Yara_Rules["Rule Scanner<br/>(rule_scanner)<br/>[Yara/IP Lists]"]:::engine
|
||||
subgraph UserMonitors [User-Mode Protection Threads]
|
||||
direction TB
|
||||
ProcMon[Process Monitor - CreateToolhelp32Snapshot]:::userMode
|
||||
FileMon[File Monitor - ReadDirectoryChangesW]:::userMode
|
||||
NetMon[Network Monitor - GetExtendedTcpTable]:::userMode
|
||||
SysRep[System Repair - MBR/Reg/Wallpaper]:::userMode
|
||||
PopupBlock[Popup Blocker - EnumWindows]:::userMode
|
||||
PipeClient[IPC Client Thread]:::userMode
|
||||
end
|
||||
|
||||
Thread_Proc --> Scan_Worker
|
||||
Thread_File --> Scan_Worker
|
||||
Thread_Net --> Scan_Worker
|
||||
UI_Controller -->|Manual Scan| Scan_Worker
|
||||
|
||||
Scan_Worker --> Sign_Check
|
||||
Scan_Worker --> AI_Model
|
||||
Scan_Worker --> Yara_Rules
|
||||
GUI --> ConfigMgr
|
||||
GUI --> DetectionEngine
|
||||
GUI --> UserMonitors
|
||||
UserMonitors --> DetectionEngine
|
||||
end
|
||||
|
||||
subgraph Kernel_OS_Layer ["Windows Kernel & Driver"]
|
||||
subgraph StorageLayer [File System / Configuration]
|
||||
direction LR
|
||||
JSONRules[JSON Rules Files]:::storage
|
||||
ConfigJSON[Config.json]:::storage
|
||||
Quarantine[Quarantine Folder]:::storage
|
||||
end
|
||||
|
||||
subgraph KernelSpace [Kernel Mode Driver - PYAS_Driver.sys]
|
||||
direction TB
|
||||
WinAPI["Windows API (ctypes)<br/>(kernel32, user32, ntdll,<br/>advapi32, iphlpapi, psapi)"]:::kernel
|
||||
Driver_Sys["Kernel Driver<br/>(PYAS_Driver.sys)"]:::kernel
|
||||
OS_FS["File System (NTFS)"]:::kernel
|
||||
OS_Net["Network Stack"]:::kernel
|
||||
|
||||
Init_Env -- Load DLLs --> WinAPI
|
||||
Thread_File -- Monitor --> OS_FS
|
||||
Thread_Net -- Monitor --> OS_Net
|
||||
Thread_Proc -- Suspend/Kill --> WinAPI
|
||||
|
||||
Thread_Pipe <==>|"Named Pipe IPC<br/>(\\.\pipe\PYAS_Output_Pipe)"| Driver_Sys
|
||||
Driver_Sys -- Callback/Block --> WinAPI
|
||||
DriverEntry[DriverEntry / Initialization]:::kernelMode
|
||||
GlobalData[Global Data & State]:::kernelMode
|
||||
CommServer[Communication Port Server]:::kernelMode
|
||||
|
||||
subgraph KernelLogic [Core Protection Logic]
|
||||
RuleLoader[Rule Loader & Parser]:::kernelMode
|
||||
TrustCache[Trust Cache & Ransom Tracker]:::kernelMode
|
||||
|
||||
subgraph MiniFilter [File System MiniFilter]
|
||||
PreCreate[PreCreate: HoneyToken / Access Control]:::kernelMode
|
||||
PreWrite[PreWrite: Ransomware / Entropy Check]:::kernelMode
|
||||
PreSetInfo[PreSetInfo: Anti-Rename / Extension]:::kernelMode
|
||||
PreDevCtrl[PreDeviceControl: Boot / Disk Wipe Protect]:::kernelMode
|
||||
end
|
||||
|
||||
subgraph ObjectCallbacks [Object Manager Callbacks]
|
||||
ProcProtect[ObRegisterCallbacks: Handle Stripping]:::kernelMode
|
||||
ImageLoad[PsSetLoadImageNotifyRoutine: Image Blocking]:::kernelMode
|
||||
end
|
||||
|
||||
subgraph RegistryCallbacks [Configuration Manager Callbacks]
|
||||
RegFilter[CmRegisterCallbackEx: Registry Guard]:::kernelMode
|
||||
end
|
||||
end
|
||||
|
||||
DriverEntry --> GlobalData
|
||||
DriverEntry --> CommServer
|
||||
DriverEntry --> MiniFilter
|
||||
DriverEntry --> ProcProtect
|
||||
DriverEntry --> ImageLoad
|
||||
DriverEntry --> RegFilter
|
||||
|
||||
MiniFilter --> RuleLoader
|
||||
ProcProtect --> RuleLoader
|
||||
RegFilter --> RuleLoader
|
||||
|
||||
PreWrite --> TrustCache
|
||||
PreCreate --> TrustCache
|
||||
end
|
||||
|
||||
subgraph Data_Persistence ["Data & Resources"]
|
||||
Config_File[("Config.json")]:::data
|
||||
Model_Files[("Model Files<br/>(.onnx)")]:::data
|
||||
Rule_Files[("Rule Files<br/>(.yar)")]:::data
|
||||
Quarantine_Dir[("/Quarantine/")]:::data
|
||||
|
||||
Config_Mgr <--> Config_File
|
||||
AI_Model <--> Model_Files
|
||||
Yara_Rules <--> Rule_Files
|
||||
List_Mgr <--> Quarantine_Dir
|
||||
end
|
||||
%% Cross-Boundary Interactions
|
||||
ConfigMgr -- Writes --> JSONRules
|
||||
ConfigMgr -- Writes --> ConfigJSON
|
||||
RuleLoader -- Reads --> JSONRules
|
||||
|
||||
GUI -- Service Control (SCM) --> DriverEntry
|
||||
PipeClient -- FltSendMessage (IPC) --> CommServer
|
||||
CommServer -- Notifications --> PipeClient
|
||||
|
||||
FileMon -- Moves Malicious Files --> Quarantine
|
||||
|
||||
%% Logic Flow Details
|
||||
ProcProtect -- Protects --> UserSpace
|
||||
RegFilter -- Protects --> StorageLayer
|
||||
PreDevCtrl -- Protects --> StorageLayer
|
||||
```
|
||||
|
||||
## Support System
|
||||
@@ -186,6 +186,8 @@ Source Available : https://github.com/87owo/PYAS
|
||||
|
||||
Official Website : https://pyas-security.com/antivirus
|
||||
|
||||
Online Analyze : https://pyas-security.com/analyze
|
||||
|
||||
## Project License
|
||||
|
||||
For any questions, needs, or bug feedback, please contact us through the following website.
|
||||
|
||||
Reference in new issue
Block a user