Compare commits

...
43 Commits
Author SHA1 Message Date
87owo ac4f960352 Add files via upload 2026-02-10 17:45:23 +08:00
87owo e4b3e4e438 Add files via upload 2026-02-10 17:44:48 +08:00
87owo d6ee476296 Add files via upload 2026-02-10 17:44:15 +08:00
87owo c23741ba9a Add files via upload 2026-02-10 17:43:47 +08:00
87owo 7bac5b4740 Add files via upload 2026-02-10 17:43:04 +08:00
87owo 1fe440168e Add files via upload 2026-02-04 23:11:56 +08:00
87owo 26ad57138b Add files via upload 2026-02-04 23:11:14 +08:00
87owo 92e1fdaf0f Add files via upload 2026-02-04 23:09:59 +08:00
87owo 357540d0ad Update README.md 2026-01-24 01:19:33 +08:00
87owo 405622a91e Update README.md 2026-01-24 01:14:02 +08:00
87owo 5686c73c56 Update README.md 2026-01-24 01:11:38 +08:00
87owo 76e2f787ac Update README.md 2026-01-24 01:10:28 +08:00
87owo 02b67f4e6a Update README.md 2026-01-24 01:06:59 +08:00
87owo a2ab527761 Update README.md 2026-01-24 01:05:17 +08:00
87owo f1142ae742 Add files via upload 2026-01-23 14:48:01 +08:00
87owo decb222dac Add files via upload 2026-01-23 14:25:34 +08:00
87owo 00c598e6f7 Add files via upload 2026-01-23 14:25:11 +08:00
87owo e9843dd3a5 Add files via upload 2026-01-23 14:24:27 +08:00
87owo 37f390742b Add files via upload 2026-01-23 14:23:14 +08:00
87owo 533e7dfd15 Add files via upload 2026-01-23 13:49:06 +08:00
87owo ddcbe1cd51 Delete Engine directory 2026-01-23 13:46:15 +08:00
87owo 4eabd04bc6 Add files via upload 2026-01-23 13:44:24 +08:00
87owo c8f2da6ac9 Add files via upload 2026-01-23 13:43:17 +08:00
87owo 8820ac4bf3 Add files via upload 2026-01-23 13:42:37 +08:00
87owo 236b200f39 Add files via upload 2026-01-12 14:28:24 +08:00
87owo 6789e32674 Add files via upload 2026-01-12 14:27:37 +08:00
87owo 9eb55deedf Add files via upload 2026-01-12 13:29:30 +08:00
87owo 6e76b5b8eb Add files via upload 2026-01-12 13:28:04 +08:00
87owo 491b0da83d Add files via upload 2026-01-10 22:48:17 +08:00
87owo 5d2c17e940 Add files via upload 2026-01-10 22:47:35 +08:00
87owo 0497c4a9e7 Add files via upload 2026-01-10 20:05:44 +08:00
87owo 6d0765848b Add files via upload 2026-01-10 20:03:39 +08:00
87owo 40c4ae71bd Merge pull request #35 from secretlay3r/main
refactor: replace ifs with dicts/sets + simplify exception handling
2026-01-10 19:37:25 +08:00
secretlay3r 8edba10dff Update PYAS.py
backup_mbr():
- Shorten except blocks logic into one
- Add len(mbr) == 512 check  
- Add MBR_SIGNATURE instead of just b"\x55\xAA"

pipe_server_thread():
- Replace whole ifs chain with RULES dict
- Replace [4001, 5001] with KILL_CODES set (for readability)

get_window_info()  and  get_process_name():
- Extract process name logic into private _get_process_name()
2026-01-10 14:17:27 +03:00
87owo 85aa7bc652 Add files via upload 2026-01-09 14:05:47 +08:00
87owo 854ad1db07 Add files via upload 2026-01-09 00:11:30 +08:00
87owo 955600a1e1 Add files via upload 2026-01-07 22:19:13 +08:00
87owo b739134d85 Add files via upload 2026-01-07 22:18:36 +08:00
87owo 723152566d Add files via upload 2026-01-07 21:10:34 +08:00
87owo 678b045e0e Update README.md 2026-01-07 17:47:13 +08:00
87owo 681f4df49a Add files via upload 2026-01-07 17:44:23 +08:00
87owo b19f7d7098 Add files via upload 2026-01-06 15:11:42 +08:00
87owo daaae3ec10 Add files via upload 2026-01-06 14:43:06 +08:00
20 changed files with 2484 additions and 269970 deletions

No files matched your search

File renamed without changes.
@@ -1,5 +1,5 @@
from PIL import Image
import numpy, pefile, os
import numpy, os
####################################################################################################
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,7 +111,7 @@ 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)
####################################################################################################
@@ -124,18 +123,20 @@ def calculate_lr_cosine_sq(epoch, total_epochs=30, lr_start=5e-4, lr_end=5e-7):
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())
@@ -148,16 +149,17 @@ for imgs, _ in train_ds.take(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=30, 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')
+513
View File
@@ -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)}")
+195
View File
@@ -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
+160 -125
View File
@@ -120,8 +120,9 @@ class MainWindow_Controller(QMainWindow):
self.sign = sign_scanner()
self.sign.init_windll(["wintrust"])
self.model = model_scanner()
self.rule = rule_scanner()
self.pattern = cnn_scanner()
self.heuristic = rule_scanner()
self.properties = pe_scanner()
self.cloud = cloud_scanner()
self.cloud_queue = queue.Queue()
self.init_cloud_worker()
@@ -130,6 +131,7 @@ class MainWindow_Controller(QMainWindow):
self.mouse_pos = 0
self.current_opacity = 0
self.driver_port = None
self.scan_running = False
self.virus_lock = {}
self.virus_results = []
@@ -144,12 +146,12 @@ class MainWindow_Controller(QMainWindow):
"traditional_switch", "simplified_switch", "english_switch",
]
self.pyas_default = {
"version": "3.3.8",
"version": "3.4.0",
"api_host": "https://pyas-security.com/",
"api_key": "",
"api_key": "fBRZxYS1UxykM-qzNOlKOEl63WILzlvgNMn6QfsG6FXCAAIktCrOPTAfY5_hEyuZ",
"suffix": [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui", ".cpl"],
"block": [2001, 3001, 4001, 5001, 6001],
"size": 256 * 1024 * 1024,
"sensitive": 95,
"language": "english_switch",
"theme": "white_switch",
"process_switch": True,
@@ -157,6 +159,9 @@ class MainWindow_Controller(QMainWindow):
"system_switch": True,
"driver_switch": True,
"network_switch": True,
"extension_switch": False,
"sensitive_switch": False,
"cloud_switch": True,
"custom_rule": [],
}
self.pass_windows = [
@@ -179,6 +184,13 @@ class MainWindow_Controller(QMainWindow):
"SCREEN_BLOCK": "監控行為攔截",
"KILL_ATTEMPT": "終止行為攔截",
}
self.block_rules = {
2001: "FILE_BLOCK",
3001: "REG_BLOCK",
4001: "BOOT_BLOCK",
5001: "RANSOM_BLOCK",
6001: "INJECT_BLOCK"
}
####################################################################################################
@@ -197,8 +209,9 @@ class MainWindow_Controller(QMainWindow):
self.path_systemp = os.path.join(self.path_system, "Temp")
self.file_config = os.path.join(self.path_config, "PYAS", "Config.json")
self.path_models = os.path.join(self.path_pyas, "Engine", "Models")
self.path_rules = os.path.join(self.path_pyas, "Engine", "Rules")
self.path_properties = os.path.join(self.path_pyas, "Engine", "Properties")
self.path_pattern = os.path.join(self.path_pyas, "Engine", "Pattern")
self.path_heuristic = os.path.join(self.path_pyas, "Engine", "Heuristic")
self.path_protect = os.path.join(self.path_pyas, "Plugins", "Filter")
self.path_drivers = os.path.join(self.path_protect, "PYAS_Driver.sys")
@@ -209,7 +222,7 @@ class MainWindow_Controller(QMainWindow):
try:
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"init_windll | {e}", "warn", False)
self.advapi32.OpenSCManagerW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.wintypes.DWORD]
self.advapi32.OpenSCManagerW.restype = ctypes.wintypes.HANDLE
@@ -262,7 +275,7 @@ class MainWindow_Controller(QMainWindow):
with open(file_path, "w", encoding="utf-8", errors="ignore") as f:
f.write(json.dumps(filter_config, indent=4, ensure_ascii=False))
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"save_config | {e}", "warn", False)
def read_config(self, file_path, default_value):
try:
@@ -274,7 +287,7 @@ class MainWindow_Controller(QMainWindow):
config_value.setdefault(key, value)
return config_value
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"read_config | {e}", "warn", False)
return default_value.copy()
def load_config(self):
@@ -296,11 +309,12 @@ class MainWindow_Controller(QMainWindow):
def init_engine_thread(self):
try:
self.rule.load_path(self.path_rules, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
self.model.load_path(self.path_models, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
self.heuristic.load_path(self.path_heuristic, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
self.pattern.load_path(self.path_pattern, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
self.properties.load_path(self.path_properties, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
self.init_log_signal.emit("引擎加載完成")
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"init_engine_thread | {e}", "warn", False)
####################################################################################################
@@ -392,9 +406,11 @@ class MainWindow_Controller(QMainWindow):
def save_state(self, name, checked):
self.pyas_config[name] = bool(checked)
if name == "sensitive_switch":
self.pyas_config["sensitive"] = 0 if checked else 95
pass
elif name == "extension_switch":
pass
elif name == "cloud_switch":
pass
elif name == "process_switch":
if checked:
self.start_daemon_thread(self.protect_proc_thread)
@@ -623,7 +639,7 @@ class MainWindow_Controller(QMainWindow):
else:
sys.exit(0)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"show_startup | {e}", "warn", False)
def show_button(self):
self.show()
@@ -816,7 +832,7 @@ class MainWindow_Controller(QMainWindow):
lang = self.pyas_config.get("language", "english_switch")
self.scan_progress_signal.emit(self.trans(lang, "正在初始化中"))
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"init_scan | {e}", "warn", False)
def file_button(self):
targets = self.send_message("檔案掃描", "files", True)
@@ -865,7 +881,7 @@ class MainWindow_Controller(QMainWindow):
self.scan_progress_signal.emit(norm_path)
gc.collect()
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"scan_worker | {e}", "warn", False)
finally:
count = len(self.virus_results)
elapsed = int(time.time() - self.scan_start)
@@ -890,21 +906,30 @@ class MainWindow_Controller(QMainWindow):
def scan_engine(self, file_path):
try:
result = False
label, level = self.model.model_scan(file_path)
if label and level >= self.pyas_config.get("sensitive", 95):
if not self.sign.sign_verify(file_path):
result = f"{label}.{level}"
if self.pyas_config.get("extension_switch", False):
types, detail = self.rule.yara_scan(file_path)
if types and detail:
result = f"{types}.{detail}"
return result
if self.sign.sign_verify(file_path):
return False
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"sign_verify | {e}", "warn", False)
try:
pe_label, pe_score = self.properties.pe_scan(file_path)
if pe_label:
return pe_label
except Exception as e:
self.send_message(f"pe_scan | {e}", "warn", False)
try:
if self.pyas_config.get("extension_switch", False):
yara_label, yara_level = self.heuristic.yara_scan(file_path)
if yara_label:
return yara_label
except Exception as e:
self.send_message(f"yara_scan | {e}", "warn", False)
try:
if self.pyas_config.get("sensitive_switch", False):
cnn_label, cnn_score = self.pattern.model_scan(file_path)
if cnn_label:
return cnn_label
except Exception as e:
self.send_message(f"cnn_scan | {e}", "warn", False)
return False
####################################################################################################
@@ -931,8 +956,12 @@ class MainWindow_Controller(QMainWindow):
def perform_cloud_scan(self, file_path, get_result=False):
was_locked = False
try:
if not self.pyas_config.get("cloud_switch", True):
return False
if not os.path.exists(file_path):
return False
if not os.path.isfile(file_path):
return False
if file_path in self.virus_lock:
self.lock_file(file_path, False)
@@ -945,7 +974,7 @@ class MainWindow_Controller(QMainWindow):
return False
api_host = self.pyas_config.get("api_host", "https://pyas-security.com/")
api_key = self.pyas_config.get("api_key", "")
api_key = self.pyas_config.get("api_key", "fBRZxYS1UxykM-qzNOlKOEl63WILzlvgNMn6QfsG6FXCAAIktCrOPTAfY5_hEyuZ")
success, sha256 = self.cloud.upload_file(file_path, api_host, api_key)
if was_locked:
@@ -959,7 +988,7 @@ class MainWindow_Controller(QMainWindow):
self.send_message(f"雲端服務 | 通訊服務錯誤 | None | {file_path} | None", "warn", True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"perform_cloud_scan | {e}", "warn", False)
finally:
if 'was_locked' in locals() and was_locked:
try:
@@ -989,7 +1018,7 @@ class MainWindow_Controller(QMainWindow):
virus_list.takeItem(i)
self.virus_results.remove(file_path)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"solve_button | {e}", "warn", False)
QApplication.processEvents()
count = len(self.virus_results)
@@ -1047,7 +1076,7 @@ class MainWindow_Controller(QMainWindow):
widget.takeItem(widget.row(item))
self.virus_results.remove(file_path)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"show_scan_menu | {e}", "warn", False)
elif act == white_action:
file_path = self.norm_path(path)
@@ -1058,7 +1087,7 @@ class MainWindow_Controller(QMainWindow):
widget.takeItem(widget.row(item))
self.virus_results.remove(file_path)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"show_scan_menu | {e}", "warn", False)
elif act == quarantine_action:
file_path = self.norm_path(path)
@@ -1069,7 +1098,7 @@ class MainWindow_Controller(QMainWindow):
widget.takeItem(widget.row(item))
self.virus_results.remove(file_path)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"show_scan_menu | {e}", "warn", False)
count = len(self.virus_results)
self.widgets["solve_button"].setVisible(count > 0)
@@ -1171,7 +1200,7 @@ class MainWindow_Controller(QMainWindow):
n = self.manage_named_list("white_list", [target_path], action="add", with_hash=True)
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"show_process_menu | {e}", "warn", False)
elif act == quarantine_action:
target_path = self.norm_path(file_path)
@@ -1180,7 +1209,7 @@ class MainWindow_Controller(QMainWindow):
n = self.manage_named_list("quarantine", [target_path], action="add", with_hash=True, lock_func=self.lock_file)
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info", True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"show_process_menu | {e}", "warn", False)
def kill_process(self, pid):
try:
@@ -1193,7 +1222,7 @@ class MainWindow_Controller(QMainWindow):
self.kernel32.TerminateProcess(h, 0)
self.kernel32.CloseHandle(h)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"kill_process | {e}", "warn", False)
self.refresh_process()
####################################################################################################
@@ -1207,7 +1236,7 @@ class MainWindow_Controller(QMainWindow):
size_text = self.format_size(self.total_deleted_size)
self.send_message(f"成功清理了 {size_text} 系統垃圾", "info", True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"clean_button | {e}", "warn", False)
def traverse_temp(self, path):
try:
@@ -1230,7 +1259,7 @@ class MainWindow_Controller(QMainWindow):
except Exception:
continue
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"traverse_temp | {e}", "warn", False)
def format_size(self, size):
units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
@@ -1252,23 +1281,23 @@ class MainWindow_Controller(QMainWindow):
self.repair_system_image()
self.send_message("修復系統檔案成功", "info", True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_button | {e}", "warn", False)
def backup_mbr(self, max_drives=26):
self.mbr_backup = {}
mbr_signature = b"\x55\xAA"
for drive in range(max_drives):
drive_path = rf"\\.\PhysicalDrive{drive}"
try:
with open(drive_path, "rb") as f:
mbr = f.read(512)
if mbr[510:512] == b"\x55\xAA":
self.mbr_backup[drive] = mbr
except FileNotFoundError:
continue
except PermissionError as e:
if len(mbr) == 512 and mbr[510:512] == mbr_signature:
self.mbr_backup[drive] = mbr
except (FileNotFoundError, PermissionError, OSError):
continue
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(str(e), "warn", False)
def repair_system_mbr(self):
if not hasattr(self, "mbr_backup") or not self.mbr_backup:
@@ -1285,7 +1314,7 @@ class MainWindow_Controller(QMainWindow):
except PermissionError as e:
continue
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_system_mbr | {e}", "warn", False)
def repair_system_wallpaper(self):
try:
@@ -1314,7 +1343,7 @@ class MainWindow_Controller(QMainWindow):
pass
self.user32.SystemParametersInfoW(20, 0, wallpaper, 3)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_system_wallpaper | {e}", "warn", False)
def repair_system_restrict(self):
try:
@@ -1353,7 +1382,7 @@ class MainWindow_Controller(QMainWindow):
except FileNotFoundError:
continue
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_system_restrict | {e}", "warn", False)
def repair_system_file_type(self):
try:
@@ -1373,7 +1402,7 @@ class MainWindow_Controller(QMainWindow):
except Exception:
continue
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_system_file_type | {e}", "warn", False)
def repair_system_file_icon(self):
try:
@@ -1384,7 +1413,7 @@ class MainWindow_Controller(QMainWindow):
except Exception:
continue
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_system_file_icon | {e}", "warn", False)
def repair_system_image(self):
try:
@@ -1405,7 +1434,7 @@ class MainWindow_Controller(QMainWindow):
except OSError:
break
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"repair_system_image | {e}", "warn", False)
####################################################################################################
@@ -1434,7 +1463,7 @@ class MainWindow_Controller(QMainWindow):
self.send_message(f"系統防護 | 系統進程重啟 | None | None | {name}", "notify", True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"check_process_survival | {e}", "warn", False)
####################################################################################################
@@ -1522,7 +1551,7 @@ class MainWindow_Controller(QMainWindow):
self.kernel32.TerminateProcess(h, 0)
self.kernel32.CloseHandle(h)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"popup_intercept_thread | {e}", "warn", False)
####################################################################################################
@@ -1545,34 +1574,36 @@ class MainWindow_Controller(QMainWindow):
pid = ctypes.c_ulong()
self.user32.GetWindowThreadProcessId(hWnd, ctypes.byref(pid))
proc_name = ""
try:
proc_name = self.get_process_name(pid.value)
except Exception:
pass
proc_name = self.get_process_name(pid.value)
return str(title.value), str(class_name.value), proc_name
def get_process_name(self, pid):
name, file_path = self.get_exe_info(pid)
if name:
return name
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
entry = PROCESSENTRY32W()
entry.dwSize = ctypes.sizeof(PROCESSENTRY32W)
try:
result_name = ""
name, _ = self.get_exe_info(pid)
if name:
return name
result = ""
if self.kernel32.Process32FirstW(snapshot, ctypes.byref(entry)):
while True:
if entry.th32ProcessID == pid:
try:
result = entry.szExeFile
except Exception:
result = ""
break
if not self.kernel32.Process32NextW(snapshot, ctypes.byref(entry)):
break
self.kernel32.CloseHandle(snapshot)
return result
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
if snapshot == -1:
return result_name
entry = PROCESSENTRY32W()
entry.dwSize = ctypes.sizeof(PROCESSENTRY32W)
if self.kernel32.Process32FirstW(snapshot, ctypes.byref(entry)):
while True:
if entry.th32ProcessID == pid:
result_name = entry.szExeFile
break
if not self.kernel32.Process32NextW(snapshot, ctypes.byref(entry)):
break
self.kernel32.CloseHandle(snapshot)
return result_name
except:
pass
return result_name
####################################################################################################
@@ -1638,7 +1669,7 @@ class MainWindow_Controller(QMainWindow):
try:
lock_func(path, True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"manage_named_list | {e}", "warn", False)
self.config_list(list_key).append({"file": path, "hash": file_hash})
n += 1
@@ -1651,7 +1682,7 @@ class MainWindow_Controller(QMainWindow):
self.config_list(list_key).remove(item)
n += 1
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"manage_named_list | {e}", "warn", False)
if n:
self.save_config(self.file_config, self.pyas_config)
@@ -1674,15 +1705,7 @@ class MainWindow_Controller(QMainWindow):
webbrowser.open("https://pyas-security.com/antivirus")
return True
except Exception as e:
self.send_message(e, "warn", False)
return False
def cloud_button(self):
try:
webbrowser.open("https://pyas-security.com/analyze")
return True
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"website_button | {e}", "warn", False)
return False
def update_button(self):
@@ -1717,7 +1740,7 @@ class MainWindow_Controller(QMainWindow):
webbrowser.open(page)
return True
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"update_button | {e}", "warn", False)
return False
####################################################################################################
@@ -1734,7 +1757,7 @@ class MainWindow_Controller(QMainWindow):
os.close(self.virus_lock[file])
del self.virus_lock[file]
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"lock_file | {e}", "warn", False)
def relock_file(self):
quarantine_list = self.pyas_config.get("quarantine", [])
@@ -1744,7 +1767,7 @@ class MainWindow_Controller(QMainWindow):
try:
self.lock_file(file, True)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"relock_file | {e}", "warn", False)
####################################################################################################
@@ -1758,7 +1781,7 @@ class MainWindow_Controller(QMainWindow):
self.handle_new_process(pid)
self.exist_process = cur
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"protect_proc_thread | {e}", "warn", False)
def handle_new_process(self, pid):
try:
@@ -1774,10 +1797,19 @@ class MainWindow_Controller(QMainWindow):
seen = set()
paths = [p for p in paths if p and (p not in seen and not seen.add(p))]
suffix = self.pyas_config.get("suffix", [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui"])
for p in paths:
file_path = self.norm_path(self.device_path_to_drive(p)) if p else ""
if not file_path:
if not file_path or not os.path.isfile(file_path):
continue
ext = os.path.splitext(file_path)[-1].lower()
if ext not in suffix:
continue
if self.is_in_whitelist(file_path):
continue
if self.scan_engine(file_path):
@@ -1788,7 +1820,7 @@ class MainWindow_Controller(QMainWindow):
self.suspend_process(h, False)
self.kernel32.CloseHandle(h)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"handle_new_process | {e}", "warn", False)
def suspend_process(self, h_process, suspend):
try:
@@ -1802,7 +1834,7 @@ class MainWindow_Controller(QMainWindow):
else:
self.ntdll.NtResumeProcess(h_process)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"suspend_process | {e}", "warn", False)
####################################################################################################
@@ -1941,7 +1973,7 @@ class MainWindow_Controller(QMainWindow):
self.send_message(f"檔案防護 | 靜態掃描攔截 | None | None | {file_path}", "notify", True)
self.start_daemon_thread(self.cloud_check, file_path)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"protect_file_thread | {e}", "warn", False)
self.kernel32.CloseHandle(hDir)
####################################################################################################
@@ -1960,7 +1992,7 @@ class MainWindow_Controller(QMainWindow):
self.handle_new_connection(key)
self.exist_connections = conns
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"protect_net_thread | {e}", "warn", False)
self.exist_connections = set()
def get_connections_list(self):
@@ -1986,7 +2018,7 @@ class MainWindow_Controller(QMainWindow):
connections.add((row.dwOwningPid, row.dwRemoteAddr, row.dwRemotePort))
return connections
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"get_connections_list | {e}", "warn", False)
return set()
def handle_new_connection(self, key):
@@ -1999,12 +2031,12 @@ class MainWindow_Controller(QMainWindow):
file_path = self.norm_path(self.get_process_file(h))
if file_path and not self.is_in_whitelist(file_path):
if hasattr(self.rule, "network") and remote_ip in self.rule.network:
if hasattr(self.heuristic, "network") and remote_ip in self.heuristic.network:
self.kernel32.TerminateProcess(h, 0)
self.send_message(f"網路防護 | 規則列表攔截 | {pid} | {file_path} | {remote_ip}", "notify", True)
self.kernel32.CloseHandle(h)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"handle_new_connection | {e}", "warn", False)
####################################################################################################
@@ -2019,7 +2051,7 @@ class MainWindow_Controller(QMainWindow):
self.check_process_survival()
time.sleep(0.5)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"protect_system_thread | {e}", "warn", False)
####################################################################################################
@@ -2048,11 +2080,19 @@ class MainWindow_Controller(QMainWindow):
subprocess.run(["sc", "start", service_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
return True
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"install_system_driver | {e}", "warn", False)
return False
def stop_system_driver(self):
try:
if self.driver_port:
try:
self.kernel32.CloseHandle(self.driver_port)
self.driver_port = None
except Exception:
pass
time.sleep(0.5)
service_name = "PYAS_Driver"
subprocess.run(["sc", "stop", service_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
subprocess.run(["sc", "delete", service_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
@@ -2065,38 +2105,31 @@ class MainWindow_Controller(QMainWindow):
def pipe_server_thread(self):
try:
port_name = "\\PYAS_Output_Pipe"
h_port = ctypes.wintypes.HANDLE()
self.driver_port = ctypes.wintypes.HANDLE()
while self.pyas_config.get("driver_switch", False):
hr = self.fltlib.FilterConnectCommunicationPort(port_name, 0, None, 0, None, ctypes.byref(h_port))
hr = self.fltlib.FilterConnectCommunicationPort(port_name, 0, None, 0, None, ctypes.byref(self.driver_port))
if hr == 0:
message = PYAS_FULL_MESSAGE()
while self.pyas_config.get("driver_switch", False):
hr_get = self.fltlib.FilterGetMessage(h_port, ctypes.byref(message), ctypes.sizeof(PYAS_FULL_MESSAGE), None)
try:
hr_get = self.fltlib.FilterGetMessage(self.driver_port, ctypes.byref(message), ctypes.sizeof(PYAS_FULL_MESSAGE), None)
except OSError:
break
if hr_get == 0:
code = message.Data.MessageCode
pid = message.Data.ProcessId
raw_path = self.get_exe_info(pid)[1]
target = message.Data.Path
rule_name = "Unknown"
if code == 2001:
rule_name = "FILE_BLOCK"
elif code == 3001:
rule_name = "REG_BLOCK"
elif code == 4001:
rule_name = "BOOT_BLOCK"
elif code == 5001:
rule_name = "RANSOM_BLOCK"
elif code == 6001:
rule_name = "INJECT_BLOCK"
rule_name = self.block_rules.get(code, "Unknown")
display_rule = self.block_replace.get(rule_name, rule_name)
self.send_message(f"驅動防護 | {display_rule} | {pid} | {raw_path} | {target}", "notify", True)
if code in [4001, 5001]:
if code in self.pyas_config.get("block", []) and not self.is_in_whitelist(raw_path):
try:
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
if h:
@@ -2106,10 +2139,12 @@ class MainWindow_Controller(QMainWindow):
pass
else:
break
self.kernel32.CloseHandle(h_port)
if self.driver_port:
self.kernel32.CloseHandle(self.driver_port)
self.driver_port = None
time.sleep(0.2)
except Exception as e:
self.send_message(e, "warn", False)
self.send_message(f"pipe_server_thread | {e}", "warn", False)
####################################################################################################
+1 -1
View File
@@ -27,7 +27,7 @@ translate_dict = {
"腳本":"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 ",
+338 -15
View File
@@ -1,4 +1,4 @@
import os, yara, time, numpy, base64, requests
import os, yara, time, numpy, base64, requests, pefile, math
import ctypes, ctypes.wintypes, hashlib, onnxruntime
from PIL import Image
@@ -94,6 +94,8 @@ class rule_scanner:
if yara_files:
self.compile_all_rules(yara_files)
####################################################################################################
def load_compiled_rule(self, file):
try:
self.rules = yara.load(file)
@@ -113,6 +115,8 @@ class rule_scanner:
except Exception:
pass
####################################################################################################
def yara_scan(self, file_path):
try:
if not self.rules:
@@ -121,21 +125,20 @@ class rule_scanner:
matches = self.rules.match(filepath=file_path)
if matches:
rule_name = str(matches[0])
types = rule_name.split("_")[0]
label = rule_name.split("_")[1]
level = rule_name.split("_")[2]
return f"{types}/{label}", level
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.models = []
self.labels = ["Pefile/White", "Pefile/General"]
self.detect_set = {"Pefile/General"}
self.labels = ["White:WinPE/Unknown", "General:WinPE/Unknown"]
self.detect_set = {"General:WinPE/Unknown"}
self.resize = (224, 224)
def load_path(self, path, callback=None):
@@ -149,11 +152,13 @@ class model_scanner:
def load_file(self, file):
if file.lower().endswith('.onnx'):
try:
session = 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.models:
return (False, False) if not full_output else ([], str(file_path), None)
@@ -186,20 +191,23 @@ class model_scanner:
try:
probs = model.run(None, {input_name: curr_arr})[0]
pred = probs[0]
pred_prob = float(probs[0][0]) if hasattr(probs[0], '__len__') else float(probs[0])
except Exception:
continue
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)
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(("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]))
@@ -209,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')
@@ -232,6 +242,315 @@ class model_scanner:
####################################################################################################
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
@@ -246,6 +565,8 @@ class cloud_scanner:
except Exception:
return ""
####################################################################################################
def upload_file(self, file_path, api_host, api_key):
try:
sha256 = self.calc_hash(file_path)
@@ -276,6 +597,8 @@ class cloud_scanner:
except Exception:
return False, None
####################################################################################################
def get_result(self, sha256, api_host, api_key):
try:
if not sha256:
@@ -316,7 +639,7 @@ class cloud_scanner:
sim_malicious_count += 1
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
return True
return f"General:WinPE/Unknown.{score}!cl"
except Exception:
pass
return False
+16 -13
View File
@@ -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
View File
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
pyinstaller_versionfile.create_versionfile(
output_file="versionfile.txt",
version='3.3.8',
version='3.4.0',
company_name="PYAS Security",
file_description="Python Antivirus Software",
internal_name="PYAS",
+24 -1
View File
@@ -100,12 +100,29 @@ typedef struct _DRIVER_DATA {
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();
@@ -113,9 +130,15 @@ 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);
@@ -123,7 +146,7 @@ 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 Buffer, ULONG Length, BOOLEAN IsWrite);
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
+17 -3
View File
@@ -27,11 +27,20 @@ static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTA
}
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
UNREFERENCED_PARAMETER(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);
@@ -81,6 +90,7 @@ 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 }
@@ -103,8 +113,6 @@ CONST FLT_REGISTRATION FilterRegistration = {
};
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status = STATUS_SUCCESS;
PSECURITY_DESCRIPTOR sd = NULL;
OBJECT_ATTRIBUTES oa = { 0 };
@@ -117,6 +125,12 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
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;
+57 -5
View File
@@ -40,9 +40,16 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_R
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)) {
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;
@@ -69,7 +76,8 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_R
if (CheckProtectedPathRule(&FileName)) {
if (!Trusted) {
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (FILE_WRITE_DATA | DELETE | WRITE_DAC | GENERIC_WRITE)) {
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;
@@ -126,7 +134,15 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
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;
@@ -135,17 +151,53 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_
}
}
}
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(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) {
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)) {
+4 -1
View File
@@ -9,7 +9,10 @@ static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
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\\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;
+218 -16
View File
@@ -2,38 +2,240 @@
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 == RegNtPreSetValueKey || Class == RegNtPreDeleteKey || Class == RegNtPreCreateKey) {
REG_SET_VALUE_KEY_INFORMATION* Info = (REG_SET_VALUE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (Class == RegNtPreCreateKey || Class == RegNtPreOpenKey || Class == RegNtPreOpenKeyEx) {
PVOID RootObject = NULL;
PUNICODE_STRING CompleteName = NULL;
ACCESS_MASK DesiredAccess = 0;
if (!NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 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;
}
if (KeyName && KeyName->Buffer) {
if (CheckRegistryRule(KeyName)) {
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)) {
if (!IsInstallerProcess(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
CmCallbackReleaseKeyObjectIDEx(KeyName);
return STATUS_ACCESS_DENIED;
}
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(FullPathBuffer);
}
if (KeyName) {
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
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;
}
File diff suppressed because it is too large. Load diff
+217
View File
@@ -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"
]
}
+108 -105
View File
@@ -9,14 +9,15 @@ Antivirus software written in Python and C++ that blocks threats through deep le
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,25 +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
│ │ └── ... # Other models folders and files
│ │
│ └── Rules/
│ ├── rules.yar # Yara virus signature rule matching
│ ├── rules.ips # IP network address rule matching
│ └── ... # Other rules folders and files
│ ├── Heuristic/
│ │ ├── rules.yar # Yara virus signature rule matching
│ │ └── ... # Other rules folders and files
│ └── ... # Other engine folders and files
│
├── Plugins/
│ └── Filter/
│ ├── DriverEntry.cpp # Main driver entry and initialization logic
│ ├── DriverCommon.h # Global driver definitions, constants, and functions
│ ├── ProtectBoot.cpp # Disk boot sector write protection
│ ├── ProtectRegistry.cpp # Registry modification protection
│ ├── ProtectRules.cpp # White, block list, and matching logic for files registry
│ └── ... # Other driver folders and files
│
│ │ ├── 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
@@ -61,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
@@ -185,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.