Compare commits

...
154 Commits
Author SHA1 Message Date
87owo dc498949c7 Add files via upload 2026-06-16 04:27:36 +08:00
87owo f804a5b7f0 Add files via upload 2026-06-16 04:20:08 +08:00
87owo b0a3482f25 Add files via upload 2026-06-13 13:04:04 +08:00
87owo 018b497575 Add files via upload 2026-06-09 16:45:28 +08:00
87owo 130b040de1 Add files via upload 2026-06-09 16:08:44 +08:00
87owo bc0a62224a Add files via upload 2026-06-09 15:59:16 +08:00
87owo 77525fc7c5 Add files via upload 2026-06-09 15:15:53 +08:00
87owo a304a2f02d Add files via upload 2026-06-09 13:49:52 +08:00
87owo 9456659544 Add files via upload 2026-06-09 13:49:23 +08:00
87owo 3ec340e0da Add files via upload 2026-06-09 13:48:47 +08:00
87owo f1f35badcb Update LICENSE.txt 2026-06-09 04:08:32 +08:00
87owo 045160c482 Update LICENSE.txt 2026-06-09 04:06:41 +08:00
87owo 559c493997 Update LICENSE.txt 2026-06-09 04:03:01 +08:00
87owo 936475f5bf Merge pull request #63 from WBT907/main
Fixed a bug where PYAS process protection was not working. (To be verified)
2026-06-08 10:41:36 +08:00
wangbotao e97f8eed54 修复 PYAS 进程保护失效 Bug
修复任务管理器可以成功终止 PYAS 进程的问题。原代码中进程保护完全依赖于 GlobalData.PyasPid ,而该变量只有在用户模式程序成功连接到驱动通信端口后才会被设置。如果连接未建立或断开, PyasPid 为 0,导致 IsTargetProtected() 始终返回 FALSE ,进程保护完全失效。
2026-06-07 20:05:38 +08:00
87owo eb1955779b Add files via upload 2026-06-06 12:39:10 +08:00
87owo 2e5b4cc5a1 Add files via upload 2026-06-05 21:25:07 +08:00
87owo 38b4f96437 Add files via upload 2026-06-05 21:24:24 +08:00
87owo 173922d0ce Add files via upload 2026-06-05 19:21:52 +08:00
87owo 99303a237e Add files via upload 2026-06-05 14:30:56 +08:00
87owo 8f7baf2790 Add files via upload 2026-06-05 13:49:43 +08:00
87owo b673336b3f Add files via upload 2026-06-01 19:41:23 +08:00
87owo e37400a0f3 Add files via upload 2026-06-01 18:58:54 +08:00
87owo 7b9474521d Add files via upload 2026-06-01 17:37:51 +08:00
87owo 78c3f2c9b8 Add files via upload 2026-06-01 16:59:04 +08:00
87owo ad256abb7c Add files via upload 2026-06-01 13:59:39 +08:00
87owo 86d160ae5f Add files via upload 2026-06-01 12:55:09 +08:00
87owo fde6afb39e Add files via upload 2026-05-31 23:59:07 +08:00
87owo 7c3e78296a Add files via upload 2026-05-31 23:35:58 +08:00
87owo 9cc06ff767 Add files via upload 2026-05-31 23:09:51 +08:00
87owo 766b6e81d0 Add files via upload 2026-05-28 16:20:20 +08:00
87owo 0349e01629 Add files via upload 2026-05-28 15:53:52 +08:00
87owo 0bd9da2bbc Add files via upload 2026-05-28 14:31:24 +08:00
87owo f14c7c3f55 Add files via upload 2026-05-28 14:05:15 +08:00
87owo ef5793fb15 Add files via upload 2026-05-26 09:30:27 +08:00
87owo 9f22fe6d97 Add files via upload 2026-05-26 09:29:51 +08:00
87owo 3147d576ee Add files via upload 2026-05-25 22:53:18 +08:00
87owo 2ceec2588c Add files via upload 2026-05-25 14:19:26 +08:00
87owo 5b4d88b9c7 Add files via upload 2026-05-25 14:18:52 +08:00
87owo 55043cca36 Add files via upload 2026-05-24 02:44:05 +08:00
87owo 097ebe025f Add files via upload 2026-05-23 14:10:33 +08:00
87owo 9f92307f64 Add files via upload 2026-05-22 13:49:52 +08:00
87owo c220035fd9 Add files via upload 2026-05-21 00:27:56 +08:00
87owo d7b98d3cde Add files via upload 2026-05-21 00:27:38 +08:00
87owo 0ab876633f Add files via upload 2026-05-21 00:27:01 +08:00
87owo cabe76ca54 Add files via upload 2026-05-21 00:26:14 +08:00
87owo c57224e258 Add files via upload 2026-05-21 00:25:35 +08:00
87owo b56101d2e6 Add files via upload 2026-05-18 21:31:43 +08:00
87owo 16211c28d1 Add files via upload 2026-05-17 16:48:13 +08:00
87owo 139d3e0c3d Add files via upload 2026-05-17 16:47:14 +08:00
87owo f0652fc5ec Add files via upload 2026-05-17 01:32:52 +08:00
87owo 55489cd634 Add files via upload 2026-05-17 01:31:49 +08:00
87owo 742887c0e4 Add files via upload 2026-05-15 22:34:23 +08:00
87owo c3e5cd2c0d Add files via upload 2026-05-15 22:33:13 +08:00
87owo 63e125627c Update README.md 2026-05-15 14:39:15 +08:00
87owo c0189e9d05 Add files via upload 2026-05-14 22:31:46 +08:00
87owo 133e3208ec Add files via upload 2026-05-14 20:02:53 +08:00
87owo 17c6fd6d0f Add files via upload 2026-05-13 18:37:07 +08:00
87owo 2aa7ea43b8 Add files via upload 2026-05-13 17:56:05 +08:00
87owo 6d7dbfac31 Add files via upload 2026-05-13 00:51:04 +08:00
87owo d4942c6dd6 Add files via upload 2026-05-13 00:49:54 +08:00
87owo 39ae3c24f6 Add files via upload 2026-05-12 22:41:11 +08:00
87owo a04d74a949 Add files via upload 2026-05-10 17:54:55 +08:00
87owo b93a7ebd94 Add files via upload 2026-05-10 17:45:08 +08:00
87owo cbc7b0340f Add files via upload 2026-05-10 17:38:28 +08:00
87owo ea7a197848 Add files via upload 2026-05-10 17:31:14 +08:00
87owo 82d4df889e Add files via upload 2026-05-10 13:06:24 +08:00
87owo df3a2a1010 Add files via upload 2026-05-10 13:06:05 +08:00
87owo 67cd6c2f59 Add files via upload 2026-05-10 02:30:07 +08:00
87owo b1788e7df9 Add files via upload 2026-05-10 02:28:27 +08:00
87owo 1cb8a81518 Add files via upload 2026-05-10 00:06:45 +08:00
87owo e1b61f2675 Update README.md 2026-05-09 22:53:01 +08:00
87owo 834d44856c Add files via upload 2026-05-09 22:49:48 +08:00
87owo c088eeb5bc Add files via upload 2026-05-09 22:47:09 +08:00
87owo 921510b7b7 Add files via upload 2026-05-09 22:27:36 +08:00
87owo 17a54f7a8a Add files via upload 2026-05-08 20:02:34 +08:00
87owo 43ff924efc Add files via upload 2026-05-08 18:24:36 +08:00
87owo f9bd4b6628 Add files via upload 2026-05-08 18:20:52 +08:00
87owo 702aa22139 Delete Interface/static/js/style.css 2026-05-08 18:20:37 +08:00
87owo 57d53f63e5 Add files via upload 2026-05-08 18:17:20 +08:00
87owo 835ada4a53 Add files via upload 2026-05-08 13:51:08 +08:00
87owo 7f204f3e1b Add files via upload 2026-05-08 10:53:11 +08:00
87owo 70e13b8414 Delete Engine/Properties/train_server_lfp.py 2026-05-07 14:13:19 +08:00
87owo df9bb8cbb8 Add files via upload 2026-05-07 14:12:56 +08:00
87owo cebdb32502 Add files via upload 2026-05-07 13:58:44 +08:00
87owo 58bd61343a Add files via upload 2026-05-06 14:27:54 +08:00
87owo 90faf1007e Add files via upload 2026-05-06 14:14:48 +08:00
87owo f8dbd12b69 Add files via upload 2026-05-06 12:54:54 +08:00
87owo cf6ba66dd2 Add files via upload 2026-05-04 23:29:36 +08:00
87owo 2080b5d10a Add files via upload 2026-05-04 23:29:14 +08:00
87owo dd180977d9 Add files via upload 2026-05-04 23:28:48 +08:00
87owo 221dcbaf1b Add files via upload 2026-05-04 23:28:13 +08:00
87owo 1a2098f4fb Add files via upload 2026-05-04 23:27:50 +08:00
87owo 73eab17110 Add files via upload 2026-05-03 11:34:05 +08:00
87owo 978a336358 Update README.md 2026-05-03 03:27:57 +08:00
87owo 6a0fd4e427 Update README.md 2026-05-03 03:23:58 +08:00
87owo 5fd4efd920 Add files via upload 2026-05-03 03:20:31 +08:00
87owo 43be2987f5 Add files via upload 2026-05-02 17:47:38 +08:00
87owo 06cc048fdc Add files via upload 2026-05-02 17:47:03 +08:00
87owo 5752471420 Add files via upload 2026-05-02 16:16:46 +08:00
87owo 3dfe12b90a Add files via upload 2026-05-02 15:48:47 +08:00
87owo ad1e3501b1 Add files via upload 2026-05-02 15:33:52 +08:00
87owo 1b46572e4c Add files via upload 2026-05-02 15:09:42 +08:00
87owo 9d8e42d34c Add files via upload 2026-05-02 15:09:28 +08:00
87owo 3f6e9dab3e Add files via upload 2026-05-02 15:09:06 +08:00
87owo ad85f11875 Add files via upload 2026-05-02 01:49:17 +08:00
87owo 9ae31834cc Add files via upload 2026-05-02 01:16:32 +08:00
87owo 49a4dc6942 Add files via upload 2026-05-02 01:16:05 +08:00
87owo a9f58cc30f Add files via upload 2026-05-02 01:15:51 +08:00
87owo bdb2198582 Add files via upload 2026-05-02 01:15:28 +08:00
87owo f32a77ca8b Add files via upload 2026-05-01 23:16:30 +08:00
87owo 121d3fb0ae Add files via upload 2026-05-01 23:15:59 +08:00
87owo 1dd72f7600 Add files via upload 2026-05-01 23:15:22 +08:00
87owo c7c168b007 Add files via upload 2026-05-01 23:14:45 +08:00
87owo 441b41c6af Add files via upload 2026-05-01 23:13:56 +08:00
87owo 49a907283a Add files via upload 2026-05-01 15:41:33 +08:00
87owo 0ffc52f9ba Add files via upload 2026-05-01 13:10:33 +08:00
87owo 4e96ec49e7 Update README.md 2026-04-29 21:04:53 +08:00
87owo dfb4646f51 Delete Embed directory 2026-04-29 21:04:30 +08:00
87owo a8e82c38c9 Add files via upload 2026-04-29 18:09:24 +08:00
87owo dba23289fd Add files via upload 2026-04-29 17:41:00 +08:00
87owo c2c1eb98d9 Add files via upload 2026-04-29 16:19:44 +08:00
87owo 364176912d Add files via upload 2026-04-28 14:38:35 +08:00
87owo 202245baad Add files via upload 2026-04-28 14:10:07 +08:00
87owo c3fa99640c Add files via upload 2026-04-27 15:03:53 +08:00
87owo a7c34b5a6a Add files via upload 2026-04-27 10:19:47 +08:00
87owo 05de092642 Update README.md 2026-04-26 22:35:28 +08:00
87owo 1bfa40c841 Add files via upload 2026-04-26 21:13:42 +08:00
87owo c325435ae8 Add files via upload 2026-04-26 15:41:47 +08:00
87owo b978bd41c3 Add files via upload 2026-04-26 14:31:23 +08:00
87owo f03d8733b0 Add files via upload 2026-04-26 14:30:49 +08:00
87owo a624b3b759 Update README.md 2026-04-26 14:29:57 +08:00
87owo d683398ba9 Add files via upload 2026-04-26 14:28:24 +08:00
87owo 42f692b82d Add files via upload 2026-04-26 14:27:33 +08:00
87owo 855fe9115e Delete Engine/Pattern directory 2026-04-26 14:26:30 +08:00
87owo d6bd383822 Add files via upload 2026-04-26 02:13:58 +08:00
87owo b1b77b73ac Add files via upload 2026-04-25 21:45:46 +08:00
87owo 8d87adcc50 Add files via upload 2026-04-25 21:45:32 +08:00
87owo 49bf6c54e7 Delete Interface/main.py 2026-04-25 20:53:22 +08:00
87owo 7e92e0b1d9 Add files via upload 2026-04-25 20:52:55 +08:00
87owo f0805d320c Add files via upload 2026-04-25 19:19:36 +08:00
87owo 364f48a6f0 Update README.md 2026-04-25 16:55:34 +08:00
87owo cfebf6df0e Update README.md 2026-04-25 16:40:38 +08:00
87owo 688855b72d Update README.md 2026-04-25 16:38:08 +08:00
87owo 77b275a04b Update README.md 2026-04-25 16:30:11 +08:00
87owo 98b142df12 Update README.md 2026-04-25 16:01:11 +08:00
87owo c5fe845f18 Delete PYAS_Resource.py 2026-04-25 15:59:56 +08:00
87owo e5e9629d31 Delete PYAS_Interface.py 2026-04-25 15:59:48 +08:00
87owo b65b9e6bfb Delete PYAS_Config.py 2026-04-25 15:59:26 +08:00
87owo 93ae8b02e3 Add files via upload 2026-04-25 15:55:56 +08:00
87owo 5c183ea200 Add files via upload 2026-04-25 15:54:58 +08:00
87owo 661765f08c Add files via upload 2026-04-25 15:54:33 +08:00
87owo 60fa19c681 Add files via upload 2026-04-25 15:53:27 +08:00
87owo 606eddba53 Add files via upload 2026-04-21 14:56:52 +08:00
36 changed files with 11003 additions and 21651 deletions

No files matched your search

-16
View File
@@ -1,16 +0,0 @@
FROM python:3.10-slim
RUN apt-get update && apt-get install -y \
build-essential \
libfuzzy-dev \
git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "-u", "predict_onnx_linux.py"]
-1
View File
@@ -1 +0,0 @@
["FileEntropy", "SectionMeanEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit", "Machine", "Magic", "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", "TextSectionMaxEntropy", "TextSectionMeanEntropy", "TextSizeRatio", "DataSectionMaxEntropy", "DataSectionMeanEntropy", "DataSizeRatio", "RsrcSectionMaxEntropy", "RsrcSectionMeanEntropy", "RsrcSizeRatio", "SectionCount", "ExecutableSections", "WritableSections", "ReadableSections", "SectionException", "Char_00000020_Count", "Char_00000020_MeanEntropy", "Char_00000040_Count", "Char_00000040_MeanEntropy", "Char_00000080_Count", "Char_00000080_MeanEntropy", "Char_02000000_Count", "Char_02000000_MeanEntropy", "Char_20000000_Count", "Char_20000000_MeanEntropy", "Char_40000000_Count", "Char_40000000_MeanEntropy", "Char_80000000_Count", "Char_80000000_MeanEntropy", "IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount", "ImportCount", "ImportFunctionCount", "FileDescriptionLength", "FileVersionLength", "ProductNameLength", "ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength", "CommentsLength", "InternalNameLength", "LegalTrademarksLength", "SpecialBuildLength", "PrivateBuildLength", "TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease", "IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks", "HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException", "Cat_AntiDebug", "Cat_Audio", "Cat_COM", "Cat_Camera", "Cat_Clipboard", "Cat_DataObfuscation", "Cat_DotNet", "Cat_Encryption", "Cat_FileIO", "Cat_Graphics", "Cat_Injection", "Cat_Input", "Cat_Keylogging", "Cat_Memory", "Cat_MultiThreading", "Cat_Native", "Cat_Network", "Cat_Privileges", "Cat_ProcessControl", "Cat_Registry", "Cat_Resource", "Cat_ScreenCapture", "Cat_Services", "Cat_Synchronization", "Cat_WindowControl", "Dll_advapi32_dll", "Dll_bcrypt_dll", "Dll_comctl32_dll", "Dll_crypt32_dll", "Dll_dbghelp_dll", "Dll_gdi32_dll", "Dll_hal_dll", "Dll_imagehlp_dll", "Dll_iphlpapi_dll", "Dll_kernel32_dll", "Dll_mscoree_dll", "Dll_netapi32_dll", "Dll_ntdll_dll", "Dll_ole32_dll", "Dll_oleaut32_dll", "Dll_psapi_dll", "Dll_shell32_dll", "Dll_shlwapi_dll", "Dll_urlmon_dll", "Dll_user32_dll", "Dll_vbscript_dll", "Dll_version_dll", "Dll_winhttp_dll", "Dll_wininet_dll", "Dll_winmm_dll", "Dll_ws2_32_dll", "Dll_wsock32_dll", "Dll_wtsapi32_dll", "Api_AdjustTokenPrivileges", "Api_Base64Decode", "Api_BitBlt", "Api_CallNextHookEx", "Api_CallWindowProcA", "Api_CapCreateCaptureWindowA", "Api_CapCreateCaptureWindowW", "Api_CharLowerA", "Api_CharUpperA", "Api_CheckRemoteDebuggerPresent", "Api_CloseClipboard", "Api_CloseHandle", "Api_CoCreateInstance", "Api_CoInitialize", "Api_CoUninitialize", "Api_CompareStringA", "Api_ConnectNamedPipe", "Api_ControlService", "Api_CopyFileA", "Api_CopyFileW", "Api_CreateCompatibleBitmap", "Api_CreateCompatibleDC", "Api_CreateDCA", "Api_CreateDCW", "Api_CreateEventA", "Api_CreateEventW", "Api_CreateFileA", "Api_CreateFileMappingA", "Api_CreateFileMappingW", "Api_CreateFileW", "Api_CreateMutexA", "Api_CreateMutexW", "Api_CreateProcessA", "Api_CreateProcessW", "Api_CreateRemoteThread", "Api_CreateServiceA", "Api_CreateServiceW", "Api_CreateThread", "Api_CreateThreadpoolWork", "Api_CreateWindowExA", "Api_CryptAcquireContextA", "Api_CryptAcquireContextW", "Api_CryptCreateHash", "Api_CryptDecodeObject", "Api_CryptDecrypt", "Api_CryptDeriveKey", "Api_CryptDestroyHash", "Api_CryptDestroyKey", "Api_CryptEncrypt", "Api_CryptExportKey", "Api_CryptGenKey", "Api_CryptHashData", "Api_CryptImportKey", "Api_CryptReleaseContext", "Api_D3D11CreateDevice", "Api_DecodePointer", "Api_DefWindowProcA", "Api_DeleteCriticalSection", "Api_DeleteDC", "Api_DeleteFileA", "Api_DeleteFileW", "Api_DeleteObject", "Api_DeleteService", "Api_DestroyWindow", "Api_DeviceIoControl", "Api_Direct3DCreate9", "Api_DispatchMessageA", "Api_DnsQuery_A", "Api_DnsQuery_W", "Api_EncodePointer", "Api_EnterCriticalSection", "Api_EnumChildWindows", "Api_EnumWindows", "Api_ExitProcess", "Api_ExitThread", "Api_FindClose", "Api_FindFirstFileA", "Api_FindNextFileA", "Api_FindResourceA", "Api_FindWindowA", "Api_FindWindowW", "Api_FlushFileBuffers", "Api_FreeEnvironmentStringsW", "Api_FreeLibrary", "Api_FreeResource", "Api_GdipSaveImageToFile", "Api_GetACP", "Api_GetActiveWindow", "Api_GetAsyncKeyState", "Api_GetCapture", "Api_GetClientRect", "Api_GetClipboardData", "Api_GetCommandLineA", "Api_Line truncated
BIN
View File
Binary file not shown.
-611
View File
@@ -1,611 +0,0 @@
import os, sys, time, re, math, pefile, json, datetime, mmap
import numpy as np
import onnxruntime as ort
from signify.authenticode.signed_file.pe import SignedPEFile
from signify.authenticode.verification_result import AuthenticodeVerificationResult
####################################################################################################
MODEL_FILE = "model.onnx"
FEATURE_FILE = "features.json"
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
####################################################################################################
class WinTrust:
@staticmethod
def verify(file_path):
if not file_path or not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
return 0
try:
with open(file_path, 'rb') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
pe = SignedPEFile(mm)
if not pe.signatures:
return 0
status, _ = pe.explain_verify()
if status == AuthenticodeVerificationResult.OK:
return 1
except Exception:
pass
return 0
####################################################################################################
class FeatureExtractor:
API_CAT_MAPPING = {
'ProcessControl': {
'CreateProcessA', 'CreateProcessW', 'WinExec', 'ShellExecuteA', 'ShellExecuteW',
'ShellExecuteExW', 'ExitProcess', 'TerminateProcess', 'OpenProcess', 'GetExitCodeProcess',
'SetThreadPriority', 'GetThreadPriority', 'GetCurrentProcess', 'GetCurrentProcessId',
'GetModuleFileNameA', 'GetCommandLineA', 'GetModuleFileNameW', 'GetStartupInfoW'
},
'Injection': {
'VirtualAlloc', 'VirtualAllocEx', 'VirtualProtect', 'VirtualProtectEx', 'WriteProcessMemory',
'ReadProcessMemory', 'CreateRemoteThread', 'QueueUserAPC', 'SetThreadContext', 'GetThreadContext',
'Wow64SetThreadContext', 'NtUnmapViewOfSection', 'ZwUnmapViewOfSection', 'RtlCreateUserThread',
'SetWindowsHookExA', 'SetWindowsHookExW', 'UnhookWindowsHookEx', 'LoadLibraryA', 'LoadLibraryW',
'LoadLibraryExA', 'LoadLibraryExW', 'GetProcAddress', 'GetModuleHandleA', 'GetModuleHandleW',
'FreeLibrary', 'CreateFileMappingA', 'CreateFileMappingW', 'MapViewOfFile', 'UnmapViewOfSection',
'VirtualFree', 'VirtualQuery'
},
'Synchronization': {
'WaitForSingleObject', 'WaitForSingleObjectEx', 'WaitForMultipleObjects', 'WaitForMultipleObjectsEx',
'CreateMutexA', 'CreateMutexW', 'OpenMutexW', 'ReleaseMutex', 'CreateEventA', 'CreateEventW',
'OpenEventW', 'SetEvent', 'ResetEvent', 'EnterCriticalSection', 'LeaveCriticalSection',
'InitializeCriticalSection', 'DeleteCriticalSection', 'Sleep', 'SleepEx',
'InitializeCriticalSectionAndSpinCount', 'InterlockedDecrement', 'InterlockedIncrement',
'InterlockedExchange', 'InterlockedCompareExchange', 'SetTimer', 'KillTimer'
},
'MultiThreading': {
'CreateThread', 'ResumeThread', 'SuspendThread', 'ExitThread', 'TerminateThread',
'GetCurrentThread', 'GetCurrentThreadId', 'TlsAlloc', 'TlsSetValue', 'TlsGetValue',
'CreateThreadpoolWork', 'SubmitThreadpoolWork', 'TlsFree'
},
'Network': {
'socket', 'connect', 'send', 'recv', 'bind', 'listen', 'accept', 'gethostbyname', 'getaddrinfo',
'WSAStartup', 'WSACleanup', 'WSAIoctl', 'WSASocketA', 'WSASocketW', 'closesocket', 'htons',
'InternetOpenA', 'InternetOpenW', 'InternetConnectA', 'InternetConnectW', 'InternetOpenUrlA',
'InternetOpenUrlW', 'HttpOpenRequestA', 'HttpOpenRequestW', 'HttpSendRequestA', 'HttpSendRequestW',
'InternetReadFile', 'URLDownloadToFileA', 'URLDownloadToFileW', 'DnsQuery_A', 'DnsQuery_W'
},
'Encryption': {
'CryptAcquireContextA', 'CryptAcquireContextW', 'CryptCreateHash', 'CryptHashData',
'CryptDeriveKey', 'CryptEncrypt', 'CryptDecrypt', 'CryptDestroyKey', 'CryptDestroyHash',
'CryptReleaseContext', 'CryptGenKey', 'CryptImportKey', 'CryptExportKey'
},
'DataObfuscation': {
'RtlDecompressBuffer', 'MultiByteToWideChar', 'WideCharToMultiByte', 'Base64Decode',
'CryptDecodeObject', 'IsDBCSLeadByte', 'CharUpperA', 'CharLowerA',
'GetStringTypeW', 'LCMapStringW', 'IsValidCodePage', 'DecodePointer', 'EncodePointer'
},
'FileIO': {
'CreateFileA', 'CreateFileW', 'WriteFile', 'ReadFile', 'DeleteFileA', 'DeleteFileW',
'CopyFileA', 'CopyFileW', 'MoveFileA', 'MoveFileW', 'FindFirstFileA', 'FindNextFileA',
'GetTempPathA', 'GetTempPathW', 'GetTempFileNameA', 'GetTempFileNameW', 'SetFileAttributesA',
'SetFileAttributesW', 'DeviceIoControl', 'SetFileTime', 'GetFileSize', 'GetFileSizeEx',
'SetFilePointer', 'FlushFileBuffers', 'ConnectNamedPipe', 'PeekNamedPipe',
'CloseHandle', 'GetFileType', 'SetStdHandle', 'SetFilePointerEx', 'FindClose',
'SetHandleCount', 'SetEndOfFile', 'GetFullPathNameA', 'GetFileAttributesA'
},
'Registry': {
'RegOpenKeyExA', 'RegOpenKeyExW', 'RegSetValueExA', 'RegSetValueExW',
'RegCreateKeyExA', 'RegCreateKeyExW', 'RegDeleteKeyA', 'RegDeleteKeyW',
'RegEnumValueA', 'RegEnumValueW', 'RegQueryValueExA', 'RegQueryValueExW',
'RegDeleteValueA', 'RegDeleteValueW', 'RegCloseKey'
},
'Services': {
'OpenSCManagerA', 'OpenSCManagerW', 'CreateServiceA', 'CreateServiceW',
'StartServiceA', 'StartServiceW', 'ControlService', 'DeleteService'
},
'Privileges': {
'AdjustTokenPrivileges', 'LookupPrivilegeValueA', 'LookupPrivilegeValueW',
'OpenProcessToken', 'NetUserAdd', 'NetLocalGroupAddMembers', 'IsAdmin',
'GetUserNameA', 'GetUserNameW'
},
'Native': {
'RtlUnwind', 'RtlVirtualUnwind', 'RtlCaptureContext', 'RtlLookupFunctionEntry',
'NtClose', 'NtQueryInformationProcess', 'RtlAllocateHeap', 'RtlFreeHeap',
'GetSystemTimeAsFileTime', 'GetLocalTime', 'GlobalMemoryStatus',
'GetVersionExA', 'GetVersionExW', 'GetComputerNameA', 'GetComputerNameW',
'GetLastError', 'GetACP', 'RaiseException', 'SetLastError', 'GetEnvironmentStringsW',
'FreeEnvironmentStringsW', 'GetLocaleInfoA', 'lstrlenA', 'GetVersion', 'GetSystemInfo',
'CompareStringA'
},
'DotNet': {
'_CorExeMain', '_CorDllMain'
},
'AntiDebug': {
'IsDebuggerPresent', 'CheckRemoteDebuggerPresent', 'OutputDebugStringA', 'OutputDebugStringW',
'GetTickCount', 'GetTickCount64', 'QueryPerformanceCounter', 'FindWindowA', 'FindWindowW',
'EnumWindows', 'EnumChildWindows', 'GetWindowRect', 'GetClientRect', 'SetWindowDisplayAffinity',
'UnhandledExceptionFilter', 'SetUnhandledExceptionFilter', 'IsProcessorFeaturePresent',
'SetErrorMode', 'GetTimeZoneInformation', 'GetDriveTypeW', 'GetDiskFreeSpaceA'
},
'Keylogging': {
'GetAsyncKeyState', 'GetKeyState', 'GetKeyboardState', 'MapVirtualKeyA', 'MapVirtualKeyW',
'ToAscii', 'ToAsciiEx', 'ToUnicode', 'ToUnicodeEx', 'GetKeyNameTextA', 'GetForegroundWindow',
'KeybdEvent', 'SendInput', 'MapVirtualKeyExA', 'MapVirtualKeyExW', 'CallNextHookEx'
},
'Input': {
'GetCursorPos', 'SetCursorPos', 'MouseEvent', 'GetDoubleClickTime',
'GetCapture', 'SetCapture'
},
'ScreenCapture': {
'BitBlt', 'StretchBlt', 'GetDC', 'GetWindowDC', 'CreateCompatibleDC', 'CreateCompatibleBitmap',
'GdipSaveImageToFile', 'PrintWindow', 'GetDesktopWindow', 'CreateDCW', 'CreateDCA',
'SelectObject', 'DeleteDC', 'DeleteObject', 'GetDeviceCaps', 'GetSystemMetrics'
},
'Graphics': {
'Direct3DCreate9', 'D3D11CreateDevice'
},
'Audio': {
'WaveInOpen', 'WaveInClose', 'WaveInStart', 'WaveInStop'
},
'Clipboard': {
'OpenClipboard', 'CloseClipboard', 'GetClipboardData', 'SetClipboardData'
},
'Camera': {
'CapCreateCaptureWindowA', 'CapCreateCaptureWindowW'
},
'Memory': {
'HeapAlloc', 'HeapFree', 'HeapSize', 'HeapCreate', 'GetProcessHeap',
'LocalAlloc', 'LocalFree', 'GlobalAlloc', 'GlobalFree', 'GlobalUnlock',
'HeapDestroy', 'GlobalLock', 'SysFreeString', 'SysAllocStringLen'
},
'Resource': {
'LoadResource', 'SizeofResource', 'LockResource', 'FreeResource', 'FindResourceA'
},
'WindowControl': {
'ShowWindow', 'DestroyWindow', 'TranslateMessage', 'DispatchMessageA', 'GetWindow',
'PeekMessageA', 'GetWindowLongA', 'CallWindowProcA', 'SetWindowLongA', 'GetWindowTextA',
'ScreenToClient', 'GetActiveWindow', 'CreateWindowExA', 'DefWindowProcA', 'GetMessageA',
'RegisterClassA', 'UnregisterClassA', 'MessageBoxA'
},
'COM': {
'CoCreateInstance', 'CoUninitialize', 'CoInitialize'
}
}
TARGET_DLLS = {
'kernel32.dll', 'user32.dll', 'gdi32.dll', 'advapi32.dll', 'shell32.dll', 'ntdll.dll', 'hal.dll',
'ws2_32.dll', 'wsock32.dll', 'wininet.dll', 'winhttp.dll', 'urlmon.dll', 'crypt32.dll', 'bcrypt.dll',
'psapi.dll', 'dbghelp.dll', 'imagehlp.dll', 'shlwapi.dll', 'ole32.dll', 'oleaut32.dll', 'comctl32.dll',
'mscoree.dll', 'vbscript.dll', 'netapi32.dll', 'iphlpapi.dll', 'wtsapi32.dll', 'version.dll', 'winmm.dll'
}
BASE_SCHEMA = [
"FileEntropy", "SectionMeanEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit", "Machine", "Magic",
"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",
"TextSectionMaxEntropy", "TextSectionMeanEntropy", "TextSizeRatio",
"DataSectionMaxEntropy", "DataSectionMeanEntropy", "DataSizeRatio",
"RsrcSectionMaxEntropy", "RsrcSectionMeanEntropy", "RsrcSizeRatio",
"SectionCount", "ExecutableSections",
"WritableSections", "ReadableSections", "SectionException",
"Char_00000020_Count", "Char_00000020_MeanEntropy",
"Char_00000040_Count", "Char_00000040_MeanEntropy",
"Char_00000080_Count", "Char_00000080_MeanEntropy",
"Char_02000000_Count", "Char_02000000_MeanEntropy",
"Char_20000000_Count", "Char_20000000_MeanEntropy",
"Char_40000000_Count", "Char_40000000_MeanEntropy",
"Char_80000000_Count", "Char_80000000_MeanEntropy",
"IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount",
"ImportCount", "ImportFunctionCount",
"FileDescriptionLength", "FileVersionLength", "ProductNameLength",
"ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength",
"CommentsLength", "InternalNameLength", "LegalTrademarksLength",
"SpecialBuildLength", "PrivateBuildLength",
"TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease",
"IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks",
"HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException"
]
CAT_SCHEMA = [f"Cat_{k}" for k in sorted(API_CAT_MAPPING.keys())]
DLL_SCHEMA = [f"Dll_{d.replace('.', '_')}" for d in sorted(TARGET_DLLS)]
_ALL_APIS = {api for apis in API_CAT_MAPPING.values() for api in apis}
API_SCHEMA = [f"Api_{api}" for api in sorted(_ALL_APIS)]
SCHEMA = BASE_SCHEMA + CAT_SCHEMA + DLL_SCHEMA + API_SCHEMA
_API_TO_CATS = {}
for _cat, _apis in API_CAT_MAPPING.items():
for _api in _apis:
if _api not in _API_TO_CATS:
_API_TO_CATS[_api] = []
_API_TO_CATS[_api].append(_cat)
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"]
####################################################################################################
@staticmethod
def _safe_float(val):
try:
f = float(val)
if math.isinf(f) or math.isnan(f): return 0.0
return f
except Exception:
return 0.0
@staticmethod
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)))
@staticmethod
def _safe_div(n, d):
return float(n) / d if d > 0 else 0.0
####################################################################################################
@classmethod
def extract(cls, file_path):
fts = {k: 0.0 for k in cls.SCHEMA}
pe = None
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > MAX_FILE_SIZE:
return None
with open(file_path, "rb") as f:
file_bytes = f.read()
pe = pefile.PE(data=file_bytes, fast_load=True)
fts['TrustSigned'] = WinTrust.verify(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
fts['FileTimeException'] = 1 if fh.TimeDateStamp == 0 else 0
if hasattr(pe, 'OPTIONAL_HEADER'):
op = pe.OPTIONAL_HEADER
fts['Magic'] = getattr(op, 'Magic', 0)
fts['MajorLinkerVersion'] = getattr(op, 'MajorLinkerVersion', 0)
fts['MinorLinkerVersion'] = getattr(op, 'MinorLinkerVersion', 0)
fts['SizeOfCode'] = getattr(op, 'SizeOfCode', 0)
fts['SizeOfInitializedData'] = getattr(op, 'SizeOfInitializedData', 0)
fts['SizeOfUninitializedData'] = getattr(op, 'SizeOfUninitializedData', 0)
fts['AddressOfEntryPoint'] = getattr(op, 'AddressOfEntryPoint', 0)
fts['BaseOfCode'] = getattr(op, 'BaseOfCode', 0)
fts['ImageBase'] = getattr(op, 'ImageBase', 0)
fts['SectionAlignment'] = getattr(op, 'SectionAlignment', 0)
fts['FileAlignment'] = getattr(op, 'FileAlignment', 0)
fts['MajorOperatingSystemVersion'] = getattr(op, 'MajorOperatingSystemVersion', 0)
fts['MinorOperatingSystemVersion'] = getattr(op, 'MinorOperatingSystemVersion', 0)
fts['MajorImageVersion'] = getattr(op, 'MajorImageVersion', 0)
fts['MinorImageVersion'] = getattr(op, 'MinorImageVersion', 0)
fts['MajorSubsystemVersion'] = getattr(op, 'MajorSubsystemVersion', 0)
fts['MinorSubsystemVersion'] = getattr(op, 'MinorSubsystemVersion', 0)
fts['SizeOfImage'] = getattr(op, 'SizeOfImage', 0)
fts['SizeOfHeaders'] = getattr(op, 'SizeOfHeaders', 0)
fts['CheckSum'] = getattr(op, 'CheckSum', 0)
fts['Subsystem'] = getattr(op, 'Subsystem', 0)
fts['DllCharacteristics'] = getattr(op, 'DllCharacteristics', 0)
fts['SizeOfStackReserve'] = getattr(op, 'SizeOfStackReserve', 0)
fts['SizeOfStackCommit'] = getattr(op, 'SizeOfStackCommit', 0)
fts['SizeOfHeapReserve'] = getattr(op, 'SizeOfHeapReserve', 0)
fts['SizeOfHeapCommit'] = getattr(op, 'SizeOfHeapCommit', 0)
fts['LoaderFlags'] = getattr(op, 'LoaderFlags', 0)
fts['NumberOfRvaAndSizes'] = getattr(op, 'NumberOfRvaAndSizes', 0)
fts['Is64Bit'] = 1 if fh.Machine in (0x8664, 0xAA64, 0x0200) else 0
fts['IsExe'] = 1 if pe.is_exe() else 0
fts['IsDll'] = 1 if pe.is_dll() else 0
fts['IsDriver'] = 1 if fts['Subsystem'] == 1 else 0
if hasattr(op, 'DATA_DIRECTORY'):
reloc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_BASERELOC', 5)
if len(op.DATA_DIRECTORY) > reloc_idx and op.DATA_DIRECTORY[reloc_idx].Size > 0:
fts['HasRelocationDirectory'] = 1
try:
fts['FileEntropy'] = cls._calc_entropy(file_bytes)
except Exception:
pass
sec_entropies = []
text_entropies = []
data_entropies = []
rsrc_entropies = []
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
if hasattr(pe, 'sections'):
fts['SectionCount'] = len(pe.sections)
for section in pe.sections:
try:
name = section.Name.decode('ascii', 'ignore').strip('\x00')
except Exception:
name = ""
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
except Exception:
s_data = b""
s_entropy = 0.0
sec_entropies.append(s_entropy)
if any(p in name.lower() for p in cls.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
for flag in char_flags:
if section.Characteristics & flag:
fts[f'Char_{flag:08X}_Count'] += 1
fts[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
ratio = cls._safe_div(section.SizeOfRawData, fsize)
if name.startswith('.text'):
text_entropies.append(s_entropy)
fts['TextSizeRatio'] += ratio
elif name.startswith('.data'):
data_entropies.append(s_entropy)
fts['DataSizeRatio'] += ratio
elif name.startswith('.rsrc'):
rsrc_entropies.append(s_entropy)
fts['RsrcSizeRatio'] += ratio
if b"requireAdministrator" in s_data: fts['IsAdmin'] = 1
for sig in cls.INSTALLER_SIGS:
if sig in s_data:
fts['IsInstall'] = 1
break
fts['SectionMeanEntropy'] = cls._safe_div(sum(sec_entropies), len(sec_entropies))
fts['TextSectionMaxEntropy'] = max(text_entropies) if text_entropies else 0.0
fts['TextSectionMeanEntropy'] = cls._safe_div(sum(text_entropies), len(text_entropies))
fts['DataSectionMaxEntropy'] = max(data_entropies) if data_entropies else 0.0
fts['DataSectionMeanEntropy'] = cls._safe_div(sum(data_entropies), len(data_entropies))
fts['RsrcSectionMaxEntropy'] = max(rsrc_entropies) if rsrc_entropies else 0.0
fts['RsrcSectionMeanEntropy'] = cls._safe_div(sum(rsrc_entropies), len(rsrc_entropies))
for flag in char_flags:
fts[f'Char_{flag:08X}_MeanEntropy'] = cls._safe_div(fts[f'Char_{flag:08X}_MeanEntropy'], fts[f'Char_{flag:08X}_Count'])
try:
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'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS']
])
except Exception:
pass
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0:
fts['HasTlsCallbacks'] = 1
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
fts['IsDebug'] = 1 if flags & 0x1 else 0
fts['IsPreRelease'] = 1 if flags & 0x2 else 0
fts['IsPatched'] = 1 if flags & 0x4 else 0
fts['IsPrivateBuild'] = 1 if flags & 0x8 else 0
fts['IsSpecialBuild'] = 1 if flags & 0x20 else 0
if hasattr(pe, 'FileInfo'):
for fileinfo_list in pe.FileInfo:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
length = len(v)
if k == 'FileDescription': fts['FileDescriptionLength'] = length
elif k == 'FileVersion': fts['FileVersionLength'] = length
elif k == 'ProductName': fts['ProductNameLength'] = length
elif k == 'ProductVersion': fts['ProductVersionLength'] = length
elif k == 'CompanyName': fts['CompanyNameLength'] = length
elif k == 'LegalCopyright': fts['LegalCopyrightLength'] = length
elif k == 'Comments': fts['CommentsLength'] = length
elif k == 'InternalName': fts['InternalNameLength'] = length
elif k == 'LegalTrademarks': fts['LegalTrademarksLength'] = length
elif k == 'SpecialBuild': fts['SpecialBuildLength'] = length
elif k == 'PrivateBuild': fts['PrivateBuildLength'] = length
except Exception:
continue
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
fts['ImportCount'] = len(pe.DIRECTORY_ENTRY_IMPORT)
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try:
dll_name = entry.dll.decode('ascii', 'ignore').lower()
if dll_name in cls.TARGET_DLLS:
fts[f"Dll_{dll_name.replace('.', '_')}"] = 1.0
except Exception:
pass
imports = getattr(entry, 'imports', [])
for imp in imports:
fts['ImportFunctionCount'] += 1
if not getattr(imp, 'name', None): continue
try:
func_name = imp.name.decode('ascii', 'ignore')
if func_name in cls._ALL_APIS:
fts[f'Api_{func_name}'] += 1
fts['ApiCount'] += 1
for cat in cls._API_TO_CATS.get(func_name, []):
fts[f'Cat_{cat}'] += 1
except Exception:
continue
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols'):
fts['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols)
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
fts['IconCount'] += len(getattr(entry.directory, 'entries', []))
if hasattr(pe, 'OPTIONAL_HEADER') and hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY') and len(pe.OPTIONAL_HEADER.DATA_DIRECTORY) > 3:
exc_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[3]
if exc_dir.Size > 0:
fts['ExceptionCount'] = exc_dir.Size // 12 if fts['Machine'] in (0x8664, 0xAA64) else 0
if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'):
fts['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG)
for k in fts:
fts[k] = cls._safe_float(fts[k])
return fts
except Exception:
return None
finally:
if pe:
pe.close()
####################################################################################################
class ModelPredictor:
def __init__(self, model_path, feature_path):
self.sess = None
self.input_name = None
self.feature_order = []
self.model_features = self._load_features(feature_path)
self._load_model(model_path)
self._build_input_map()
def _load_features(self, path):
if os.path.exists(path):
try:
with open(path, 'r') as f:
return json.load(f)
except Exception:
pass
return [re.sub(r'[^A-Za-z0-9_]+', '', f) for f in FeatureExtractor.SCHEMA]
def _load_model(self, model_path):
if not os.path.exists(model_path):
raise FileNotFoundError(f"Model missing: {model_path}")
self.sess = ort.InferenceSession(model_path, providers=['CPUExecutionProvider'])
model_inputs = self.sess.get_inputs()
self.input_name = model_inputs[0].name
expected_dim = model_inputs[0].shape[1]
current_dim = len(self.model_features)
if current_dim != expected_dim:
raise ValueError(f"Dimension Mismatch: Expected {expected_dim}, got {current_dim}.")
def _build_input_map(self):
local_map = {re.sub(r'[^A-Za-z0-9_]+', '', raw_name): raw_name for raw_name in FeatureExtractor.SCHEMA}
self.feature_order = [local_map.get(req_feat) for req_feat in self.model_features]
def predict(self, raw_data):
vec_data = [raw_data.get(key, 0.0) if key else 0.0 for key in self.feature_order]
vec = np.array(vec_data, dtype=np.float32).reshape(1, -1)
outputs = self.sess.run(None, {self.input_name: vec})
if len(outputs) > 1:
result = outputs[1]
if isinstance(result, list) and len(result) > 0:
prob_dict = result[0]
if hasattr(prob_dict, 'get'):
return float(prob_dict.get(1, prob_dict.get('1', 0.0)))
elif isinstance(result, np.ndarray):
if result.ndim == 2 and result.shape[1] > 1:
return float(result[0][1])
return 0.0
####################################################################################################
def get_threat_name(prob_float):
prob_pct = min(int(prob_float * 100), 99)
if prob_pct >= 80:
return f"General:WinPE/Malware.{prob_pct}!ml"
elif prob_pct >= 50:
return f"General:WinPE/Suspicious.{prob_pct}!ml"
else:
return "Undetected"
def scan_target(target, predictor):
if not os.path.exists(target):
print(json.dumps({"result": "error", "prob": 0.0, "file": target, "details": "Path not found"}))
sys.stdout.flush()
return
try:
data = FeatureExtractor.extract(target)
if not data:
print(json.dumps({"result": "error", "prob": 0.0, "file": target, "details": "Extraction failed"}))
sys.stdout.flush()
return
prob = predictor.predict(data)
label = get_threat_name(prob)
print(json.dumps({"result": label, "prob": round(prob, 4), "file": target}))
sys.stdout.flush()
except Exception as e:
print(json.dumps({"result": "error", "prob": 0.0, "file": target, "details": str(e)}))
sys.stdout.flush()
####################################################################################################
if __name__ == "__main__":
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
model_path = os.path.join(base_dir, MODEL_FILE)
feature_path = os.path.join(base_dir, FEATURE_FILE)
try:
predictor = ModelPredictor(model_path, feature_path)
except Exception as e:
print(json.dumps({"result": "fatal", "details": str(e)}))
sys.stdout.flush()
sys.exit(1)
if len(sys.argv) > 1:
for path in sys.argv[1:]:
scan_target(path.strip(), predictor)
else:
for line in sys.stdin:
target = line.strip()
if not target or target.lower() in ['q', 'exit']:
break
scan_target(target, predictor)
-4
View File
@@ -1,4 +0,0 @@
onnxruntime
numpy
pefile
signify[full]
-55
View File
@@ -1,55 +0,0 @@
from PIL import Image
import numpy, os
####################################################################################################
def preprocess_image(file_data, size, channels=1):
width, height = size
wah = int(numpy.ceil(numpy.sqrt(len(file_data) / channels)))
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
img[:len(file_data)] = arr
image = Image.fromarray(img.reshape((wah, wah)), 'L')
return image.resize((width, height), Image.Resampling.NEAREST)
####################################################################################################
def get_type(file_path):
suffix = {".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui", ".cpl"}
try:
file_content = None
if not os.path.splitext(file_path)[-1].lower() in suffix:
return file_content
with open(file_path, 'rb') as f:
file_content = f.read()
except:
pass
return file_content
####################################################################################################
def file_to_images(input_dir, output_dir):
for root, dirs, files in os.walk(input_dir):
for file in files:
try:
file_path = os.path.join(root, file)
file_content = get_type(file_path)
if file_content:
print(file_path)
relative_path = os.path.relpath(root, input_dir)
output_path_dir = os.path.join(output_dir, relative_path)
if not os.path.exists(output_path_dir):
os.makedirs(output_path_dir)
image = preprocess_image(file_content, (224, 224))
safe_name = os.path.basename(file_path).replace('/', '_').replace('\\', '_')
image.save(os.path.join(output_path_dir, f"{safe_name}.png"))
except Exception as e:
print(e)
####################################################################################################
input_directory = input('Enter the folder path: ')
output_directory = "./Image_Output"
file_to_images(input_directory, output_directory)
input('Conversion Complete')
-34
View File
@@ -1,34 +0,0 @@
import os, hashlib
####################################################################################################
def calc_sha256(path):
with open(path, "rb") as f:
return hashlib.sha256(f.read()).digest()
def dedup_realtime(root):
seen = set()
deleted = 0
for dirpath, _, files in os.walk(root):
for name in files:
p = os.path.join(dirpath, name)
try:
h = calc_sha256(p)
except Exception:
continue
if h in seen:
try:
os.remove(p)
print(p)
deleted += 1
except Exception:
pass
else:
seen.add(h)
print(f"deleted {deleted}")
####################################################################################################
if __name__ == "__main__":
dedup_realtime(input("Input file path: "))
input('Deduplicate Complete')
-165
View File
@@ -1,165 +0,0 @@
import tensorflow as tf
import os, tf2onnx, numpy, random
from PIL import Image
from tensorflow.keras import *
from collections import Counter
####################################################################################################
Image.MAX_IMAGE_PIXELS = None
tf.keras.mixed_precision.set_global_policy('mixed_float16')
####################################################################################################
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)
combined = list(zip(file_paths, labels))
random.shuffle(combined)
file_paths[:], labels[:] = zip(*combined)
num_val = int(val_split * len(file_paths))
train_files = file_paths[num_val:]
train_labels = labels[num_val:]
val_files = file_paths[:num_val]
val_labels = labels[:num_val]
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_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_parallel_calls=tf.data.AUTOTUNE)
val_ds = val_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)
train_ds.class_indices = class_indices
return train_ds, val_ds
####################################################################################################
def 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.cast(label, tf.float32)
return img, label
def get_file_list(directory):
file_paths, labels, class_indices = [], [], {}
for i, cls in enumerate(sorted([d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d))])):
class_indices[cls] = i
cls_dir = os.path.join(directory, cls)
for root, _, files in os.walk(cls_dir):
for file in files:
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
file_paths.append(os.path.join(root, file))
labels.append(i)
return file_paths, labels, class_indices
####################################################################################################
def multi_conv(x, strides):
convs = []
for k in [(1, 7), (1, 1), (3, 3), (5, 5)]:
c = layers.DepthwiseConv2D(kernel_size=k, strides=strides, padding='same', activation='gelu')(x)
convs.append(c)
x = layers.Concatenate()(convs)
x = layers.BatchNormalization()(x)
return layers.Activation('gelu')(x)
def gated_conv(x, strides):
c = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='gelu')(x)
g = layers.DepthwiseConv2D(3, strides=strides, padding='same', activation='sigmoid')(x)
return layers.Multiply()([c, g])
def coordinate_attention(x, reduction):
input_shape = tf.shape(x)
h, w = input_shape[1], input_shape[2]
filters = x.shape[-1]
mip = max(8, filters // reduction)
x_h = layers.Lambda(lambda t: tf.reduce_mean(t, axis=2, keepdims=True))(x)
x_w = layers.Lambda(lambda t: tf.reduce_mean(t, axis=1, keepdims=True))(x)
x_w_t = layers.Permute((2, 1, 3))(x_w)
x_cat = layers.Concatenate(axis=1)([x_h, x_w_t])
x_cat = layers.Conv2D(mip, 1, padding='valid', use_bias=False)(x_cat)
x_cat = layers.BatchNormalization()(x_cat)
x_cat = layers.Activation('gelu')(x_cat)
x_cat_h, x_cat_w = layers.Lambda(lambda t: tf.split(t[0], [t[1], t[2]], axis=1))([x_cat, h, w])
x_h_sigmoid = layers.Conv2D(filters, 1, padding='valid', activation='sigmoid')(x_cat_h)
x_cat_w = layers.Permute((2, 1, 3))(x_cat_w)
x_w_sigmoid = layers.Conv2D(filters, 1, padding='valid', activation='sigmoid')(x_cat_w)
return layers.Multiply()([x, x_h_sigmoid, x_w_sigmoid])
def build_model(input_shape):
i = layers.Input(shape=input_shape)
x = multi_conv(i, strides=1)
x = gated_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = multi_conv(x, strides=2)
x = coordinate_attention(x, reduction=4)
x = layers.GlobalMaxPooling2D()(x)
x = layers.Dense(x.shape[-1] // 2, activation='gelu', dtype='float32')(x)
o = layers.Dense(1, activation='sigmoid', dtype='float32')(x)
return models.Model(i, o)
####################################################################################################
def calculate_lr_cosine_sq(epoch, total_epochs=30, lr_start=5e-4, lr_end=5e-7):
cos_val = 0.5 * (1 + numpy.cos(epoch / total_epochs * numpy.pi))
return lr_end + (lr_start - lr_end) * (cos_val ** 2)
def lr_scheduler(epoch):
return calculate_lr_cosine_sq(epoch)
def 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())
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,) + self.model.input.shape[1:], tf.float32),)
model_proto, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=17)
with open(f"PYAS_Model_Epoch_{epoch + 1}.onnx", "wb") as f:
f.write(model_proto.SerializeToString())
####################################################################################################
train_ds, val_ds = load_dataset(r'.\Image_Full_Pefile')
for imgs, _ in train_ds.take(1):
train_ds.image_shape = imgs.shape[1:]
break
try:
model = models.load_model('PYAS_Model_Epoch_0.h5', compile=False)
print("Load model from disk")
except:
print("Creating a new model")
model = build_model(train_ds.image_shape)
print(f"Total parameters: {model.count_params()}")
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, class_weight={0: 10.0, 1: 1.0})
input('Training Complete')
File diff suppressed because it is too large. Load diff
+731
View File
@@ -0,0 +1,731 @@
import os, re, sys, math, json, zlib, pefile, datetime
import ctypes, ctypes.wintypes
import numpy as np
import onnxruntime as ort
####################################################################################################
MODEL_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
####################################################################################################
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_UNION(ctypes.Union):
_fields_ = [
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
("pCatalog", ctypes.wintypes.LPVOID),
("pBlob", ctypes.wintypes.LPVOID),
("pSgnr", ctypes.wintypes.LPVOID),
("pCert", 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),
("u", WINTRUST_DATA_UNION),
("dwStateAction", ctypes.wintypes.DWORD),
("hWVTStateData", ctypes.wintypes.HANDLE),
("pwszURLReference", ctypes.wintypes.LPCWSTR),
("dwProvFlags", ctypes.wintypes.DWORD),
("dwUIContext", ctypes.wintypes.DWORD),
("pSignatureSettings", ctypes.wintypes.LPVOID)
]
class GUID(ctypes.Structure):
_fields_ = [
("Data1", ctypes.wintypes.DWORD),
("Data2", ctypes.wintypes.WORD),
("Data3", ctypes.wintypes.WORD),
("Data4", ctypes.c_ubyte * 8)
]
V2_GUID = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
####################################################################################################
class WinTrust:
@staticmethod
def verify(file_path):
if os.name != 'nt':
return 0
try:
wintrust = ctypes.windll.wintrust
wintrust.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.wintypes.LPVOID]
wintrust.WinVerifyTrust.restype = ctypes.wintypes.LONG
fi = WINTRUST_FILE_INFO()
fi.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
fi.pcwszFilePath = os.path.abspath(file_path)
fi.hFile = None
fi.pgKnownSubject = None
wt_union = WINTRUST_DATA_UNION()
wt_union.pFile = ctypes.pointer(fi)
td = WINTRUST_DATA()
td.cbStruct = ctypes.sizeof(WINTRUST_DATA)
td.pPolicyCallbackData = None
td.pSIPClientData = None
td.dwUIChoice = 2
td.fdwRevocationChecks = 0
td.dwUnionChoice = 1
td.u = wt_union
td.dwStateAction = 0
td.hWVTStateData = None
td.pwszURLReference = None
td.dwProvFlags = 0
td.dwUIContext = 0
td.pSignatureSettings = None
return 1 if wintrust.WinVerifyTrust(None, ctypes.byref(V2_GUID), ctypes.byref(td)) == 0 else 0
except Exception:
return 0
####################################################################################################
class FeatureExtractor:
_STRING_PATTERN = re.compile(b'[\x20-\x7E]{5,}')
_BYTE_HIST_KEYS = [f'ByteHist_{i:02X}' for i in range(256)]
_BYTE_ENT_KEYS = [f'ByteEnt_{i:02X}' for i in range(256)]
_STRING_KEYS = [f'StringHist_{i:02d}' for i in range(95)]
_SECTION_HASH_KEYS = [f'SectionHash_{i:02d}' for i in range(50)]
_CHAR_FLAGS = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
_CHAR_COUNT_KEYS = {flag: f'Char_{flag:08X}_Count' for flag in _CHAR_FLAGS}
_CHAR_ENT_KEYS = {flag: f'Char_{flag:08X}_MeanEntropy' for flag in _CHAR_FLAGS}
_C_LOG_C_TABLE = np.zeros(2049, dtype=np.float64)
_C_LOG_C_TABLE[1:] = np.arange(1, 2049) * np.log2(np.arange(1, 2049))
@staticmethod
def _safe_float(val):
try:
f = float(val)
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
except Exception:
return 0.0
@staticmethod
def _calc_entropy(data):
if not data:
return 0.0
arr = np.frombuffer(data, dtype=np.uint8)
sz = len(arr)
counts = np.bincount(arr, minlength=256)
counts = counts[counts > 0]
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
@classmethod
def _extract_strings(cls, file_bytes):
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
res.update(dict.fromkeys(cls._STRING_KEYS, 0.0))
matches = cls._STRING_PATTERN.findall(file_bytes)
if not matches:
return res
combined_data = b''.join(matches)
count = len(matches)
total_len = len(combined_data)
res["StringCount"] = float(count)
res["StringMeanLength"] = float(total_len) / count
res["StringEntropy"] = cls._calc_entropy(combined_data)
arr = np.frombuffer(combined_data, dtype=np.uint8)
counts = np.bincount(arr, minlength=127)
char_counts = counts[0x20:0x7F]
res.update(dict(zip(cls._STRING_KEYS, char_counts.astype(np.float64) / total_len)))
return res
@classmethod
def _extract_histograms(cls, file_bytes):
arr = np.frombuffer(file_bytes, dtype=np.uint8)
sz = len(arr)
res = {}
if sz == 0:
res.update(dict.fromkeys(cls._BYTE_HIST_KEYS, 0.0))
res.update(dict.fromkeys(cls._BYTE_ENT_KEYS, 0.0))
return res
byte_counts = np.bincount(arr, minlength=256)
byte_hist = byte_counts.astype(np.float64) / sz
res.update(dict(zip(cls._BYTE_HIST_KEYS, byte_hist)))
ent_hist = np.zeros(256, dtype=np.float64)
window = 2048
step = 1024
if sz < window:
w_counts = byte_counts
p = w_counts[w_counts > 0] / float(sz)
entropy = float(-np.sum(p * np.log2(p)))
ent_bin = min(int(entropy * 2.0), 15)
byte_bins = w_counts.reshape(16, 16).sum(axis=1)
idx_start = ent_bin * 16
ent_hist[idx_start : idx_start+16] += byte_bins
else:
num_windows = (sz - window) // step + 1
batch_windows = 10000
for w_start in range(0, num_windows, batch_windows):
w_end = min(w_start + batch_windows, num_windows)
b_num = w_end - w_start
start_byte = w_start * step
end_byte = start_byte + b_num * step + step
b_arr = arr[start_byte:end_byte]
arr_2d = b_arr.reshape(b_num + 1, step)
offsets = (np.arange(b_num + 1, dtype=np.int32)[:, None] * 256)
flat_idx = (arr_2d.astype(np.int32) + offsets).ravel()
chunk_counts = np.bincount(flat_idx, minlength=(b_num + 1) * 256).reshape(b_num + 1, 256)
window_counts = chunk_counts[:-1] + chunk_counts[1:]
sum_c_log_c = np.sum(cls._C_LOG_C_TABLE[window_counts], axis=1)
entropies = 11.0 - sum_c_log_c / 2048.0
ent_bins = (entropies * 2.0).astype(np.int32)
np.clip(ent_bins, 0, 15, out=ent_bins)
byte_bins = window_counts.reshape(b_num, 16, 16).sum(axis=2)
flat_ent_bins = (ent_bins[:, None] * 16 + np.arange(16)).ravel()
np.add.at(ent_hist, flat_ent_bins, byte_bins.ravel())
ent_sum = np.sum(ent_hist)
if ent_sum > 0:
ent_hist /= ent_sum
res.update(dict(zip(cls._BYTE_ENT_KEYS, ent_hist)))
return res
@classmethod
def _extract_overlay_features(cls, pe, fsize):
overlay_offset = pe.get_overlay_data_start_offset()
if not overlay_offset or overlay_offset >= fsize:
return {"HasOverlay": 0.0, "OverlaySize": 0.0, "OverlayRatio": 0.0, "OverlayEntropy": 0.0}
overlay_data = pe.get_overlay()
if not overlay_data:
return {"HasOverlay": 0.0, "OverlaySize": 0.0, "OverlayRatio": 0.0, "OverlayEntropy": 0.0}
sz = len(overlay_data)
return {
"HasOverlay": 1.0,
"OverlaySize": float(sz),
"OverlayRatio": float(sz) / float(fsize),
"OverlayEntropy": cls._calc_entropy(overlay_data)
}
@classmethod
def _extract_rich_header(cls, pe):
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
return {
"HasRichHeader": 1.0,
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
}
@classmethod
def _extract_ep_anomalies(cls, pe):
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe, 'sections') or not pe.sections:
return {"EntryPointSectionIndex": -1.0, "EntryPointInExecutable": 0.0, "EntryPointInLastSection": 0.0}
ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
ep_section_idx = -1
ep_in_exec = 0.0
for idx, sec in enumerate(pe.sections):
if sec.VirtualAddress <= ep < (sec.VirtualAddress + sec.Misc_VirtualSize):
ep_section_idx = idx
if sec.Characteristics & 0x20000000:
ep_in_exec = 1.0
break
return {
"EntryPointSectionIndex": float(ep_section_idx),
"EntryPointInExecutable": ep_in_exec,
"EntryPointInLastSection": 1.0 if ep_section_idx == len(pe.sections) - 1 else 0.0
}
@classmethod
def _extract_advanced_resources(cls, pe):
res_data = {
"ResourceMaxEntropy": 0.0,
"ResourceMinEntropy": 0.0,
"ResourceMeanEntropy": 0.0,
"ResourceLangCount": 0.0,
"ResourceRCDataCount": 0.0
}
if not hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
return res_data
entropies = []
langs = set()
rcdata_count = 0
for entry_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if hasattr(entry_type, 'id') and entry_type.id == 10:
rcdata_count += len(getattr(entry_type.directory, 'entries', []))
if not hasattr(entry_type, 'directory'):
continue
for entry_id in entry_type.directory.entries:
if not hasattr(entry_id, 'directory'):
continue
for entry_lang in entry_id.directory.entries:
if hasattr(entry_lang, 'id'):
langs.add(entry_lang.id)
if hasattr(entry_lang, 'data'):
try:
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
entropies.append(cls._calc_entropy(data))
except Exception:
continue
res_data["ResourceLangCount"] = float(len(langs))
res_data["ResourceRCDataCount"] = float(rcdata_count)
if entropies:
res_data["ResourceMaxEntropy"] = max(entropies)
res_data["ResourceMinEntropy"] = min(entropies)
res_data["ResourceMeanEntropy"] = sum(entropies) / len(entropies)
return res_data
@classmethod
def _extract_load_config(cls, pe):
cfg = {"HasLoadConfig": 0.0, "HasCFG": 0.0, "HasSEHTable": 0.0}
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
cfg["HasLoadConfig"] = 1.0
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
cfg["HasCFG"] = 1.0
if getattr(struct, 'SEHandlerTable', 0) != 0:
cfg["HasSEHTable"] = 1.0
return cfg
@classmethod
def _extract_security_directory(cls, pe, file_bytes, fsize):
res = {"HasSignature": 0.0, "SignatureCount": 0.0}
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY'):
return res
sec_idx = 4
directories = pe.OPTIONAL_HEADER.DATA_DIRECTORY
if len(directories) <= sec_idx:
return res
sec_dir = directories[sec_idx]
if sec_dir.Size == 0 or sec_dir.VirtualAddress == 0:
return res
res["HasSignature"] = 1.0
offset = sec_dir.VirtualAddress
size = sec_dir.Size
if offset + size > fsize or offset < 0 or size < 0:
return res
count = 0
curr = offset
end = offset + size
while curr + 8 <= end:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
res["SignatureCount"] = float(count)
return res
@classmethod
def extract(cls, file_path):
pe = None
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > MAX_FILE_SIZE:
return None
with open(file_path, "rb") as f:
file_bytes = f.read()
base, dlls, apis = {}, set(), set()
pe = pefile.PE(data=file_bytes, fast_load=True)
try:
pe.parse_rich_header()
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'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception:
pass
base['TrustSigned'] = float(WinTrust.verify(file_path))
base['FileEntropy'] = cls._calc_entropy(file_bytes)
base['FileSize'] = float(fsize)
fh = pe.FILE_HEADER
base['Machine'] = getattr(fh, 'Machine', 0)
base['NumberOfSections'] = getattr(fh, 'NumberOfSections', 0)
base['TimeDateStamp'] = getattr(fh, 'TimeDateStamp', 0)
base['PointerToSymbolTable'] = getattr(fh, 'PointerToSymbolTable', 0)
base['NumberOfSymbols'] = getattr(fh, 'NumberOfSymbols', 0)
base['SizeOfOptionalHeader'] = getattr(fh, 'SizeOfOptionalHeader', 0)
base['Characteristics'] = getattr(fh, 'Characteristics', 0)
curr_ts = datetime.datetime.utcnow().timestamp()
base['HasInvalidTimestamp'] = 1.0 if (base['TimeDateStamp'] < 631152000 or base['TimeDateStamp'] > curr_ts + 2592000) else 0.0
base['FileTimeException'] = 1.0 if base['TimeDateStamp'] == 0 else 0.0
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
op = pe.OPTIONAL_HEADER
fields = ['Magic', 'MajorLinkerVersion', 'MinorLinkerVersion', 'SizeOfCode', 'SizeOfInitializedData', 'SizeOfUninitializedData', 'AddressOfEntryPoint', 'BaseOfCode', 'ImageBase', 'SectionAlignment', 'FileAlignment', 'MajorOperatingSystemVersion', 'MinorOperatingSystemVersion', 'MajorImageVersion', 'MinorImageVersion', 'MajorSubsystemVersion', 'MinorSubsystemVersion', 'SizeOfImage', 'SizeOfHeaders', 'CheckSum', 'Subsystem', 'DllCharacteristics', 'SizeOfStackReserve', 'SizeOfStackCommit', 'SizeOfHeapReserve', 'SizeOfHeapCommit', 'LoaderFlags', 'NumberOfRvaAndSizes']
for f in fields: base[f] = getattr(op, f, 0)
base['IsDriver'] = 1.0 if base.get('Subsystem') == 1 else 0.0
if hasattr(op, 'DATA_DIRECTORY'):
for i, directory in enumerate(op.DATA_DIRECTORY):
base[f'DataDirectory_{i}_Size'] = directory.Size
base[f'DataDirectory_{i}_VA'] = directory.VirtualAddress
exc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_EXCEPTION', 3)
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
res_flags_count = dict.fromkeys(cls._CHAR_COUNT_KEYS.values(), 0.0)
res_flags_ent = dict.fromkeys(cls._CHAR_ENT_KEYS.values(), 0.0)
res_sec_hash = dict.fromkeys(cls._SECTION_HASH_KEYS, 0.0)
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
entropies, raw_sizes, v_sizes = [], [], []
exec_sec, write_sec, read_sec, sec_exc = 0, 0, 0, 0
for section in pe.sections:
sec_name = section.Name.rstrip(b'\x00')
hash_val = zlib.crc32(sec_name) % 50
res_sec_hash[cls._SECTION_HASH_KEYS[hash_val]] += 1.0
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
except Exception:
s_entropy = 0.0
entropies.append(s_entropy)
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000:
exec_sec += 1
if section.Characteristics & 0x80000000:
write_sec += 1
if section.Characteristics & 0x40000000:
read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize:
sec_exc = 1
for flag in cls._CHAR_FLAGS:
if section.Characteristics & flag:
res_flags_count[cls._CHAR_COUNT_KEYS[flag]] += 1.0
res_flags_ent[cls._CHAR_ENT_KEYS[flag]] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
base['SectionMeanEntropy'] = sum(entropies) / len(entropies) if entropies else 0.0
base['SectionMaxRawSize'] = max(raw_sizes) if raw_sizes else 0.0
base['SectionMinRawSize'] = min(raw_sizes) if raw_sizes else 0.0
base['SectionMeanRawSize'] = sum(raw_sizes) / len(raw_sizes) if raw_sizes else 0.0
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = float(exec_sec)
base['WritableSections'] = float(write_sec)
base['ReadableSections'] = float(read_sec)
base['SectionException'] = float(sec_exc)
for flag in cls._CHAR_FLAGS:
cnt_key = cls._CHAR_COUNT_KEYS[flag]
if res_flags_count[cnt_key] > 0:
res_flags_ent[cls._CHAR_ENT_KEYS[flag]] /= res_flags_count[cnt_key]
base.update(res_flags_count)
base.update(res_flags_ent)
base.update(res_sec_hash)
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
base[f'{key}Length'] = 0.0
if hasattr(pe, 'FileInfo'):
for fileinfo_list in pe.FileInfo:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
base['HasTlsCallbacks'] = 1.0 if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0 else 0.0
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
base['IsDebug'] = 1.0 if flags & 0x1 else 0.0
base['IsPreRelease'] = 1.0 if flags & 0x2 else 0.0
base['IsPatched'] = 1.0 if flags & 0x4 else 0.0
base['IsPrivateBuild'] = 1.0 if flags & 0x8 else 0.0
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = float(len(pe.DIRECTORY_ENTRY_IMPORT))
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try:
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception:
pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ImportFunctionCount'] = float(func_count)
base['ExportCount'] = float(len(pe.DIRECTORY_ENTRY_EXPORT.symbols)) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = float(icon_count)
else:
base['IconCount'] = 0.0
base['DebugCount'] = float(len(pe.DIRECTORY_ENTRY_DEBUG)) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base.update(cls._extract_strings(file_bytes))
base.update(cls._extract_histograms(file_bytes))
base.update(cls._extract_overlay_features(pe, fsize))
base.update(cls._extract_rich_header(pe))
base.update(cls._extract_ep_anomalies(pe))
base.update(cls._extract_advanced_resources(pe))
base.update(cls._extract_load_config(pe))
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
for k in base:
base[k] = cls._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception:
return None
finally:
if pe:
pe.close()
####################################################################################################
class ModelPredictor:
def __init__(self, model_path, feature_path):
self.model_features = self._load_features(feature_path)
self.sess = ort.InferenceSession(model_path, providers=['CPUExecutionProvider'])
self.input_name = self.sess.get_inputs()[0].name
expected_dim = self.sess.get_inputs()[0].shape[1]
current_dim = len(self.model_features)
if current_dim != expected_dim:
raise ValueError(f"Dimension mismatch! Model expects {expected_dim} features, but feature file lists {current_dim}.")
self.feat_map = {feat: i for i, feat in enumerate(self.model_features)}
def _load_features(self, path):
if not os.path.exists(path):
raise FileNotFoundError(f"Feature file not found: {path}")
with open(path, 'r') as f:
return json.load(f)
def predict(self, raw_data):
vec = np.zeros((1, len(self.model_features)), dtype=np.float32)
base = raw_data.get('Base', {})
dlls = raw_data.get('DLLs', [])
apis = raw_data.get('APIs', [])
for k, v in base.items():
if k in self.feat_map:
vec[0, self.feat_map[k]] = v
for d in dlls:
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
feat_name = f"DllHash_{h:03d}"
if feat_name in self.feat_map:
vec[0, self.feat_map[feat_name]] += 1.0
for a in apis:
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
feat_name = f"ApiHash_{h:04d}"
if feat_name in self.feat_map:
vec[0, self.feat_map[feat_name]] += 1.0
outputs = self.sess.run(None, {self.input_name: vec})
if len(outputs) > 1:
result = outputs[1]
if isinstance(result, list) and len(result) > 0:
prob_dict = result[0]
if hasattr(prob_dict, 'get'):
return float(prob_dict.get(1, prob_dict.get('1', 0.0)))
elif isinstance(result, np.ndarray):
if result.ndim == 2 and result.shape[1] > 1:
return float(result[0][1])
return 0.0
####################################################################################################
def scan_target(target, predictor):
files = []
if os.path.isfile(target):
files.append(target)
elif os.path.isdir(target):
for r, _, fs in os.walk(target):
for f in fs:
files.append(os.path.join(r, f))
if not files:
print("[-] No valid PE files found.")
return
print(f"\n[*] Scanning {len(files)} files...\n")
print(f"{'RESULT':<10} | {'PROB':<8} | {'FILE'}")
print("-" * 80)
safe_count = 0
mal_count = 0
for fpath in files:
try:
data = FeatureExtractor.extract(fpath)
if not data:
print(f"{'ERROR':<10} | {'----':<8} | {fpath}")
continue
prob = predictor.predict(data)
is_malware = prob > 0.5
label = "MALWARE" if is_malware else "SAFE"
if is_malware:
mal_count += 1
else:
safe_count += 1
print(f"{label:<10} | {prob:.4f} | {fpath}")
except Exception as e:
print(f"{'FAIL':<10} | {'----':<8} | {fpath} ({str(e)})")
print("-" * 80)
print(f"[*] Summary: Safe={safe_count}, Malware={mal_count}, Total={len(files)}")
####################################################################################################
if __name__ == "__main__":
print("\n-------------------------- PE Malware Predictor v4.0 --------------------------\n")
print(f"[*] © 2020-2026 87owo (PYAS Security)")
print(f"[*] Loading model features and weights...")
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
model_path = os.path.join(base_dir, MODEL_FILE)
feature_path = os.path.join(base_dir, FEATURE_FILE)
try:
predictor = ModelPredictor(model_path, feature_path)
except Exception as e:
print(f"[-] Critical Error: {e}")
sys.exit(1)
if len(sys.argv) > 1:
for path in sys.argv[1:]:
target = path.strip('"').strip("'")
if os.path.exists(target):
print()
print("-" * 80)
scan_target(target, predictor)
else:
print(f"[-] Path does not exist: {target}")
else:
while True:
try:
print()
print("-" * 80)
path = input("\n[?] Enter File or Folder Path (or 'q' to exit): ").strip().strip('"').strip("'")
if path.lower() in ['q', 'exit']:
break
if not os.path.exists(path):
print("[-] Path does not exist.")
continue
scan_target(path, predictor)
except KeyboardInterrupt:
break
except Exception as e:
print(f"[-] Error: {e}")
+197 -103
View File
@@ -1,127 +1,205 @@
import os, sys, time, sqlite3, re, json
import pandas as pd
import os, sys, time, json, zlib, array, onnxmltools, multiprocessing
import numpy as np
import pandas as pd
import lightgbm as lgb
import onnxmltools
import scipy.sparse as sp
from collections import defaultdict
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
from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
####################################################################################################
DB_PATH = "pe_features.db"
JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "model.onnx"
ONNX_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
DLL_HASH_DIM = 256
API_HASH_DIM = 1024
EXCLUDE_FEATURES = {
'label', 'filehash',
}
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 512,
'learning_rate': 0.05,
'num_leaves': 256,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_fraction': 1.0,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 10,
'lambda_l1': 0.1,
'lambda_l2': 0.1,
'min_data_in_leaf': 50,
}
####################################################################################################
def clean_col_name(name):
return re.sub(r'[^A-Za-z0-9_]+', '', name)
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
num_lines = 0
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 = {'label', 'id', 'filename', 'filelength', 'rowid', 'probability', 'predictedlabel', 'score', 'filehash', 'timedatestamp'}
return [c for c in columns if c.lower().strip() not in exclude]
except Exception as e:
print(f"[-] Schema extraction error: {e}")
return None
finally:
conn.close()
with open(jsonl_path, 'rb') as f:
for line in f:
num_lines += 1
####################################################################################################
if not base_keys:
try:
data = JSON_LOADS(line)
base_data = data.get('Base')
if base_data:
base_keys.update(base_data.keys())
def load_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"
except Exception:
pass
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
api_schema = [f"ApiHash_{i:04d}" for i in range(API_HASH_DIM)]
try:
chunks = []
for chunk in pd.read_sql_query(sql, conn, chunksize=20000):
label_col = next((c for c in chunk.columns if c.lower().strip() == 'label'), None)
if not label_col:
print("[-] Critical Error: Label column not found.")
return None, None, None
cols_to_cast = [c for c in chunk.columns if c != label_col]
chunk[cols_to_cast] = chunk[cols_to_cast].astype(np.float32)
chunk[label_col] = chunk[label_col].astype(np.int8)
chunks.append(chunk)
if not chunks:
return None, None, None
df = pd.concat(chunks, ignore_index=True)
label_col = next((c for c in df.columns if c.lower().strip() == 'label'), 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()
rename_map = {old: clean_col_name(old) for old in X.columns}
X = X.rename(columns=rename_map)
if X.columns.duplicated().any():
print("[-] Warning: Duplicate column names detected after cleaning. Deduplicating...")
X = X.loc[:, ~X.columns.duplicated()]
final_features = X.columns.tolist()
return X, y, final_features
finally:
conn.close()
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def train_process(X, y):
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically via Multiprocessing...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
file_size = os.path.getsize(jsonl_path)
workers = min(os.cpu_count() or 4, 16)
chunk_size = file_size // workers
tasks = []
for i in range(workers):
start = i * chunk_size
end = file_size if i == workers - 1 else (i + 1) * chunk_size
tasks.append((jsonl_path, start, end, base_schema, num_features, offset_dll, offset_api))
with multiprocessing.Pool(workers) as pool:
results = pool.map(_chunk_worker, tasks)
Xs = [r[0] for r in results if r[0].shape[0] > 0]
ys = [r[1] for r in results if r[1].shape[0] > 0]
X_final = sp.vstack(Xs, format='csr') if Xs else sp.csr_matrix((0, num_features), dtype=np.float32)
y_final = np.concatenate(ys) if ys else np.array([], dtype=np.int8)
return X_final, pd.Series(y_final), final_features
def _chunk_worker(args):
filepath, start_byte, end_byte, base_schema, num_features, offset_dll, offset_api = args
base_idx = {feat: i for i, feat in enumerate(base_schema)}
base_idx_get = base_idx.get
rows = array.array('i')
cols = array.array('i')
vals = array.array('f')
y_list = array.array('b')
with open(filepath, 'rb') as f:
if start_byte > 0:
f.seek(start_byte - 1)
if f.read(1) != b'\n':
f.seek(start_byte)
f.readline()
else:
f.seek(start_byte)
else:
f.seek(0)
row_idx = 0
while f.tell() < end_byte:
line = f.readline()
if not line:
break
try:
data = JSON_LOADS(line)
y_list.append(data.get('Label', 0))
base_data = data.get('Base')
if base_data:
for k, v in base_data.items():
idx = base_idx_get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
dll_data = data.get('DLLs')
if dll_data:
for d in dll_data:
idx = zlib.crc32(d.encode('utf-8', 'ignore')) % DLL_HASH_DIM
rows.append(row_idx)
cols.append(offset_dll + idx)
vals.append(1.0)
api_data = data.get('APIs')
if api_data:
for a in api_data:
idx = zlib.crc32(a.encode('utf-8', 'ignore')) % API_HASH_DIM
rows.append(row_idx)
cols.append(offset_api + idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y_arr = np.frombuffer(y_list, dtype=np.int8)
np_rows = np.frombuffer(rows, dtype=np.int32)
np_cols = np.frombuffer(cols, dtype=np.int32)
np_vals = np.frombuffer(vals, dtype=np.float32)
X_local = sp.csr_matrix((np_vals, (np_rows, np_cols)), shape=(row_idx, num_features), dtype=np.float32)
return X_local, y_arr
####################################################################################################
def _calculate_dynamic_lr(current_iter: int) -> float:
return max(0.01, 0.1 - (current_iter // 80) * 0.01)
def train_process(X, y, feature_names):
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
)
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_target = 0.1
weight_ratio_target = 0.005
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Balance Report: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight Config: Base Balanced={base_weight:.4f}, Final Adjusted={final_pos_weight:.4f}")
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight correction: Base={base_weight:.4f}, Final={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)
train_data = lgb.Dataset(X_train, label=y_train, feature_name=feature_names)
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data, feature_name=feature_names)
params = LGBM_PARAMS.copy()
params['scale_pos_weight'] = final_pos_weight
@@ -133,14 +211,15 @@ def train_process(X, y):
params,
train_data,
valid_sets=[valid_data],
num_boost_round=1000,
num_boost_round=500,
callbacks=[
lgb.log_evaluation(period=100)
lgb.log_evaluation(period=50),
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
]
)
elapsed = time.time() - start_time
print(f"\n[+] Training finished in {elapsed:.2f}s")
print(f"\n[+] Training complete, time elapsed {elapsed:.2f} seconds")
return model, X_test, y_test
@@ -156,12 +235,10 @@ def export_onnx_model(model, feature_names):
with open(FEATURE_FILE, 'w') as f:
json.dump(feature_names, f)
print(f"[+] ONNX export success. Features synced to {FEATURE_FILE}")
print(f"[+] ONNX export successful. Feature order saved 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)
@@ -170,12 +247,25 @@ def evaluate_and_save(model, X_test, y_test):
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))
tp = np.sum((y_test == 1) & (y_pred == 1))
fn = np.sum((y_test == 1) & (y_pred == 0))
tn = np.sum((y_test == 0) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
total_malware = tp + fn
total_safe = tn + fp
detection_rate = (tp / total_malware * 100) if total_malware > 0 else 0.0
fpr = (fp / total_safe * 100) if total_safe > 0 else 0.0
print(f"Accuracy : {acc:.6f}")
print(f"ROC AUC : {auc:.6f}\n")
print(f"True Positive (Class 1) : {detection_rate:.3f}% ({tp}/{total_malware})")
print(f"False Positive (Class 0) : {fpr:.3f}% ({fp}/{total_safe})")
print("---------------------------------------------------------\n")
print("[*] Top 20 Features (Importance by Gain):")
print("\n---------------------------------------------------------\n")
print("[*] Top 20 Features:")
feature_names = model.feature_name()
importance = model.feature_importance(importance_type='gain')
@@ -192,25 +282,29 @@ def evaluate_and_save(model, X_test, y_test):
####################################################################################################
if __name__ == "__main__":
print("\n---------------- PE Malware Trainer v3.1 ----------------\n")
multiprocessing.freeze_support()
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(DB_PATH):
print(f"[-] Error: Database {DB_PATH} not found.")
if not os.path.exists(JSONL_PATH):
print(f"[-] Error: Feature file {JSONL_PATH} not found.")
sys.exit(1)
raw_schema = get_raw_schema(DB_PATH)
if not raw_schema:
print("[-] Error: Could not read schema.")
base_schema, dll_schema, api_schema, num_lines = analyze_schema(JSONL_PATH)
if num_lines == 0:
print("[-] Error: Feature file is empty.")
sys.exit(1)
X, y, _ = load_data(DB_PATH, raw_schema)
if X is None:
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Hashing DLL features: {len(dll_schema)}")
print(f"[*] Hashing API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
try:
model, X_test, y_test = train_process(X, y)
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training interrupted.")
print("\n\n[-] Training aborted.")
except Exception as e:
print(f"\n[-] Critical Error: {e}")
print(f"\n[-] System exception: {e}")
-216
View File
@@ -1,216 +0,0 @@
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 = "model.txt"
ONNX_FILE = "model.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': 512,
'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': 10,
'lambda_l1': 0.1,
'lambda_l2': 0.1,
}
####################################################################################################
def clean_col_name(name):
return re.sub(r'[^A-Za-z0-9_]+', '', name)
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 = {'label', 'id', 'filename', 'filelength', 'rowid', 'probability', 'predictedlabel', 'score', 'filehash', 'timedatestamp'}
return [c for c in columns if c.lower().strip() not in exclude]
except Exception as e:
print(f"[-] Schema extraction error: {e}")
return None
finally:
conn.close()
####################################################################################################
def load_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:
chunks = []
for chunk in pd.read_sql_query(sql, conn, chunksize=20000):
label_col = next((c for c in chunk.columns if c.lower().strip() == 'label'), None)
if not label_col:
print("[-] Critical Error: Label column not found.")
return None, None, None
cols_to_cast = [c for c in chunk.columns if c != label_col]
chunk[cols_to_cast] = chunk[cols_to_cast].astype(np.float32)
chunk[label_col] = chunk[label_col].astype(np.int8)
chunks.append(chunk)
if not chunks:
return None, None, None
df = pd.concat(chunks, ignore_index=True)
label_col = next((c for c in df.columns if c.lower().strip() == 'label'), 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()
rename_map = {old: clean_col_name(old) for old in X.columns}
X = X.rename(columns=rename_map)
if X.columns.duplicated().any():
print("[-] Warning: Duplicate column names detected after cleaning. Deduplicating...")
X = X.loc[:, ~X.columns.duplicated()]
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_target = 0.001
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Balance Report: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight Config: Base Balanced={base_weight:.4f}, Final Adjusted={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...")
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.1 ----------------\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_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}")
+195 -101
View File
@@ -1,114 +1,192 @@
import os, sys, time, sqlite3, re, json
import pandas as pd
import os, sys, time, json, zlib, array, onnxmltools, multiprocessing
import numpy as np
import pandas as pd
import lightgbm as lgb
import onnxmltools
import scipy.sparse as sp
from collections import defaultdict
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
from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
####################################################################################################
DB_PATH = "pe_features.db"
JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_T1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
DLL_HASH_DIM = 256
API_HASH_DIM = 1024
EXCLUDE_FEATURES = {
'label', 'filehash',
}
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 63,
'learning_rate': 0.05,
'num_leaves': 128,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_fraction': 1.0,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 10,
'lambda_l1': 0.1,
'lambda_l2': 0.1,
'min_data_in_leaf': 50,
}
####################################################################################################
def clean_col_name(name):
return re.sub(r'[^A-Za-z0-9_]+', '', name)
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
num_lines = 0
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 = {'label', 'id', 'filename', 'filelength', 'rowid', 'probability', 'predictedlabel', 'score', 'filehash', 'timedatestamp'}
return [c for c in columns if c.lower().strip() not in exclude]
except Exception as e:
print(f"[-] Schema extraction error: {e}")
return None
finally:
conn.close()
with open(jsonl_path, 'rb') as f:
for line in f:
num_lines += 1
####################################################################################################
if not base_keys:
try:
data = JSON_LOADS(line)
base_data = data.get('Base')
if base_data:
base_keys.update(base_data.keys())
def load_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"
except Exception:
pass
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
api_schema = [f"ApiHash_{i:04d}" for i in range(API_HASH_DIM)]
try:
chunks = []
for chunk in pd.read_sql_query(sql, conn, chunksize=20000):
label_col = next((c for c in chunk.columns if c.lower().strip() == 'label'), None)
if not label_col:
print("[-] Critical Error: Label column not found.")
return None, None, None
cols_to_cast = [c for c in chunk.columns if c != label_col]
chunk[cols_to_cast] = chunk[cols_to_cast].astype(np.float32)
chunk[label_col] = chunk[label_col].astype(np.int8)
chunks.append(chunk)
if not chunks:
return None, None, None
df = pd.concat(chunks, ignore_index=True)
label_col = next((c for c in df.columns if c.lower().strip() == 'label'), 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()
rename_map = {old: clean_col_name(old) for old in X.columns}
X = X.rename(columns=rename_map)
if X.columns.duplicated().any():
print("[-] Warning: Duplicate column names detected after cleaning. Deduplicating...")
X = X.loc[:, ~X.columns.duplicated()]
final_features = X.columns.tolist()
return X, y, final_features
finally:
conn.close()
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def train_process(X, y):
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically via Multiprocessing...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
file_size = os.path.getsize(jsonl_path)
workers = min(os.cpu_count() or 4, 16)
chunk_size = file_size // workers
tasks = []
for i in range(workers):
start = i * chunk_size
end = file_size if i == workers - 1 else (i + 1) * chunk_size
tasks.append((jsonl_path, start, end, base_schema, num_features, offset_dll, offset_api))
with multiprocessing.Pool(workers) as pool:
results = pool.map(_chunk_worker, tasks)
Xs = [r[0] for r in results if r[0].shape[0] > 0]
ys = [r[1] for r in results if r[1].shape[0] > 0]
X_final = sp.vstack(Xs, format='csr') if Xs else sp.csr_matrix((0, num_features), dtype=np.float32)
y_final = np.concatenate(ys) if ys else np.array([], dtype=np.int8)
return X_final, pd.Series(y_final), final_features
def _chunk_worker(args):
filepath, start_byte, end_byte, base_schema, num_features, offset_dll, offset_api = args
base_idx = {feat: i for i, feat in enumerate(base_schema)}
base_idx_get = base_idx.get
rows = array.array('i')
cols = array.array('i')
vals = array.array('f')
y_list = array.array('b')
with open(filepath, 'rb') as f:
if start_byte > 0:
f.seek(start_byte - 1)
if f.read(1) != b'\n':
f.seek(start_byte)
f.readline()
else:
f.seek(start_byte)
else:
f.seek(0)
row_idx = 0
while f.tell() < end_byte:
line = f.readline()
if not line:
break
try:
data = JSON_LOADS(line)
y_list.append(data.get('Label', 0))
base_data = data.get('Base')
if base_data:
for k, v in base_data.items():
idx = base_idx_get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
dll_data = data.get('DLLs')
if dll_data:
for d in dll_data:
idx = zlib.crc32(d.encode('utf-8', 'ignore')) % DLL_HASH_DIM
rows.append(row_idx)
cols.append(offset_dll + idx)
vals.append(1.0)
api_data = data.get('APIs')
if api_data:
for a in api_data:
idx = zlib.crc32(a.encode('utf-8', 'ignore')) % API_HASH_DIM
rows.append(row_idx)
cols.append(offset_api + idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y_arr = np.frombuffer(y_list, dtype=np.int8)
np_rows = np.frombuffer(rows, dtype=np.int32)
np_cols = np.frombuffer(cols, dtype=np.int32)
np_vals = np.frombuffer(vals, dtype=np.float32)
X_local = sp.csr_matrix((np_vals, (np_rows, np_cols)), shape=(row_idx, num_features), dtype=np.float32)
return X_local, y_arr
####################################################################################################
def _calculate_dynamic_lr(current_iter: int) -> float:
return max(0.01, 0.1 - (current_iter // 80) * 0.01)
def train_process(X, y, feature_names):
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
)
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()
@@ -117,11 +195,11 @@ def train_process(X, y):
weight_ratio_target = 0.001
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Balance Report: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight Config: Base Balanced={base_weight:.4f}, Final Adjusted={final_pos_weight:.4f}")
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight correction: Base={base_weight:.4f}, Final={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)
train_data = lgb.Dataset(X_train, label=y_train, feature_name=feature_names)
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data, feature_name=feature_names)
params = LGBM_PARAMS.copy()
params['scale_pos_weight'] = final_pos_weight
@@ -133,14 +211,15 @@ def train_process(X, y):
params,
train_data,
valid_sets=[valid_data],
num_boost_round=1000,
num_boost_round=300,
callbacks=[
lgb.log_evaluation(period=100)
lgb.log_evaluation(period=50),
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
]
)
elapsed = time.time() - start_time
print(f"\n[+] Training finished in {elapsed:.2f}s")
print(f"\n[+] Training complete, time elapsed {elapsed:.2f} seconds")
return model, X_test, y_test
@@ -156,12 +235,10 @@ def export_onnx_model(model, feature_names):
with open(FEATURE_FILE, 'w') as f:
json.dump(feature_names, f)
print(f"[+] ONNX export success. Features synced to {FEATURE_FILE}")
print(f"[+] ONNX export successful. Feature order saved 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)
@@ -170,12 +247,25 @@ def evaluate_and_save(model, X_test, y_test):
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))
tp = np.sum((y_test == 1) & (y_pred == 1))
fn = np.sum((y_test == 1) & (y_pred == 0))
tn = np.sum((y_test == 0) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
total_malware = tp + fn
total_safe = tn + fp
detection_rate = (tp / total_malware * 100) if total_malware > 0 else 0.0
fpr = (fp / total_safe * 100) if total_safe > 0 else 0.0
print(f"Accuracy : {acc:.6f}")
print(f"ROC AUC : {auc:.6f}\n")
print(f"True Positive (Class 1) : {detection_rate:.3f}% ({tp}/{total_malware})")
print(f"False Positive (Class 0) : {fpr:.3f}% ({fp}/{total_safe})")
print("---------------------------------------------------------\n")
print("[*] Top 20 Features (Importance by Gain):")
print("\n---------------------------------------------------------\n")
print("[*] Top 20 Features:")
feature_names = model.feature_name()
importance = model.feature_importance(importance_type='gain')
@@ -192,25 +282,29 @@ def evaluate_and_save(model, X_test, y_test):
####################################################################################################
if __name__ == "__main__":
print("\n---------------- PE Malware Trainer v3.1 ----------------\n")
multiprocessing.freeze_support()
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(DB_PATH):
print(f"[-] Error: Database {DB_PATH} not found.")
if not os.path.exists(JSONL_PATH):
print(f"[-] Error: Feature file {JSONL_PATH} not found.")
sys.exit(1)
raw_schema = get_raw_schema(DB_PATH)
if not raw_schema:
print("[-] Error: Could not read schema.")
base_schema, dll_schema, api_schema, num_lines = analyze_schema(JSONL_PATH)
if num_lines == 0:
print("[-] Error: Feature file is empty.")
sys.exit(1)
X, y, _ = load_data(DB_PATH, raw_schema)
if X is None:
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Hashing DLL features: {len(dll_schema)}")
print(f"[*] Hashing API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
try:
model, X_test, y_test = train_process(X, y)
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training interrupted.")
print("\n\n[-] Training aborted.")
except Exception as e:
print(f"\n[-] Critical Error: {e}")
print(f"\n[-] System exception: {e}")
+841
View File
@@ -0,0 +1,841 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
outline: none;
}
:root {
--bg-window: rgb(245, 245, 247);
--bg-nav: rgb(255, 255, 255);
--bg-panel: rgb(255, 255, 255);
--bg-hover: rgba(0, 0, 0, 0.05);
--text-primary: rgb(29, 29, 31);
--text-secondary: rgb(134, 134, 139);
--border-color: rgb(210, 210, 215);
--accent-color: rgb(0, 122, 255);
--accent-alpha: rgba(0, 122, 255, 0.1);
--accent-shadow: rgba(0, 122, 255, 0.2);
--accent-hover: rgba(0, 122, 255, 0.3);
--shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.04);
}
body {
background-color: transparent;
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-primary);
}
.app-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: var(--bg-window);
}
.navigate-window {
height: 44px;
background-color: var(--bg-nav);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px 0 20px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.3s;
}
.navigate-title {
font-size: 17px;
font-weight: 600;
letter-spacing: -0.2px;
}
.window-controls {
display: flex;
gap: 4px;
-webkit-app-region: no-drag;
}
.ctrl-btn {
width: 46px;
height: 32px;
border: none;
background: transparent;
border-radius: 6px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.15s;
color: var(--text-secondary);
}
.ctrl-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
#close_button:hover { background-color: #ff3b30; color: white; }
.theme-svg {
width: 16px;
height: 16px;
}
.main-layout {
display: flex;
height: calc(100% - 44px);
}
.options-window {
width: 64px;
background-color: var(--bg-nav);
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 0;
gap: 12px;
border-right: 1px solid var(--border-color);
transition: background-color 0.3s;
}
.nav-btn {
width: 44px;
height: 44px;
border: none;
background: transparent;
border-radius: 10px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-secondary);
transition: all 0.2s;
position: relative;
}
.nav-btn svg { width: 22px; height: 22px; }
.nav-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.nav-btn.active { background-color: var(--accent-alpha); color: var(--accent-color); }
.spacer { flex-grow: 1; }
.content-window {
flex-grow: 1;
position: relative;
overflow: hidden;
}
.page {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 28px 32px;
opacity: 0;
visibility: hidden;
transform: translateY(8px);
transition: opacity 0.25s ease, transform 0.25s ease;
display: flex;
flex-direction: column;
}
.page.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.state-widget {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 16px;
color: var(--accent-color);
}
.state-title {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.5px;
}
.log-widget {
flex-grow: 1;
margin-top: 20px;
}
.license-widget {
flex-grow: 1;
}
.log-text, .license-text {
width: 100%;
height: 100%;
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
color: var(--text-primary);
resize: none;
padding: 16px;
font-size: 13px;
line-height: 1.6;
white-space: pre;
overflow-x: auto;
}
.progress-widget {
height: 70px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
gap: 16px;
}
.header-group {
flex: 1;
min-width: 0;
}
.header-group h2 {
font-size: 24px;
font-weight: 600;
letter-spacing: -0.5px;
white-space: nowrap;
}
.header-group p {
font-size: 14px;
color: var(--text-secondary);
margin-top: 4px;
white-space: nowrap;
overflow-x: auto;
padding-bottom: 4px;
}
.modern-action-group {
display: flex;
gap: 12px;
align-items: center;
flex-shrink: 0;
}
.modern-select {
appearance: none;
}
.modern-select:hover, .modern-select:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.2);
}
.modern-btn {
padding: 8px 20px;
border: 1px solid var(--border-color);
border-radius: 10px;
background-color: transparent;
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.modern-btn:hover { background-color: var(--bg-hover); }
.primary-btn {
background-color: var(--accent-color);
color: white;
border: none;
box-shadow: 0 2px 4px var(--accent-shadow);
}
.primary-btn:hover {
box-shadow: 0 4px 8px var(--accent-hover);
transform: translateY(-1px);
background-color: var(--accent-color);
opacity: 0.95;
}
.primary-btn:active {
transform: translateY(0);
opacity: 0.8;
}
.danger-btn { color: #ff3b30; border-color: #ff3b30; }
.danger-btn:hover { background-color: rgba(255, 59, 48, 0.1); }
.hidden { display: none !important; }
.scan-content {
flex-grow: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.virus-widget {
width: 100%;
flex-grow: 1;
min-height: 0;
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
overflow-y: auto;
overflow-x: auto;
}
.virus-list {
list-style: none;
padding: 0;
margin: 0;
min-width: max-content;
}
.list-widget {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
padding-right: 12px;
gap: 12px;
}
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 24px;
border-radius: 16px;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
transition: background-color 0.2s;
gap: 16px;
}
.item-info {
flex: 1;
min-width: 0;
overflow-x: auto;
padding-bottom: 2px;
}
.list-item[data-target]:hover, .list-item[data-action]:hover {
background-color: var(--bg-hover);
}
.item-info h2 {
font-size: 16px;
font-weight: 600;
letter-spacing: -0.2px;
white-space: nowrap;
}
.item-info p {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
white-space: nowrap;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: var(--border-color);
transition: .3s;
border-radius: 28px;
}
.slider:before {
position: absolute;
content: "";
height: 24px; width: 24px;
left: 2px; bottom: 2px;
background-color: white;
transition: .3s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.toggle-switch input:checked + .slider {
background-color: var(--accent-color);
}
.toggle-switch input:checked + .slider:before {
transform: translateX(22px);
}
.icon-action-btn {
width: 32px; height: 32px;
border-radius: 8px;
border: none;
background-color: transparent;
color: var(--text-secondary);
cursor: pointer;
display: flex; justify-content: center; align-items: center;
transition: background-color 0.2s;
}
.icon-action-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.icon-action-btn svg { width: 20px; height: 20px; }
.sub-header-toolbar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
width: 100%;
}
.sub-header-toolbar h2 {
font-size: 24px;
font-weight: 600;
letter-spacing: -0.5px;
white-space: nowrap;
}
.icon-btn {
background: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 6px;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
transition: background-color 0.2s;
}
.icon-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.icon-btn svg { width: 24px; height: 24px; }
.search-box {
display: flex;
align-items: center;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 10px;
padding: 0 14px;
width: 200px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.search-box:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px var(--accent-shadow);
}
.search-box svg {
width: 18px; height: 18px;
color: var(--text-secondary);
}
.search-box input {
border: none;
background: transparent;
color: var(--text-primary);
padding: 10px;
outline: none;
width: 100%;
font-size: 14px;
}
.flex-grow {
flex-grow: 1;
min-height: 0;
}
.manage-widget {
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
overflow-y: auto;
overflow-x: auto;
}
.manage-list {
list-style: none;
padding: 0;
margin: 0;
min-width: max-content;
}
.manage-list-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.2s;
}
.manage-list-item:last-child {
border-bottom: none;
}
.manage-list-item:hover {
background-color: var(--bg-hover);
}
.manage-list-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent-color);
cursor: pointer;
flex-shrink: 0;
}
.manage-list-item-content {
flex-grow: 1;
min-width: 0;
font-size: 14px;
color: var(--text-primary);
white-space: nowrap;
}
.custom-select-wrapper {
position: relative;
min-width: 100px;
display: inline-grid;
vertical-align: middle;
user-select: none;
}
select.hidden + .custom-select-wrapper {
display: none !important;
}
.custom-select-trigger {
grid-area: 1 / 1;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
border-radius: 10px;
border: 1px solid var(--border-color);
background-color: var(--bg-panel);
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: border-color 0.2s, box-shadow 0.2s;
gap: 12px;
box-sizing: border-box;
}
.custom-select-phantom {
grid-area: 1 / 1;
visibility: hidden;
pointer-events: none;
display: grid;
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
box-sizing: border-box;
}
.custom-select-phantom span {
grid-area: 1 / 1;
white-space: nowrap;
padding-right: 28px;
}
.custom-select-wrapper.open .custom-select-trigger,
.custom-select-trigger:hover {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px var(--accent-shadow);
}
.custom-select-icon {
display: flex;
align-items: center;
color: var(--text-secondary);
transition: transform 0.3s ease;
}
.custom-select-wrapper.open .custom-select-icon {
transform: rotate(180deg);
}
.custom-select-icon svg { width: 16px; height: 16px; }
.custom-select-options {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 100%;
width: max-content;
white-space: nowrap;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 10px;
box-shadow: var(--shadow-sm);
z-index: 100;
list-style: none;
margin: 0;
max-height: 0;
padding: 0 6px;
border-width: 0;
overflow: hidden;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s 0.2s, max-height 0s 0.2s, padding 0s 0.2s, border-width 0s 0.2s, box-shadow 0s 0.2s;
}
.custom-select-wrapper.open .custom-select-options {
max-height: 260px;
padding: 6px;
border-width: 1px;
overflow-y: auto;
opacity: 1;
visibility: visible;
transform: translateY(0);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s 0s, max-height 0s 0s, padding 0s 0s, border-width 0s 0s, box-shadow 0s 0s;
}
.custom-select-option {
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
color: var(--text-primary);
transition: background-color 0.2s, color 0.2s;
text-align: center;
}
.custom-select-option:hover {
background-color: var(--bg-hover);
}
.custom-select-option.selected {
background-color: var(--accent-alpha);
color: var(--accent-color);
font-weight: 600;
}
.home-top-widget {
display: flex;
align-items: center;
height: 200px;
gap: 24px;
width: 100%;
}
.quick-actions {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
justify-content: center;
}
#loading_overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #e0e0e0;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.4s ease, visibility 0.4s ease;
}
#loading_overlay.fade-out {
opacity: 0;
visibility: hidden;
}
.spinner {
animation: rotate 2s linear infinite;
width: 50px;
height: 50px;
}
.spinner .path {
stroke: #86868b;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
@keyframes dash {
0% { stroke-dasharray: 1, 150; stroke-dashoffset: 0; }
50% { stroke-dasharray: 90, 150; stroke-dashoffset: -35; }
100% { stroke-dasharray: 90, 150; stroke-dashoffset: -124; }
}
.app-container.fade-in {
opacity: 1 !important;
}
.manage-list-header {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-panel);
font-weight: 600;
font-size: 13px;
color: var(--text-secondary);
position: sticky;
top: 0;
z-index: 10;
}
.manage-list-header input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent-color);
cursor: pointer;
flex-shrink: 0;
}
.header-cols-container, .manage-list-item-content {
display: flex;
flex-grow: 1;
min-width: 0;
gap: 16px;
align-items: center;
}
.col-header {
position: relative;
cursor: pointer;
display: flex;
align-items: center;
padding: 0 8px;
border-right: 1px solid var(--border-color);
background-color: var(--bg-panel);
user-select: none;
-webkit-user-select: none;
}
.col-header:last-child {
border-right: none;
}
.header-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
}
.manage-list-header, .col-header, .col-resizer {
user-select: none;
-webkit-user-select: none;
}
.col-resizer {
position: absolute;
right: -5px;
top: 0;
bottom: 0;
width: 10px;
cursor: col-resize;
z-index: 10;
background-color: transparent !important;
}
.col-resizer::after {
content: "";
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background-color: transparent;
transition: background-color 0.2s;
}
.col-resizer:hover::after,
.col-resizer.resizing::after {
background-color: var(--text-secondary) !important;
opacity: 0.6;
}
body.resizing-active,
body.resizing-active * {
user-select: none !important;
-webkit-user-select: none !important;
cursor: col-resize !important;
}
.col-resizer:hover {
background-color: var(--accent-color);
opacity: 0.5;
}
.col-header:hover {
color: var(--text-primary);
}
.sort-icon {
font-size: 10px;
margin-left: 4px;
color: var(--accent-color);
width: 12px;
}
.row-col {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
color: var(--text-primary);
}
.custom-context-menu {
position: absolute;
z-index: 10000;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 4px;
display: none;
min-width: 160px;
}
.custom-context-menu.show {
display: block;
}
.custom-context-menu-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
color: var(--text-primary);
transition: background-color 0.2s;
}
.custom-context-menu-item:hover {
background-color: var(--bg-hover);
}
.custom-context-menu-item svg {
width: 16px;
height: 16px;
color: var(--text-secondary);
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); }
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+570
View File
@@ -0,0 +1,570 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PYAS Security</title>
<link rel="stylesheet" href="/static/css/style.css">
<script defer src="/static/js/i18n.js"></script>
<script defer src="/static/js/main.js"></script>
</head>
<body>
<div id="loading_overlay">
<svg class="spinner" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
</svg>
</div>
<div class="app-container" style="opacity: 0; transition: opacity 0.5s ease;">
<header class="navigate-window pywebview-drag-region">
<div class="navigate-title" data-i18n>PYAS Security</div>
<div class="window-controls">
<button class="ctrl-btn" id="about_button" data-target="about_window">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
</button>
<button class="ctrl-btn" id="minimize_button">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
</button>
<button class="ctrl-btn" id="close_button">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
</header>
<div class="main-layout">
<aside class="options-window pywebview-drag-region">
<button class="nav-btn active" data-target="home_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
</button>
<button class="nav-btn" data-target="scan_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
</button>
<button class="nav-btn" data-target="tool_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
</button>
<button class="nav-btn" data-target="protect_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
</button>
<div class="spacer"></div>
<button class="nav-btn" data-target="setting_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</button>
</aside>
<main class="content-window">
<section id="home_window" class="page active">
<div class="home-top-widget">
<div class="state-widget">
<svg viewBox="0 0 24 24" class="state-icon theme-svg" style="width: 120px; height: 120px;" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><polyline points="9 12 11 14 15 10"></polyline></svg>
<h2 class="state-title" data-i18n>此裝置已受到防護</h2>
</div>
<div class="quick-actions">
<div class="list-item" style="cursor: pointer;" id="quick_scan_btn" data-action="scan">
<div class="item-info">
<h2 data-i18n>智能掃描</h2><p data-i18n>此選項可以快速掃描裝置安全性</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="log_export_window">
<div class="item-info">
<h2 data-i18n>日誌導出</h2><p data-i18n>此選項可以篩選日誌並匯出存檔</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</div>
<div class="log-widget">
<textarea class="log-text" readonly></textarea>
</div>
</section>
<section id="scan_window" class="page">
<div class="progress-widget">
<div class="header-group">
<h2 class="section-title" data-i18n>病毒掃描</h2>
<p class="section-desc" id="progress_text" data-i18n>此選項可以選擇進階掃描方式</p>
</div>
<div class="action-group modern-action-group">
<select id="scan_method_select" class="modern-select">
<option value="none" selected disabled data-i18n>選擇</option>
<option value="smart" data-i18n>智能掃描</option>
<option value="file" data-i18n>檔案掃描</option>
<option value="path" data-i18n>路徑掃描</option>
<option value="full" data-i18n>全盤掃描</option>
</select>
<button class="action-btn modern-btn hidden" id="stop_btn" data-i18n>停止</button>
</div>
</div>
<div class="scan-content">
<div class="virus-widget">
<ul class="virus-list" id="virus_list"></ul>
</div>
</div>
</section>
<section id="protect_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>進程防護</h2><p data-i18n>此選項可以終止惡意進程運行</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>進程暫停</h2><p data-i18n>此選項可以暫停進程等待掃描</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>檔案防護</h2><p data-i18n>此選項可以隔離惡意檔案建立</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>系統防護</h2><p data-i18n>此選項可以實時修復系統異常</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>驅動防護</h2><p data-i18n>此選項可以實時攔截惡意行為</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>網路防護</h2><p data-i18n>此選項可以攔截惡意網路連接</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item" style="cursor: pointer;" data-target="custom_protect_window">
<div class="item-info">
<h2 data-i18n>自訂防護</h2><p data-i18n>此選項可以導入自訂防護規則</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</section>
<section id="tool_window" class="page">
<div class="list-widget">
<div class="list-item" style="cursor: pointer;" data-target="taskmgr_window">
<div class="item-info">
<h2 data-i18n>進程管理</h2><p data-i18n>此選項可以實時查看系統進程</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="startup_window">
<div class="item-info">
<h2 data-i18n>啟動項管理</h2><p data-i18n>此選項可以管理各應用啟動項</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="junk_window">
<div class="item-info">
<h2 data-i18n>垃圾清理</h2><p data-i18n>此選項可以清理暫存垃圾檔案</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="repair_window">
<div class="item-info">
<h2 data-i18n>系統修復</h2><p data-i18n>此選項可以修復系統登錄檔項目</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="popup_window">
<div class="item-info">
<h2 data-i18n>彈窗攔截</h2><p data-i18n>此選項可以選擇指定窗口攔截</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="netmon_window">
<div class="item-info">
<h2 data-i18n>流量監控</h2><p data-i18n>此選項可以實時監控網路流量</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="whitelist_window">
<div class="item-info">
<h2 data-i18n>白名單</h2><p data-i18n>此選項可以查看並管理白名單</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="quarantine_window">
<div class="item-info">
<h2 data-i18n>隔離區</h2><p data-i18n>此選項可以查看並管理隔離區</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</section>
<section id="setting_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>增強模式</h2><p data-i18n>此選項可以增強靜態掃描引擎</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>擴展引擎</h2><p data-i18n>此選項可以使用擴展規則引擎</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>雲端服務</h2><p data-i18n>此選項可以自動上報可疑檔案</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>開機啟動</h2><p data-i18n>此選項可以設定開機自動執行</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>選單掃描</h2><p data-i18n>此選項可以啟用右鍵掃描功能</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>顯色主題</h2><p data-i18n>此選項可以選擇主題顏色</p>
</div>
<select id="theme_select" class="modern-select">
<option value="system_switch" selected data-i18n>跟隨系統</option>
<option value="white_switch" data-i18n>白色主題</option>
<option value="red_switch" data-i18n>紅色主題</option>
<option value="yellow_switch" data-i18n>黃色主題</option>
<option value="green_switch" data-i18n>綠色主題</option>
<option value="blue_switch" data-i18n>藍色主題</option>
<option value="black_switch" data-i18n>黑色主題</option>
</select>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>顯示語言</h2><p data-i18n>此選項可以選擇顯示語言</p>
</div>
<select id="lang_select" class="modern-select">
<option value="english_switch">English</option>
<option value="traditional_switch">繁體中文</option>
<option value="simplified_switch">简体中文</option>
<option value="japanese_switch">日本語</option>
<option value="korean_switch">한국어</option>
<option value="french_switch">Français</option>
<option value="spanish_switch">Español</option>
<option value="hindi_switch">हिन्दी</option>
<option value="arabic_switch">العربية</option>
<option value="russian_switch">Русский</option>
<option value="slovenian_switch">Slovenščina</option>
</select>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>重置選項</h2><p data-i18n>此選項可以重置所有設定</p>
</div>
<button class="action-btn modern-btn danger-btn" data-i18n>執行</button>
</div>
</div>
</section>
<section id="about_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>產品金鑰</h2><p data-i18n>此選項可以啟用產品金鑰</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path></svg></button>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>官方網站</h2><p data-i18n>此選項可以前往官方網站</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg></button>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>檢查更新</h2><p data-i18n>此選項可以檢查可用更新</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg></button>
</div>
<div class="license-widget">
<textarea class="license-text" readonly>87owo Publicly Available Source Code License Agreement
Last Updated: 2026-06-09
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
1. Definitions and Interpretation
- 1.1. Author: Refers to 87owo (PYAS Security).
- 1.2. Software: Refers to the source code, binaries, project archives, assets, and documentation associated with this repository or distribution.
- 1.3. Personal Local Use: Means viewing, downloading, executing, and modifying the Software strictly on local hardware owned or directly controlled by an individual for private, non-commercial, non-production, and non-distribution purposes.
- 1.4. Enterprise Use: Includes enterprise internal deployment, multi-user institutional use, or utilization within any corporate environment, which is strictly excluded from Personal Local Use.
2. Intellectual Property and Ownership
- 2.1. Retention of Rights: The Author reserves all legal rights, titles, and interests in and to the Software, including but not limited to all copyrights, trademarks, patents, and trade secrets.
- 2.2. No Transfer: No intellectual property rights or ownership stakes are transferred under this Agreement. All project components remain fully proprietary.
- 2.3. Activation Keys: Access to or execution of certain premium features, modules, or binary distributions within the Software may require a valid, authorized activation key issued solely by the Author.
3. Grant of License and Permitted Use
Subject to your strict compliance with all terms of this Agreement, the Author hereby grants you a limited, non-exclusive, world-wide, royalty-free, revocable, and non-transferable license to:
- 3.1. Code Inspection: View and download the source code from official authorized repositories.
- 3.2. Local Modification: Modify the source code strictly for the purposes of Personal Local Use.
- 3.3. Local Execution: Execute the Software locally for personal evaluation and private deployment.
4. Prohibitions and Restrictions
Except as explicitly granted in Section 3, you shall not, and shall not permit any third party to:
- 4.1. Unauthorized Redistribution: Copy, redistribute, sublicense, republish, mirror, or publicly disclose any original or modified versions of the source code, binaries, or assets.
- 4.2. Commercial and SaaS Deployment: Host, deploy, embed, or utilize the Software for commercial purposes, including but not limited to Software-as-a-Service (SaaS), commercial web hosting, paid remote execution, cloud infrastructure integration, or enterprise internal utility.
- 4.3. Reverse Engineering: Reverse engineer, decompile, decrypt, or disassemble any binary components of the Software, except and only to the extent that such restriction is explicitly prohibited by applicable mandatory laws.
- 4.4. Notice Preservation: Remove, obscure, deface, or alter any copyright notices, trademarks, hyperlinked terms, or license files included within the Software distribution.
5. Prior Authorization and Application Channels
- 5.1. Authorization Requirement: Any activity exceeding the scope of Personal Local Use (including but not limited to public redistribution, independent forks not targeting upstream contributions, commercial deployment, or enterprise use) requires prior explicit written consent from the Author.
- 5.2. Official Application Channels: Users seeking such authorization must submit a formal request through one of the following official channels:
- Channel A (GitHub Fork): Initiating a standard Fork on the official GitHub repository for collaborative and upstream contributions.
- Channel B (GitHub Issues): Submitting a public issue utilizing the designated authorization request template.
- Channel C (Official Email): Sending a detailed inquiry to the official organizational email address associated with the PYAS Security project.
- 5.3. Request Content: Any application must explicitly state the specific use case, intended target audience, scope of deployment, and whether any commercial monetization is involved.
6. Updates, Amendments, and Scope of Terms
- 6.1. New Releases: The Author may deliver updates, critical security patches, feature enhancements, or user interface modifications. Continued use of the Software constitutes acceptance of the latest distributed version.
- 6.2. Non-Retroactivity: The Author reserves the right to amend this License Agreement at any time. Amendments apply strictly to subsequent versions or new downloads. Copies lawfully acquired prior to such modification remain governed by the exact version of the License under which they were originally obtained.
7. Telemetry, Data, and Privacy
- 7.1. Operational Data Collection: The Software may collect anonymized technical, operational, and usage data strictly for security analysis, performance optimization, and bug reporting.
- 7.2. Cloud Sample Submission: To enhance threat detection and malware analysis, the Software may automatically or manually transmit suspicious executable files and software samples to the Author's cloud infrastructure for deep analysis.
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
8. Warranty Disclaimer and Limitation of Liability
- 8.1. Disclaimer of Warranty: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE AND ALL RELATED ASSETS ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED.
- 8.2. Limitation of Liability: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE. USERS ASSUME ALL RISKS REGARDING PERFORMANCE, DATA INTEGRITY, AND SYSTEM STABILITY.
© 2020-2026 87owo (PYAS Security). All rights reserved.</textarea>
</div>
</div>
</section>
<section id="taskmgr_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>進程管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋進程..." placeholder="搜尋進程...">
</div>
<div class="action-group modern-action-group">
<select id="taskmgr_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="view" data-i18n="btn_view_item">查看項目</option>
<option value="end" data-i18n="btn_end">結束進程</option>
<option value="quarantine" data-i18n="act_quarantine">加入隔離區</option>
<option value="whitelist" data-i18n="act_white">加入白名單</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="startup_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>啟動項管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋項目..." placeholder="搜尋項目...">
</div>
<div class="action-group modern-action-group">
<select id="startup_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="enable" data-i18n="btn_enable_item">啟用項目</option>
<option value="disable" data-i18n="btn_disable_item">禁用項目</option>
<option value="delete" data-i18n="btn_delete_item">刪除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="whitelist_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>白名單管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<div class="action-group modern-action-group">
<select id="whitelist_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add_file" data-i18n="btn_add_file">新增檔案</option>
<option value="add_path" data-i18n="btn_add_path">新增路徑</option>
<option value="extract" data-i18n="btn_extract_item">提取項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
<option value="quarantine" data-i18n="act_quarantine">加入隔離區</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="quarantine_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>隔離區管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<div class="action-group modern-action-group">
<select id="quarantine_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add_file" data-i18n="btn_add_file">新增檔案</option>
<option value="add_path" data-i18n="btn_add_path">新增路徑</option>
<option value="extract" data-i18n="btn_extract_item">提取項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
<option value="whitelist" data-i18n="act_white">加入白名單</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="junk_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>垃圾清理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<div class="action-group modern-action-group">
<select id="junk_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="view" data-i18n="btn_view_item">查看項目</option>
<option value="delete" data-i18n="btn_delete_item">刪除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="repair_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>系統修復</h2>
<div class="spacer"></div>
<div class="action-group modern-action-group">
<select id="repair_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="repair" data-i18n="btn_repair_item">修復項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="popup_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>彈窗攔截</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋窗口..." placeholder="搜尋窗口...">
</div>
<div class="action-group modern-action-group">
<select id="popup_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add" data-i18n="btn_add_item">新增項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="netmon_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>流量監控</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋進程..." placeholder="搜尋進程...">
</div>
<div class="action-group modern-action-group">
<select id="netmon_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="view" data-i18n="btn_view_item">查看項目</option>
<option value="end" data-i18n="btn_end">結束進程</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="custom_protect_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="protect_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>自訂防護</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋規則..." placeholder="搜尋規則...">
</div>
<div class="action-group modern-action-group">
<select id="custom_protect_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add" data-i18n="btn_add_item">新增項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="log_export_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="home_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>日誌導出</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋日誌..." placeholder="搜尋日誌...">
</div>
<div class="action-group modern-action-group">
<select id="log_export_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="export" data-i18n="btn_export">導出項目</option>
<option value="delete" data-i18n="btn_remove_item">移除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
</main>
</div>
</div>
</body>
</html>
+53 -23
View File
@@ -1,31 +1,61 @@
87owo Publicly Available Source Code License Agreement
Last updated: 2025-12-19
1. License and Ownership
- This software and its source code are source available for public view.
- Access or execution of certain features may require an activation key.
- 87owo reserves all legal rights, no intellectual property is transferred.
- All project archives, source code, and binaries remain fully proprietary.
Last Updated: 2026-06-09
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
2. Use and Prohibitions
- You may view, download, and modify the code for local and personal use.
- Redistribution of any original or modified code requires prior consent.
- Commercial hosting or SaaS deployment is prohibited without permission.
- Reverse engineering, license removal, or illegal use is strictly barred.
1. Definitions and Interpretation
3. Data and Privacy
- This project may collect operational data for optimization and analysis.
- All collected data is strictly anonymized to ensure the user's privacy.
- Transmission of information is encrypted to prevent unauthorized access.
- 1.1. Author: Refers to 87owo (PYAS Security).
- 1.2. Software: Refers to the source code, binaries, project archives, assets, and documentation associated with this repository or distribution.
- 1.3. Personal Local Use: Means viewing, downloading, executing, and modifying the Software strictly on local hardware owned or directly controlled by an individual for private, non-commercial, non-production, and non-distribution purposes.
- 1.4. Enterprise Use: Includes enterprise internal deployment, multi-user institutional use, or utilization within any corporate environment, which is strictly excluded from Personal Local Use.
4. Updates and Terms
- New releases may include critical patches, enhancements, or UI changes.
- Continued use of these services constitutes acceptance of updated terms.
- The author reserves the right to modify these license terms at any time.
2. Intellectual Property and Ownership
5. Warranty and Liability
- All services and assets are provided "as is" without any form of warranty.
- 87owo is not liable for any direct or indirect damages from software use.
- Users assume all risks regarding performance, data, and system integrity.
- 2.1. Retention of Rights: The Author reserves all legal rights, titles, and interests in and to the Software, including but not limited to all copyrights, trademarks, patents, and trade secrets.
- 2.2. No Transfer: No intellectual property rights or ownership stakes are transferred under this Agreement. All project components remain fully proprietary.
- 2.3. Activation Keys: Access to or execution of certain premium features, modules, or binary distributions within the Software may require a valid, authorized activation key issued solely by the Author.
3. Grant of License and Permitted Use
Subject to your strict compliance with all terms of this Agreement, the Author hereby grants you a limited, non-exclusive, world-wide, royalty-free, revocable, and non-transferable license to:
- 3.1. Code Inspection: View and download the source code from official authorized repositories.
- 3.2. Local Modification: Modify the source code strictly for the purposes of Personal Local Use.
- 3.3. Local Execution: Execute the Software locally for personal evaluation and private deployment.
4. Prohibitions and Restrictions
Except as explicitly granted in Section 3, you shall not, and shall not permit any third party to:
- 4.1. Unauthorized Redistribution: Copy, redistribute, sublicense, republish, mirror, or publicly disclose any original or modified versions of the source code, binaries, or assets.
- 4.2. Commercial and SaaS Deployment: Host, deploy, embed, or utilize the Software for commercial purposes, including but not limited to Software-as-a-Service (SaaS), commercial web hosting, paid remote execution, cloud infrastructure integration, or enterprise internal utility.
- 4.3. Reverse Engineering: Reverse engineer, decompile, decrypt, or disassemble any binary components of the Software, except and only to the extent that such restriction is explicitly prohibited by applicable mandatory laws.
- 4.4. Notice Preservation: Remove, obscure, deface, or alter any copyright notices, trademarks, hyperlinked terms, or license files included within the Software distribution.
5. Prior Authorization and Application Channels
- 5.1. Authorization Requirement: Any activity exceeding the scope of Personal Local Use (including but not limited to public redistribution, independent forks not targeting upstream contributions, commercial deployment, or enterprise use) requires prior explicit written consent from the Author.
- 5.2. Official Application Channels: Users seeking such authorization must submit a formal request through one of the following official channels:
- Channel A (GitHub Fork): Initiating a standard Fork on the official GitHub repository for collaborative and upstream contributions.
- Channel B (GitHub Issues): Submitting a public issue utilizing the designated authorization request template.
- Channel C (Official Email): Sending a detailed inquiry to the official organizational email address associated with the PYAS Security project.
- 5.3. Request Content: Any application must explicitly state the specific use case, intended target audience, scope of deployment, and whether any commercial monetization is involved.
6. Updates, Amendments, and Scope of Terms
- 6.1. New Releases: The Author may deliver updates, critical security patches, feature enhancements, or user interface modifications. Continued use of the Software constitutes acceptance of the latest distributed version.
- 6.2. Non-Retroactivity: The Author reserves the right to amend this License Agreement at any time. Amendments apply strictly to subsequent versions or new downloads. Copies lawfully acquired prior to such modification remain governed by the exact version of the License under which they were originally obtained.
7. Telemetry, Data, and Privacy
- 7.1. Operational Data Collection: The Software may collect anonymized technical, operational, and usage data strictly for security analysis, performance optimization, and bug reporting.
- 7.2. Cloud Sample Submission: To enhance threat detection and malware analysis, the Software may automatically or manually transmit suspicious executable files and software samples to the Author's cloud infrastructure for deep analysis.
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
8. Warranty Disclaimer and Limitation of Liability
- 8.1. Disclaimer of Warranty: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE AND ALL RELATED ASSETS ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED.
- 8.2. Limitation of Liability: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE. USERS ASSUME ALL RISKS REGARDING PERFORMANCE, DATA INTEGRITY, AND SYSTEM STABILITY.
© 2020-2026 87owo (PYAS Security). All rights reserved.
+3021 -1990
View File
File diff suppressed because it is too large. Load diff
-555
View File
@@ -1,555 +0,0 @@
translate_dict = {
"traditional_switch": {"开启":"開啟","关闭":"關閉","On":"開啟","Off":"關閉"},
"simplified_switch": {"開啟":"开启","關閉":"关闭","On":"开启","Off":"关闭",
"錯誤":"错误","發現":"发现","關閉":"关闭","裝置":"设备","記憶體":"内存",
"軟體":"软件","檔案":"文件","確定":"确认","設定":"设置","關於":"关于",
"總數":"总数","註冊":"注册","圖標":"图标","路徑":"路径","網路":"网络",
"位元":"字节","靈敏":"敏感","複製":"复制","開啟":"开启","靜態":"静态",
"訊":"讯","項":"项","掃":"扫","試":"试","黃":"黄","嗎":"吗","防":"保",
"鑰":"钥","當":"当","後":"后","護":"护","導":"导","區":"区","復":"复",
"攔":"拦","請":"请","統":"统","刪":"删","進":"进","個":"个","選":"选",
"啟":"启","監":"监","並":"并","徑":"径","盤":"盘","隱":"隐","動":"动",
"狀":"状","誌":"志","這":"这","會":"会","題":"题","擇":"择","語":"语",
"無":"无","顯":"显","變":"变","戶":"户","碼":"码","雲":"云","介":"界",
"稱":"称","類":"类","輸":"输","執":"执","數":"数","尋":"寻","網":"网",
"詢":"询","製":"制","員":"员","許":"许","條":"条","藍":"蓝","紅":"红",
"綠":"绿","態":"态","務":"务","連":"连","檢":"检","測":"测","關":"关",
"強":"强","驅":"驱","為":"为","腳":"脚","單":"单","開":"开","擴":"扩",
"惡":"恶","據":"据","庫":"库","經":"经","敗":"败","謝":"谢","別":"别",
"載":"载","誤":"误","報":"报","鏈":"链","竄":"窜","蹤":"踪","與":"与",
"異":"异","實":"实","時":"时","彈":"弹","窗":"窗","暫":"暂","訂":"订",
"資":"资","產":"产","金":"密","離":"离","規":"规","則":"则","來":"来",
"殼":"壳","碼":"码","線":"线","餘":"余",},
"english_switch": {"開啟":"On","關閉":"Off","开启":"On","关闭":"Off",",":", ",
"立即刪除":"Delete Now","安全模式":"Safe Mode","官方網站":"Official Website ",
"腳本":"Script ","鏈式":"Chain ","記憶體":"Memory ","正在加載":"Loading ","加載":"Load ",
"行為":"Behavior ","可疑":"Suspicious ","勒索":"Ransom ","主要":"Main ",
"許可條款":"License Terms ","惡意":"Malicious", "開發":"Developers ",
"啟用":"Enable ","禁用":"Disable ","正在初始化中":"Initializing ","初始化":"initialize ",
"正在掃描":"Scanning ","正在刪除":"Deleting ","正在追蹤":"Tracking ",
"最小化到":"Minimize To ","靈敏度":"Sensitivity ","正在檢查":"Checking ",
"確定要":"Sure Want To ","引擎靈敏度":"Sensitivity ","更新":"Update ",
"白色主題":"White","黃色主題":"Yellow","紅色主題":"Red","語言":"Language ",
"藍色主題":"Blue","綠色主題":"Green","自訂主題":"Custom","黑色主題":"Black",
"特別感謝":"Contributors ","版本資訊":"Information","通訊":"Connect ",
"註冊":"Registry ","退出":"Quit ","檔案":"File ","當前":"Currently ",
"狀態":"State ","掃描":"Scan ","工具":"Tools ","防護":"Protect ",
"失敗":"Failed ","請":"Please ","路徑":"Path ","方式":"Method ",
"需要":"Need ","病毒":"Virus ","選擇":"Select ","返回":"Back",
"受到":"Been ","錯誤":"Error","完成":"Complete ","引擎":"Engine ",
"托盤":"Tray ","圖標":"Icon ","不安全":"Unsafe","增強":"Enhance ",
"系統":"System ","雲端":"Cloud ","重啟":"Reboot ","修復":"Repair ",
"裝置":"Device ","發現":"Found ","成功":"Success ","清理":"Cleanup ",
"確定":"OK","執行":"Execute ","警告":"Warning ","使用":"Use ",
"網路":"Network ","變更":"Change ","總數":"Total","結束":"End ",
"選項":"Options ","監控":"Monitor ","連接":"Connect ","可以":"Can ",
"進程":"Process ","攔截":"Block ","引導":"Boot ","刪除":"Delete ",
"分區":"Partition ","設定":"Setting ","關於":"About","模式":"Mode ",
"提高":"Improve ","安全":"Security ","顯色":"Color ","顏色":"Color ",
"主題":"Theme ", "回收":"Collect ", "無用":"Useless ","導入":"Import ",
"風險":"Risk ","介面":"UI ","核心":"Core ","服務":"Services ",
"管理":"Manage","所有":"All ","您":"You ","取消":"Cancel ","未":"Not ",
"垃圾":"Junk ","位元":"byte ","本機":"Computer ","停止":"Stop ",
"到":"To ","白名單":"White List ","軟體":"Software ","正在":"",
"增加":"Add ","移除":"Delete ","項目":"Items ","生成":"Create ","稱":"",
"複製":"Copy ","此":"This ","並":"And ","全盤":"Full ","最新":"Latest ",
"已存在":"Already ","數據庫":"Database ","誤報":"False Positive ",
"高":"High ","新":"New ","和":"And ","可能會":"Possible ","會":"Can ",
"日誌":"Log","已":"Has ","彈窗":"Window ","要":"To ","驅動":"Driver ",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Rule ",
"密鑰":"Key ","檢測":"Detect ","製作":"Maker ","運行":"Running",
"擴展":"Extension ","套件":"Kit ","自動":"Auto ","上報":"Report ",
"分析":"Analysis ","自身":"Self ","其他":"Other ","配置":"Config ",
"丟失":"Lost ","網站":"Website ","不兼容":"Not Compatible ","列表":"List ",
"竄改":"Tamper ","第三方":"Other ","實時":"Real Time ","查看":"View ",
"指定":"Specify ","清空":"Empty ","資源回收桶":"Recycle Bin ", "無法":"Cannot ",
"與":"With ","當":"When ","功能":"Function ","異常":"Abnormal ", "來":"To ",
"重置":"Reset ","窗口":"Window ","暫存":"Temp ","自訂":"Custom ",
"不支持":"Not Support ","共":"Total ","秒":"Seconds ","耗時":"Takes ",
"沙箱":"Sandbox ", "虛擬":"Virtual ", "隔離":"Quarantine ", "時":"",
"建立":"Create ","環境":"Environment ","化":"","表":"",
"達":"Reached ", "上限":"Limit ", "區":"", "金鑰":"Key ", "產品":"Product ",
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ",
"版本":"Version ","是":"","剩餘":"Remain ","遠控":"Remote "," ":" "},
"japanese_switch": {"開啟":"オン","關閉":"オフ","开启":"オン","关闭":"オフ",",":", ",
"立即刪除":"即時削除","安全模式":"セーフモード","官方網站":"公式サイト",
"腳本":"スクリプト","鏈式":"チェーン","記憶體":"メモリ","正在加載":"読み込み中","加載":"ロード",
"行為":"振る舞い","可疑":"疑わしい","勒索":"ランサム","主要":"メイン",
"許可條款":"ライセンス条項","惡意":"悪意のある","開發":"開発",
"啟用":"有効化","禁用":"無効化","正在初始化中":"初期化中","初始化":"初期化",
"正在掃描":"スキャン中","正在刪除":"削除中","正在追蹤":"追跡中",
"最小化到":"最小化先","靈敏度":"感度","正在檢查":"確認中",
"確定要":"本当によろしいですか","引擎靈敏度":"エンジン感度","更新":"更新",
"白色主題":"ホワイト","黃色主題":"イエロー","紅色主題":"レッド","語言":"言語",
"藍色主題":"ブルー","綠色主題":"グリーン","自訂主題":"カスタム","黑色主題":"ブラック",
"特別感謝":"貢献者","版本資訊":"バージョン情報","通訊":"通信",
"註冊":"レジストリ","退出":"終了","檔案":"ファイル","當前":"現在の",
"狀態":"状態","掃描":"スキャン","工具":"ツール","防護":"保護",
"失敗":"失敗","請":"してください","路徑":"パス","方式":"方式",
"需要":"必要","病毒":"ウイルス","選擇":"選択","返回":"戻る",
"受到":"受けた","錯誤":"エラー","完成":"完了","引擎":"エンジン",
"托盤":"トレイ","圖標":"アイコン","不安全":"非セキュア","增強":"強化",
"系統":"システム","雲端":"クラウド","重啟":"再起動","修復":"修復",
"裝置":"デバイス","發現":"検出","成功":"成功","清理":"クリーンアップ",
"確定":"確定","執行":"実行","警告":"警告","使用":"使用",
"網路":"ネットワーク","變更":"変更","總數":"合計","結束":"終了",
"選項":"オプション","監控":"監視","連接":"接続","可以":"可能",
"進程":"プロセス","攔截":"ブロック","引導":"ブート","刪除":"削除",
"分區":"パーティション","設定":"設定","關於":"情報","模式":"モード",
"提高":"向上","安全":"セキュリティ","顯色":"色","顏色":"色",
"主題":"テーマ","回收":"回収","無用":"不要な","導入":"インポート",
"風險":"リスク","介面":"インターフェース","核心":"コア","服務":"サービス",
"管理":"管理","所有":"すべての","您":"あなた","取消":"キャンセル","未":"未",
"垃圾":"ジャンク","位元":"バイト","本機":"PC","停止":"停止",
"到":"へ","白名單":"ホワイトリスト","軟體":"ソフトウェア","正在":"",
"增加":"追加","移除":"削除","項目":"項目","生成":"生成","稱":"",
"複製":"コピー","此":"この","並":"および","全盤":"フル","最新":"最新",
"已存在":"既に存在します","數據庫":"データベース","誤報":"誤検知",
"高":"高","新":"新","和":"と","可能會":"可能性があります","會":"します",
"日誌":"ログ","已":"済み","彈窗":"ポップアップ","要":"する","驅動":"ドライバ",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"ルール",
"密鑰":"キー","檢測":"検知","製作":"作成","運行":"実行中",
"擴展":"拡張","套件":"キット","自動":"自動","上報":"報告",
"分析":"分析","自身":"自身","其他":"その他","配置":"設定",
"丟失":"紛失","網站":"ウェブサイト","不兼容":"非互換","列表":"リスト",
"竄改":"改ざん","第三方":"サードパーティ","實時":"リアルタイム","查看":"表示",
"指定":"指定","清空":"空にする","資源回收桶":"ごみ箱","無法":"できません",
"與":"と","當":"時","功能":"機能","異常":"異常","來":"へ",
"重置":"リセット","窗口":"ウィンドウ","暫存":"一時","自訂":"カスタム",
"不支持":"未対応","共":"合計","秒":"秒","耗時":"所要時間",
"沙箱":"サンドボックス","虛擬":"仮想","隔離":"隔離","時":"",
"建立":"作成","環境":"環境","化":"","表":"",
"達":"到達","上限":"上限","區":"","金鑰":"キー","產品":"製品",
"運行庫":".NET CLR","殼碼":"シェルコード","線程":"スレッド","注入":"インジェクション",
"前往":"移動","檢查":"確認","可用":"利用可能","靜態":"静的",
"版本":"バージョン","是":"","剩餘":"残り","遠控":"遠隔操作"," ":" "},
"korean_switch": {"開啟":"켜기","關閉":"끄기","开启":"켜기","关闭":"끄기",",":", ",
"立即刪除":"즉시 삭제","安全模式":"안전 모드","官方網站":"공식 웹사이트",
"腳本":"스크립트","鏈式":"체인","記憶體":"메모리","正在加載":"로딩 중","加載":"로드",
"行為":"행위","可疑":"의심스러운","勒索":"랜섬웨어","主要":"주요",
"許可條款":"라이선스 약관","惡意":"악성","開發":"개발",
"啟用":"활성화","禁用":"비활성화","正在初始化中":"초기화 중","初始化":"초기화",
"正在掃描":"스캔 중","正在刪除":"삭제 중","正在追蹤":"추적 중",
"最小化到":"최소화","靈敏度":"민감도","正在檢查":"검사 중",
"確定要":"정말","引擎靈敏度":"엔진 민감도","更新":"업데이트",
"白色主題":"화이트","黃色主題":"옐로우","紅色主題":"레드","語言":"언어",
"藍色主題":"블루","綠色主題":"그린","自訂主題":"사용자 지정","黑色主題":"블랙",
"特別感謝":"기여자","版本資訊":"버전 정보","通訊":"통신",
"註冊":"레지스트리","退出":"종료","檔案":"파일","當前":"현재",
"狀態":"상태","掃描":"스캔","工具":"도구","防護":"보호",
"失敗":"실패","請":"부탁합니다","路徑":"경로","方式":"방식",
"需要":"필요","病毒":"바이러스","選擇":"선택","返回":"뒤로",
"受到":"받음","錯誤":"오류","完成":"완료","引擎":"엔진",
"托盤":"트레이","圖標":"아이콘","不安全":"안전하지 않음","增強":"강화",
"系統":"시스템","雲端":"클라우드","重啟":"재부팅","修復":"복구",
"裝置":"장치","發現":"발견","成功":"성공","清理":"정리",
"確定":"확인","執行":"실행","警告":"경고","使用":"사용",
"網路":"네트워크","變更":"변경","總數":"총계","結束":"종료",
"選項":"옵션","監控":"모니터링","連接":"연결","可以":"가능",
"進程":"프로세스","攔截":"차단","引導":"부트","刪除":"삭제",
"分區":"파티션","設定":"설정","關於":"정보","模式":"모드",
"提高":"향상","安全":"보안","顯色":"색상","顏色":"색상",
"主題":"테마","回收":"회수","無用":"불필요한","導入":"가져오기",
"風險":"위험","介面":"인터페이스","核心":"코어","服務":"서비스",
"管理":"관리","所有":"모든","您":"귀하","取消":"취소","未":"미",
"垃圾":"정크","位元":"바이트","本機":"PC","停止":"중지",
"到":"로","白名單":"화이트리스트","軟體":"소프트웨어","正在":"",
"增加":"추가","移除":"제거","項目":"항목","生成":"생성","稱":"",
"複製":"복사","此":"이","並":"및","全盤":"전체","最新":"최신",
"已存在":"이미 존재함","數據庫":"데이터베이스","誤報":"오탐지",
"高":"높음","新":"신규","和":"와","可能會":"할 수 있습니다","會":"합니다",
"日誌":"로그","已":"완료","彈窗":"팝업","要":"할","驅動":"드라이버",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"규칙",
"密鑰":"키","檢測":"탐지","製作":"제작","運行":"실행 중",
"擴展":"확장","套件":"키트","自動":"자동","上報":"보고",
"分析":"분석","自身":"자신","其他":"기타","配置":"구성",
"丟失":"분실","網站":"웹사이트","不兼容":"호환되지 않음","列表":"목록",
"竄改":"변조","第三方":"서드파티","實時":"실시간","查看":"보기",
"指定":"지정","清空":"비우기","資源回收桶":"휴지통","無法":"할 수 없음",
"與":"와","當":"할 때","功能":"기능","異常":"이상","來":"오다",
"重置":"초기화","窗口":"창","暫存":"임시","自訂":"사용자 지정",
"不支持":"지원하지 않음","共":"총","秒":"초","耗時":"소요 시간",
"沙箱":"샌드박스","虛擬":"가상","隔離":"격리","時":"",
"建立":"생성","環境":"환경","化":"","表":"",
"達":"도달","上限":"제한","區":"","金鑰":"키","제품":"제품",
"運行庫":".NET CLR","殼碼":"셸코드","線程":"스레드","注入":"인젝션",
"前往":"이동","檢查":"검사","可用":"사용 가능","靜態":"정적",
"版本":"버전","是":"","剩餘":"남은","遠控":"원격 제어"," ":" "},
"french_switch": {"開啟":"On ","關閉":"Off ","开启":"On ","关闭":"Off ",",":", ",
"立即刪除":"Supprimer ","安全模式":"Mode sans échec ","官方網站":"Site officiel ",
"腳本":"Script ","鏈式":"Chaîne ","記憶體":"Mémoire ","正在加載":"Chargement ","加載":"Charger ",
"行為":"Comportement ","可疑":"Suspect ","勒索":"Ransomware ","主要":"Principal ",
"許可條款":"Licence ","惡意":"Malveillant ","開發":"Développeurs ",
"啟用":"Activer ","禁用":"Désactiver ","正在初始化中":"Initialisation ","初始化":"Initialiser ",
"正在掃描":"Analyse en cours ","正在刪除":"Suppression ","正在追蹤":"Suivi en cours ",
"最小化到":"Réduire vers ","靈敏度":"Sensibilité ","正在檢查":"Vérification ",
"確定要":"Êtes-vous sûr ","引擎靈敏度":"Sensibilité ","更新":"Mettre à jour ",
"白色主題":"Blanc","黃色主題":"Jaune","紅色主題":"Rouge","語言":"Langue ",
"藍色主題":"Bleu","綠色主題":"Vert","自訂主題":"Personnalisé","黑色主題":"Noir",
"特別感謝":"Contributeurs ","版本資訊":"Information ","通訊":"Connexion ",
"註冊":"Registre ","退出":"Quitter ","檔案":"Fichier ","當前":"Actuellement ",
"狀態":"État ","掃描":"Analyser ","工具":"Outils ","防護":"Protection ",
"失敗":"Échec ","請":"Veuillez ","路徑":"Chemin ","方式":"Méthode ",
"需要":"Nécessaire ","病毒":"Virus ","選擇":"Sélectionner ","返回":"Retour ",
"受到":"Reçu ","錯誤":"Erreur ","完成":"Terminé ","引擎":"Moteur ",
"托盤":"Barre ","圖標":"Icône ","不安全":"Non sécurisé ","增強":"Améliorer ",
"系統":"Système ","雲端":"Cloud ","重啟":"Redémarrer ","修復":"Réparer ",
"裝置":"Appareil ","發現":"Trouvé ","成功":"Succès ","清理":"Nettoyer ",
"確定":"OK ","執行":"Exécuter ","警告":"Avertissement ","使用":"Utiliser ",
"網路":"Réseau ","變更":"Modifier ","總數":"Total ","結束":"Fin ",
"選項":"Options ","監控":"Surveiller ","連接":"Connecter ","可以":"Peut ",
"進程":"Processus ","攔截":"Bloquer ","引導":"Amorçage ","刪除":"Supprimer ",
"分區":"Partition ","設定":"Paramètres ","關於":"À propos ","模式":"Mode ",
"提高":"Améliorer ","安全":"Sécurité ","顯色":"Couleur ","顏色":"Couleur ",
"主題":"Thème ","回收":"Corbeille ","無用":"Inutile ","導入":"Importer ",
"風險":"Risque ","介面":"UI ","核心":"Cœur ","服務":"Services ",
"管理":"Gérer ","所有":"Tout ","您":"Vous ","取消":"Annuler ","未":"Non ",
"垃圾":"Déchet ","位元":"octets ","本機":"PC ","停止":"Arrêter ",
"到":"Vers ","白名單":"Liste blanche ","軟體":"Logiciel ","正在":"",
"增加":"Ajouter ","移除":"Retirer ","項目":"Éléments ","生成":"Créer ","稱":"",
"複製":"Copier ","此":"Ce ","並":"Et ","全盤":"Complet ","最新":"Dernier ",
"已存在":"Existe déjà ","數據庫":"Base de données ","誤報":"Faux positif ",
"高":"Haut ","新":"Nouveau ","和":"Et ","可能會":"Possible ","會":"Peut ",
"日誌":"Journal ","已":"Déjà ","彈窗":"Fenêtre ","要":"À ","驅動":"Pilote ",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Règle ",
"密鑰":"Clé ","檢測":"Détecter ","製作":"Créateur ","運行":"En cours ",
"擴展":"Extension ","套件":"Kit ","自動":"Auto ","上報":"Signaler ",
"分析":"Analyse ","自身":"Soi-même ","其他":"Autre ","配置":"Config ",
"丟失":"Perdu ","網站":"Site ","不兼容":"Incompatible ","列表":"Liste ",
"竄改":"Altérer ","第三方":"Tiers ","實時":"Temps réel ","查看":"Voir ",
"指定":"Spécifier ","清空":"Vider ","資源回收桶":"Corbeille ","無法":"Impossible ",
"與":"Avec ","當":"Quand ","功能":"Fonction ","異常":"Anormal ","來":"Pour ",
"重置":"Réinitialiser ","窗口":"Fenêtre ","暫存":"Temp ","自訂":"Personnalisé ",
"不支持":"Non supporté ","共":"Total ","秒":"Secondes ","耗時":"Prend ",
"沙箱":"Sandbox ","虛擬":"Virtuel ","隔離":"Quarantaine ","時":"",
"建立":"Créer ","環境":"Environnement ","化":"","表":"",
"達":"Atteint ","上限":"Limite ","區":"","金鑰":"Clé ","產品":"Produit ",
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Injecter ",
"前往":"Aller ","檢查":"Vérifier ","可用":"Disponible ","靜態":"Statique ",
"版本":"Version ","是":"","剩餘":"Restant ","遠控":"Distant "," ":" "},
"spanish_switch": {"開啟":"On ","關閉":"Off ","开启":"On ","关闭":"Off ",",":", ",
"立即刪除":"Eliminar ","安全模式":"Modo seguro ","官方網站":"Sitio web ",
"腳本":"Script ","鏈式":"Cadena ","記憶體":"Memoria ","正在加載":"Cargando ","加載":"Cargar ",
"行為":"Comportamiento ","可疑":"Sospechoso ","勒索":"Ransomware ","主要":"Principal ",
"許可條款":"Licencia ","惡意":"Malicioso ","開發":"Desarrolladores ",
"啟用":"Activar ","禁用":"Desactivar ","正在初始化中":"Inicializando ","初始化":"Inicializar ",
"正在掃描":"Escaneando ","正在刪除":"Eliminando ","正在追蹤":"Rastreando ",
"最小化到":"Minimizar a ","靈敏度":"Sensibilidad ","正在檢查":"Comprobando ",
"確定要":"Seguro que ","引擎靈敏度":"Sensibilidad ","更新":"Actualizar ",
"白色主題":"Blanco","黃色主題":"Amarillo","紅色主題":"Rojo","語言":"Idioma ",
"藍色主題":"Azul","綠色主題":"Verde","自訂主題":"Personalizado","黑色主題":"Negro",
"特別感謝":"Colaboradores ","版本資訊":"Información ","通訊":"Conectar ",
"註冊":"Registro ","退出":"Salir ","檔案":"Archivo ","當前":"Actualmente ",
"狀態":"Estado ","掃描":"Escanear ","工具":"Herramientas ","防護":"Protección ",
"失敗":"Fallido ","請":"Por favor ","路徑":"Ruta ","方式":"Método ",
"需要":"Necesita ","病毒":"Virus ","選擇":"Seleccionar ","返回":"Volver ",
"受到":"Recibido ","錯誤":"Error ","完成":"Completado ","引擎":"Motor ",
"托盤":"Bandeja ","圖標":"Icono ","不安全":"Inseguro ","增強":"Mejorar ",
"系統":"Sistema ","雲端":"Nube ","重啟":"Reiniciar ","修復":"Reparar ",
"裝置":"Dispositivo ","發現":"Encontrado ","成功":"Éxito ","清理":"Limpiar ",
"確定":"OK ","執行":"Ejecutar ","警告":"Advertencia ","使用":"Usar ",
"網路":"Red ","變更":"Cambiar ","總數":"Total ","結束":"Fin ",
"選項":"Opciones ","監控":"Monitor ","連接":"Conectar ","可以":"Puede ",
"進程":"Proceso ","攔截":"Bloquear ","引導":"Arranque ","刪除":"Eliminar ",
"分區":"Partición ","設定":"Ajustes ","關於":"Acerca de ","模式":"Modo ",
"提高":"Mejorar ","安全":"Seguridad ","顯色":"Color ","顏色":"Color ",
"主題":"Tema ","回收":"Papelera ","無用":"Inútil ","導入":"Importar ",
"風險":"Riesgo ","介面":"UI ","核心":"Núcleo ","服務":"Servicios ",
"管理":"Gestionar ","所有":"Todo ","您":"Tú ","取消":"Cancelar ","未":"No ",
"垃圾":"Basura ","位元":"bytes ","本機":"PC ","停止":"Detener ",
"到":"A ","白名單":"Lista blanca ","軟體":"Software ","正在":"",
"增加":"Añadir ","移除":"Eliminar ","項目":"Elementos ","生成":"Crear ","稱":"",
"複製":"Copiar ","此":"Este ","並":"Y ","全盤":"Completo ","最新":"Último ",
"已存在":"Ya existe ","數據庫":"Base de datos ","誤報":"Falso positivo ",
"高":"Alto ","新":"Nuevo ","和":"Y ","可能會":"Posible ","會":"Puede ",
"日誌":"Registro ","已":"Ya ","彈窗":"Ventana ","要":"A ","驅動":"Controlador ",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Regla ",
"密鑰":"Clave ","檢測":"Detectar ","製作":"Creador ","運行":"Ejecutando ",
"擴展":"Extensión ","套件":"Kit ","自動":"Auto ","上報":"Reportar ",
"分析":"Análisis ","自身":"Sí mismo ","其他":"Otro ","配置":"Config ",
"丟失":"Perdido ","網站":"Sitio ","不兼容":"Incompatible ","列表":"Lista ",
"竄改":"Manipular ","第三方":"Terceros ","實時":"Tiempo real ","查看":"Ver ",
"指定":"Especificar ","清空":"Vaciar ","資源回收桶":"Papelera ","無法":"No puede ",
"與":"Con ","當":"Cuando ","功能":"Función ","異常":"Anormal ","來":"Para ",
"重置":"Restablecer ","窗口":"Ventana ","暫存":"Temp ","自訂":"Personalizado ",
"不支持":"No soportado ","共":"Total ","秒":"Segundos ","耗時":"Tarda ",
"沙箱":"Sandbox ","虛擬":"Virtual ","隔離":"Cuarentena ","時":"",
"建立":"Crear ","環境":"Entorno ","化":"","表":"",
"達":"Alcanzado ","上限":"Límite ","區":"","金鑰":"Clave ","產品":"Producto ",
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Hilo ","注入":"Inyectar ",
"前往":"Ir ","檢查":"Comprobar ","可用":"Disponible ","靜態":"Estático ",
"版本":"Versión ","是":"","剩餘":"Restante ","遠控":"Remoto "," ":" "},
"hindi_switch": {"開啟":"चालू ", "關閉":"बंद ", "开启":"चालू ", "关闭":"बंद ", ",":", ",
"立即刪除":"अभी हटाएं ", "安全模式":"सुरक्षित मोड ", "官方網站":"आधिकारिक वेबसाइट ",
"腳本":"स्क्रिप्ट ", "鏈式":"चेन ", "記憶體":"मेमोरी ", "正在加載":"लोड हो रहा है ", "加載":"लोड करें ",
"行為":"व्यवहार ", "可疑":"संदिग्ध ", "勒索":"रैनसमवेयर ", "主要":"मुख्य ",
"許可條款":"लाइसेंस शर्तें ", "惡意":"दुर्भावनापूर्ण ", "開發":"डेवलपर्स ",
"啟用":"सक्षम करें ", "禁用":"अक्षम करें ", "正在初始化中":"प्रारंभ हो रहा है ", "初始化":"प्रारंभ करें ",
"正在掃描":"स्कैन हो रहा है ", "正在刪除":"हटाया जा रहा है ", "正在追蹤":"ट्रैक किया जा रहा है ",
"最小化到":"इसमें छोटा करें ", "靈敏度":"संवेदनशीलता ", "正在檢查":"जांच हो रही है ",
"確定要":"क्या आप सुनिश्चित हैं ", "引擎靈敏度":"इंजन संवेदनशीलता ", "更新":"अपडेट करें ",
"白色主題":"सफ़ेद", "黃色主題":"पीला", "紅色主題":"लाल", "語言":"भाषा ",
"藍色主題":"नीला", "綠色主題":"हरा", "自訂主題":"कस्टम", "黑色主題":"काला",
"特別感謝":"योगदानकर्ता ", "版本資訊":"संस्करण जानकारी ", "通訊":"संचार ",
"註冊":"रजिस्ट्री ", "退出":"बाहर निकलें ", "檔案":"फ़ाइल ", "當前":"वर्तमान ",
"狀態":"स्थिति ", "掃描":"स्कैन ", "工具":"उपकरण ", "防護":"सुरक्षा ",
"失敗":"विफल ", "請":"कृपया ", "路徑":"पथ ", "方式":"तरीका ",
"需要":"आवश्यक ", "病毒":"वायरस ", "選擇":"चुनें ", "返回":"वापस",
"受到":"प्राप्त हुआ ", "錯誤":"त्रुटि ", "完成":"पूरा हुआ ", "引擎":"इंजन ",
"托盤":"ट्रे ", "圖標":"आइकन ", "不安全":"असुरक्षित ", "增強":"बढ़ाना ",
"系統":"सिस्टम ", "雲端":"क्लाउड ", "重啟":"रीबूट ", "修復":"मरम्मत ",
"裝置":"डिवाइस ", "發現":"मिला ", "成功":"सफल ", "清理":"सफाई ",
"確定":"ठीक ", "執行":"निष्पादित करें ", "警告":"चेतावनी ", "使用":"उपयोग ",
"網路":"नेटवर्क ", "變更":"बदलें ", "總數":"कुल ", "結束":"समाप्त ",
"選項":"विकल्प ", "監控":"मॉनिटर ", "連接":"कनेक्ट ", "可以":"सकता है ",
"進程":"प्रक्रिया ", "攔截":"ब्लॉक ", "引導":"बूट ", "刪除":"हटाएं ",
"分區":"पार्टीशन ", "設定":"सेटिंग्स ", "關於":"के बारे में ", "模式":"मोड ",
"提高":"सुधारें ", "安全":"सुरक्षा ", "顯色":"रंग ", "顏色":"रंग ",
"主題":"थीम ", "回收":"रीसायकल ", "無用":"बेकार ", "導入":"आयात ",
"風險":"जोखिम ", "介面":"यूआई ", "核心":"कोर ", "服務":"सेवाएं ",
"管理":"प्रबंधित करें ", "所有":"सभी ", "您":"आप ", "取消":"रद्द करें ", "未":"नहीं ",
"垃圾":"कचरा ", "位元":"बाइट ", "本機":"पीसी ", "停止":"रोकें ",
"到":"तक ", "白名單":"श्वेत सूची ", "軟體":"सॉफ़्टवेयर ", "正在":"",
"增加":"जोड़ें ", "移除":"हटाएं ", "項目":"आइटम ", "生成":"बनाएं ", "稱":"",
"複製":"कॉपी ", "此":"यह ", "並":"और ", "全盤":"पूर्ण ", "最新":"नवीनतम ",
"已存在":"पहले से मौजूद ", "數據庫":"डेटाबेस ", "誤報":"गलत सूचना ",
"高":"उच्च ", "新":"नया ", "和":"और ", "可能會":"संभव ", "會":"सकता है ",
"日誌":"लॉग ", "已":"हो चुका ", "彈窗":"विंडो ", "要":"को ", "驅動":"ड्राइवर ",
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"नियम ",
"密鑰":"कुंजी ", "檢測":"पता लगाएं ", "製作":"निर्माता ", "運行":"चल रहा है ",
"擴展":"एक्सटेंशन ", "套件":"किट ", "自動":"ऑटो ", "上報":"रिपोर्ट ",
"分析":"विश्लेषण ", "自身":"स्वयं ", "其他":"अन्य ", "配置":"कॉन्फ़िगर ",
"丟失":"खो गया ", "網站":"वेबसाइट ", "不兼容":"असंगत ", "列表":"सूची ",
"竄改":"छेड़छाड़ ", "第三方":"थर्ड पार्टी ", "實時":"वास्तविक समय ", "查看":"देखें ",
"指定":"निर्दिष्ट करें ", "清空":"खाली करें ", "資源回收桶":"रीसायकल बिन ", "無法":"असमर्थ ",
"與":"के साथ ", "當":"जब ", "功能":"कार्य ", "異常":"असामान्य ", "來":"के लिए ",
"重置":"रीसेट ", "窗口":"विंडो ", "暫存":"अस्थायी ", "自訂":"कस्टम ",
"不支持":"समर्थित नहीं ", "共":"कुल ", "秒":"सेकंड ", "耗時":"लगा समय ",
"沙箱":"सैंडबॉक्स ", "虛擬":"आभासी ", "隔離":"संगरोध ", "時":"",
"建立":"बनाएं ", "環境":"वातावरण ", "化":"", "表":"",
"達":"पहुंच गया ", "上限":"सीमा ", "區":"", "金鑰":"कुंजी ", "產品":"उत्पाद ",
"運行庫":".NET CLR ", "殼碼":"शेलकोड ", "線程":"थ्रेड ", "注入":"इंजेक्टर ",
"前往":"जाएं ", "檢查":"जांचें ", "可用":"उपलब्ध ", "靜態":"स्थैतिक ",
"版本":"संस्करण ", "是":"", "剩餘":"शेष ", "遠控":"रिमोट ", " ":" "},
"arabic_switch": {"開啟":"تشغيل ", "關閉":"إيقاف ", "开启":"تشغيل ", "关闭":"إيقاف ", ",":", ",
"立即刪除":"حذف الآن ", "安全模式":"الوضع الآمن ", "官方網站":"الموقع الرسمي ",
"腳本":"برنامج نصي ", "鏈式":"سلسلة ", "記憶體":"الذاكرة ", "正在加載":"جار التحميل ", "加載":"تحميل ",
"行為":"سلوك ", "可疑":"مشبوه ", "勒索":"فدية ", "主要":"رئيسي ",
"許可條款":"شروط الترخيص ", "惡意":"ضار ", "開發":"المطورين ",
"啟用":"تفعيل ", "禁用":"تعطيل ", "正在初始化中":"جار التهيئة ", "初始化":"تهيئة ",
"正在掃描":"جار الفحص ", "正在刪除":"جار الحذف ", "正在追蹤":"جار التتبع ",
"最小化到":"تصغير إلى ", "靈敏度":"حساسية ", "正在檢查":"جار التحقق ",
"確定要":"هل أنت متأكد ", "引擎靈敏度":"حساسية المحرك ", "更新":"تحديث ",
"白色主題":"أبيض", "黃色主題":"أصفر", "紅色主題":"أحمر", "語言":"اللغة ",
"藍色主題":"أزرق", "綠色主題":"أخضر", "自訂主題":"مخصص", "黑色主題":"أسود",
"特別感謝":"المساهمون ", "版本資訊":"معلومات الإصدار ", "通訊":"اتصال ",
"註冊":"السجل ", "退出":"خروج ", "檔案":"ملف ", "當前":"الحالي ",
"狀態":"الحالة ", "掃描":"فحص ", "工具":"أدوات ", "防護":"حماية ",
"失敗":"فشل ", "請":"يرجى ", "路徑":"مسار ", "方式":"طريقة ",
"需要":"يحتاج ", "病毒":"فيروس ", "選擇":"تحديد ", "返回":"رجوع",
"受到":"تم الاستلام ", "錯誤":"خطأ ", "完成":"مكتمل ", "引擎":"محرك ",
"托盤":"صينية ", "圖標":"أيقونة ", "不安全":"غير آمن ", "增強":"تحسين ",
"系統":"النظام ", "雲端":"سحابة ", "重啟":"إعادة تشغيل ", "修復":"إصلاح ",
"裝置":"جهاز ", "發現":"تم العثور ", "成功":"نجاح ", "清理":"تنظيف ",
"確定":"موافق ", "執行":"تنفيذ ", "警告":"تحذير ", "使用":"استخدام ",
"網路":"الشبكة ", "變更":"تغيير ", "總數":"الإجمالي ", "結束":"إنهاء ",
"選項":"خيارات ", "監控":"مراقبة ", "連接":"اتصال ", "可以":"يمكن ",
"進程":"عملية ", "攔截":"حظر ", "引導":"إقلاع ", "刪除":"حذف ",
"分區":"قسم ", "設定":"إعدادات ", "關於":"حول ", "模式":"وضع ",
"提高":"تحسين ", "安全":"أمان ", "顯色":"لون ", "顏色":"لون ",
"主題":"سمة ", "回收":"استرجاع ", "無用":"غير مفيد ", "導入":"استيراد ",
"風險":"خطر ", "介面":"واجهة ", "核心":"نواة ", "服務":"خدمات ",
"管理":"إدارة ", "所有":"الكل ", "您":"أنت ", "取消":"إلغاء ", "未":"غير ",
"垃圾":"مهملات ", "位元":"بايت ", "本機":"الكمبيوتر ", "停止":"إيقاف ",
"到":"إلى ", "白名單":"القائمة البيضاء ", "軟體":"برنامج ", "正在":"",
"增加":"إضافة ", "移除":"إزالة ", "項目":"عناصر ", "生成":"إنشاء ", "稱":"",
"複製":"نسخ ", "此":"هذا ", "並":"و ", "全盤":"كامل ", "最新":"الأحدث ",
"已存在":"موجود بالفعل ", "數據庫":"قاعدة البيانات ", "誤報":"إيجابي كاذب ",
"高":"عالي ", "新":"جديد ", "和":"و ", "可能會":"محتمل ", "會":"سوف ",
"日誌":"سجل ", "已":"بالفعل ", "彈窗":"نافذة ", "要":"إلى ", "驅動":"برنامج تشغيل ",
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"قاعدة ",
"密鑰":"مفتاح ", "檢測":"اكتشاف ", "製作":"صانع ", "運行":"قيد التشغيل ",
"擴展":"امتداد ", "套件":"مجموعة ", "自動":"تلقائي ", "上報":"إبلاغ ",
"分析":"تحليل ", "自身":"نفسه ", "其他":"آخر ", "配置":"تكوين ",
"丟失":"مفقود ", "網站":"موقع ", "不兼容":"غير متوافق ", "列表":"قائمة ",
"竄改":"تلاعب ", "第三方":"جهة خارجية ", "實時":"الوقت الفعلي ", "查看":"عرض ",
"指定":"تحديد ", "清空":"إفراغ ", "資源回收桶":"سلة المهملات ", "無法":"غير قادر ",
"與":"مع ", "當":"عندما ", "功能":"وظيفة ", "異常":"شاذ ", "來":"ل ",
"重置":"إعادة ضبط ", "窗口":"نافذة ", "暫存":"مؤقت ", "自訂":"مخصص ",
"不支持":"غير مدعوم ", "共":"إجمالي ", "秒":"ثواني ", "耗時":"استغرق ",
"沙箱":"صندوق الحماية ", "虛擬":"افتراضي ", "隔離":"عزل ", "時":"",
"建立":"إنشاء ", "環境":"بيئة ", "化":"", "表":"",
"達":"وصل ", "上限":"الحد ", "區":"", "金鑰":"مفتاح ", "產品":"منتج ",
"運行庫":".NET CLR ", "殼碼":"رمز قشرة ", "線程":"سلسلة ", "注入":"حقن ",
"前往":"الذهاب ", "檢查":"فحص ", "可用":"متاح ", "靜態":"ثابت ",
"版本":"إصدار ", "是":"", "剩餘":"المتبقي ", "遠控":"عن بعد ", " ":" "},
"russian_switch": {"開啟":"Вкл ", "關閉":"Выкл ", "开启":"Вкл ", "关闭":"Выкл ", ",":", ",
"立即刪除":"Удалить сейчас ", "安全模式":"Безопасный режим ", "官方網站":"Официальный сайт ",
"腳本":"Скрипт ", "鏈式":"Цепочка ", "記憶體":"Память ", "正在加載":"Загрузка ", "加載":"Загрузить ",
"行為":"Поведение ", "可疑":"Подозрительный ", "勒索":"Вымогатель ", "主要":"Главный ",
"許可條款":"Условия лицензии ", "惡意":"Вредоносный ", "開發":"Разработчики ",
"啟用":"Включить ", "禁用":"Отключить ", "正在初始化中":"Инициализация ", "初始化":"Инициализировать ",
"正在掃描":"Сканирование ", "正在刪除":"Удаление ", "正在追蹤":"Отслеживание ",
"最小化到":"Свернуть в ", "靈敏度":"Чувствительность ", "正在檢查":"Проверка ",
"確定要":"Вы уверены, что ", "引擎靈敏度":"Чувствительность движка ", "更新":"Обновить ",
"白色主題":"Белый", "黃色主題":"Желтый", "紅色主題":"Красный", "語言":"Язык ",
"藍色主題":"Синий", "綠色主題":"Зеленый", "自訂主題":"Пользовательский", "黑色主題":"Черный",
"特別感謝":"Участники ", "版本資訊":"Информация о версии ", "通訊":"Связь ",
"註冊":"Реестр ", "退出":"Выход ", "檔案":"Файл ", "當前":"Текущий ",
"狀態":"Статус ", "掃描":"Сканировать ", "工具":"Инструменты ", "防護":"Защита ",
"失敗":"Ошибка ", "請":"Пожалуйста ", "路徑":"Путь ", "方式":"Метод ",
"需要":"Требуется ", "病毒":"Вирус ", "選擇":"Выбрать ", "返回":"Назад",
"受到":"Получено ", "錯誤":"Ошибка ", "完成":"Завершено ", "引擎":"Движок ",
"托盤":"Трей ", "圖標":"Значок ", "不安全":"Небезопасно ", "增強":"Улучшить ",
"系統":"Система ", "雲端":"Облако ", "重啟":"Перезагрузка ", "修復":"Восстановить ",
"裝置":"Устройство ", "發現":"Найдено ", "成功":"Успех ", "清理":"Очистка ",
"確定":"ОК ", "執行":"Выполнить ", "警告":"Предупреждение ", "使用":"Использовать ",
"網路":"Сеть ", "變更":"Изменить ", "總數":"Всего ", "結束":"Завершить ",
"選項":"Опции ", "監控":"Монитор ", "連接":"Подключить ", "可以":"Можно ",
"進程":"Процесс ", "攔截":"Блокировать ", "引導":"Загрузка ", "刪除":"Удалить ",
"分區":"Раздел ", "設定":"Настройки ", "關於":"О программе ", "模式":"Режим ",
"提高":"Улучшить ", "安全":"Безопасность ", "顯色":"Цвет ", "顏色":"Цвет ",
"主題":"Тема ", "回收":"Корзина ", "無用":"Бесполезно ", "導入":"Импорт ",
"風險":"Риск ", "介面":"Интерфейс ", "核心":"Ядро ", "服務":"Службы ",
"管理":"Управление ", "所有":"Все ", "您":"Вы ", "取消":"Отмена ", "未":"Не ",
"垃圾":"Мусор ", "位元":"байт ", "本機":"ПК ", "停止":"Остановить ",
"到":"К ", "白名單":"Белый список ", "軟體":"ПО ", "正在":"",
"增加":"Добавить ", "移除":"Удалить ", "項目":"Элементы ", "生成":"Создать ", "稱":"",
"複製":"Копировать ", "此":"Этот ", "並":"И ", "全盤":"Полный ", "最新":"Последний ",
"已存在":"Уже существует ", "數據庫":"База данных ", "誤報":"Ложное срабатывание ",
"高":"Высокий ", "新":"Новый ", "和":"И ", "可能會":"Возможно ", "會":"Может ",
"日誌":"Журнал ", "已":"Уже ", "彈窗":"Окно ", "要":"Нужно ", "驅動":"Драйвер ",
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"Правило ",
"密鑰":"Ключ ", "檢測":"Обнаружить ", "製作":"Создатель ", "運行":"Работает ",
"擴展":"Расширение ", "套件":"Набор ", "自動":"Авто ", "上報":"Отчет ",
"分析":"Анализ ", "自身":"Сам ", "其他":"Другое ", "配置":"Конфиг ",
"丟失":"Утерян ", "網站":"Сайт ", "不兼容":"Несовместимо ", "列表":"Список ",
"竄改":"Вмешательство ", "第三方":"Сторонний ", "實時":"Реальное время ", "查看":"Просмотр ",
"指定":"Указать ", "清空":"Очистить ", "資源回收桶":"Корзина ", "無法":"Невозможно ",
"與":"С ", "當":"Когда ", "功能":"Функция ", "異常":"Аномалия ", "來":"Для ",
"重置":"Сброс ", "窗口":"Окно ", "暫存":"Временный ", "自訂":"Пользовательский ",
"不支持":"Не поддерживается ", "共":"Всего ", "秒":"Секунд ", "耗時":"Заняло ",
"沙箱":"Песочница ", "虛擬":"Виртуальный ", "隔離":"Карантин ", "時":"",
"建立":"Создать ", "環境":"Среда ", "化":"", "表":"",
"達":"Достигнут ", "上限":"Лимит ", "區":"", "金鑰":"Ключ ", "產品":"Продукт ",
"運行庫":".NET CLR ", "殼碼":"Шелл-код ", "線程":"Поток ", "注入":"Инъекция ",
"前往":"Перейти ", "檢查":"Проверить ", "可用":"Доступно ", "靜態":"Статический ",
"版本":"Версия ", "是":"", "剩餘":"Осталось ", "遠控":"Удаленный ", " ":" "},
"slovenian_switch": {"開啟":"Vklop ", "關閉":"Izklop ", "开启":"Vklop ", "关闭":"Izklop ", ",":", ",
"立即刪除":"Izbriši zdaj ", "安全模式":"Varni način ", "官方網站":"Uradna spletna stran ",
"腳本":"Skripta ", "鏈式":"Veriga ", "記憶體":"Pomnilnik ", "正在加載":"Nalaganje ", "加載":"Naloži ",
"行為":"Vedenje ", "可疑":"Sumljivo ", "勒索":"Izsiljevalska ", "主要":"Glavni ",
"許可條款":"Licenčni pogoji ", "惡意":"Zlonamerno ", "開發":"Razvijalci ",
"啟用":"Omogoči ", "禁用":"Onemogoči ", "正在初始化中":"Inicializacija ", "初始化":"Inicializiraj ",
"正在掃描":"Skeniranje ", "正在刪除":"Brisanje ", "正在追蹤":"Sledenje ",
"最小化到":"Minimiziraj v ", "靈敏度":"Občutljivost ", "正在檢查":"Preverjanje ",
"確定要":"Ali ste prepričani ", "引擎靈敏度":"Občutljivost mehanizma ", "更新":"Posodobi ",
"白色主題":"Bela", "黃色主題":"Rumena", "紅色主題":"Rdeča", "語言":"Jezik ",
"藍色主題":"Modra", "綠色主題":"Zelena", "自訂主題":"Po meri", "黑色主題":"Črna",
"特別感謝":"Sodelavci ", "版本資訊":"Informacije o različici ", "通訊":"Povezava ",
"註冊":"Register ", "退出":"Izhod ", "檔案":"Datoteka ", "當前":"Trenutno ",
"狀態":"Stanje ", "掃描":"Skeniraj ", "工具":"Orodja ", "防護":"Zaščita ",
"失敗":"Neuspešno ", "請":"Prosim ", "路徑":"Pot ", "方式":"Način ",
"需要":"Potrebno ", "病毒":"Virus ", "選擇":"Izberi ", "返回":"Nazaj",
"受到":"Prejeto ", "錯誤":"Napaka ", "完成":"Končano ", "引擎":"Mehanizem ",
"托盤":"Pladenj ", "圖標":"Ikona ", "不安全":"Nevarno ", "增強":"Izboljšaj ",
"系統":"Sistem ", "雲端":"Oblak ", "重啟":"Ponovni zagon ", "修復":"Popravi ",
"裝置":"Naprava ", "發現":"Najdeno ", "成功":"Uspeh ", "清理":"Čiščenje ",
"確定":"V redu ", "執行":"Izvedi ", "警告":"Opozorilo ", "使用":"Uporabi ",
"網路":"Omrežje ", "變更":"Spremeni ", "總數":"Skupaj ", "結束":"Konec ",
"選項":"Možnosti ", "監控":"Nadzor ", "連接":"Poveži ", "可以":"Lahko ",
"進程":"Proces ", "攔截":"Blokiraj ", "引導":"Zagon ", "刪除":"Izbriši ",
"分區":"Particija ", "設定":"Nastavitve ", "關於":"O programu ", "模式":"Način ",
"提高":"Povečaj ", "安全":"Varnost ", "顯色":"Barva ", "顏色":"Barva ",
"主題":"Tema ", "回收":"Koš ", "無用":"Nekoristno ", "導入":"Uvozi ",
"風險":"Tveganje ", "介面":"Vmesnik ", "核心":"Jedro ", "服務":"Storitve ",
"管理":"Upravljaj ", "所有":"Vse ", "您":"Vi ", "取消":"Prekliči ", "未":"Ne ",
"垃圾":"Smeti ", "位元":"bajtov ", "本機":"PC ", "停止":"Ustavi ",
"到":"V ", "白名單":"Beli seznam ", "軟體":"Programska oprema ", "正在":"",
"增加":"Dodaj ", "移除":"Odstrani ", "項目":"Predmeti ", "生成":"Ustvari ", "稱":"",
"複製":"Kopiraj ", "此":"To ", "並":"In ", "全盤":"Popolno ", "最新":"Najnovejše ",
"已存在":"Že obstaja ", "數據庫":"Podatkovna baza ", "誤報":"Lažni alarm ",
"高":"Visoko ", "新":"Novo ", "和":"In ", "可能會":"Možno ", "會":"Bo ",
"日誌":"Dnevnik ", "已":"Že ", "彈窗":"Okno ", "要":"V ", "驅動":"Gonilnik ",
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"Pravilo ",
"密鑰":"Ključ ", "檢測":"Zaznaj ", "製作":"Ustvarjalec ", "運行":"Se izvaja ",
"擴展":"Razširitev ", "套件":"Komplet ", "自動":"Samodejno ", "上報":"Prijavi ",
"分析":"Analiza ", "自身":"Samo ", "其他":"Drugo ", "配置":"Konfiguracija ",
"丟失":"Izgubljeno ", "網站":"Spletno mesto ", "不兼容":"Nezdružljivo ", "列表":"Seznam ",
"竄改":"Spreminjanje ", "第三方":"Tretja oseba ", "實時":"V realnem času ", "查看":"Poglej ",
"指定":"Določi ", "清空":"Izprazni ", "資源回收桶":"Koš ", "無法":"Ni mogoče ",
"與":"Z ", "當":"Ko ", "功能":"Funkcija ", "異常":"Nenormalno ", "來":"Za ",
"重置":"Ponastavi ", "窗口":"Okno ", "暫存":"Začasno ", "自訂":"Po meri ",
"不支持":"Ni podprto ", "共":"Skupaj ", "秒":"Sekund ", "耗時":"Trajalo ",
"沙箱":"Peskovnik ", "虛擬":"Virtualno ", "隔離":"Karantena ", "時":"",
"建立":"Ustvari ", "環境":"Okolje ", "化":"", "表":"",
"達":"Doseženo ", "上限":"Omejitev ", "區":"", "金鑰":"Ključ ", "產品":"Izdelek ",
"運行庫":".NET CLR ", "殼碼":"Zlonamerna koda ", "線程":"Nit ", "注入":"Vbrizgavanje ",
"前往":"Pojdi ", "檢查":"Preveri ", "可用":"Na voljo ", "靜態":"Statično ",
"版本":"Različica ", "是":"", "剩餘":"Preostalo ", "遠控":"Oddaljeno ", " ":" "},
"custom_language_switch": {},
"white_switch": {
"_white": "_black",},
"red_switch": {
"235, 235, 235": "255, 235, 235",
"245, 245, 245": "255, 225, 225",
"255, 255, 255": "255, 245, 245",
"240, 240, 240": "255, 220, 220",
"250, 250, 250": "255, 210, 210",
"220, 255, 220": "255, 220, 220",
"210, 255, 210": "255, 210, 210",
"_white": "_black"
},
"yellow_switch": {
"235, 235, 235": "255, 255, 235",
"245, 245, 245": "255, 255, 225",
"255, 255, 255": "255, 255, 245",
"240, 240, 240": "255, 255, 220",
"250, 250, 250": "255, 255, 210",
"220, 255, 220": "255, 255, 220",
"210, 255, 210": "255, 255, 210",
"_white": "_black"
},
"green_switch": {
"235, 235, 235": "235, 255, 235",
"245, 245, 245": "225, 255, 225",
"255, 255, 255": "245, 255, 245",
"240, 240, 240": "220, 255, 220",
"250, 250, 250": "210, 255, 210",
"_white": "_black"
},
"blue_switch": {
"235, 235, 235": "235, 235, 255",
"245, 245, 245": "225, 225, 255",
"255, 255, 255": "245, 245, 255",
"240, 240, 240": "220, 220, 255",
"250, 250, 250": "210, 210, 255",
"220, 255, 220": "220, 220, 255",
"210, 255, 210": "210, 210, 255",
"_white": "_black"
},
"black_switch": {
"255, 255, 255": "20, 20, 20",
"245, 245, 245": "40, 40, 40",
"235, 235, 235": "30, 30, 30",
"240, 240, 240": "70, 70, 70",
"250, 250, 250": "80, 80, 80",
"220, 255, 220": "70, 70, 70",
"210, 255, 210": "80, 80, 80",
"0, 0, 0": "255, 255, 255",
"_black": "_white"
}
}
+588 -589
View File
File diff suppressed because it is too large. Load diff
+253
View File
@@ -0,0 +1,253 @@
#define AppId "{{a7d7bac3-93b8-4630-8308-c7a56bf7fdf4}"
#define AppName "PYAS"
#define AppVersion "3.6.0.0"
#define AppPublisher "PYAS Security"
#define AppURL "https://github.com/87owo/PYAS"
#define AppExeName "PYAS.exe"
[Setup]
AppId={#AppId}
AppName={#AppName}
AppVersion={#AppVersion}
AppVerName={#AppName} {#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
VersionInfoVersion={#AppVersion}
VersionInfoCompany={#AppPublisher}
VersionInfoDescription={#AppName} Setup
VersionInfoProductName={#AppName} Setup
VersionInfoProductVersion={#AppVersion}
DefaultDirName={autopf}\{#AppName}
DefaultGroupName={#AppPublisher}\{#AppName}
AllowNoIcons=yes
LicenseFile=SetupResources\licence.rtf
ShowLanguageDialog=yes
WizardStyle=modern
WizardImageFile=SetupResources\wizardImage.bmp
WizardSmallImageFile=SetupResources\headerImage.png
SetupIconFile=Payload\Interface\static\img\icon.ico
UninstallDisplayIcon={app}\{#AppExeName}
PrivilegesRequired=admin
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
MinVersion=10.0
Compression=lzma2/max
SolidCompression=yes
OutputDir=Output
OutputBaseFilename=PYAS_Setup
UsePreviousTasks=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "chinesesimplified"; MessagesFile: "SetupResources\ChineseSimplified.isl"
Name: "chinesetraditional"; MessagesFile: "SetupResources\ChineseTraditional.isl"
Name: "japanese"; MessagesFile: "SetupResources\Japanese.isl"
Name: "korean"; MessagesFile: "SetupResources\Korean.isl"
Name: "french"; MessagesFile: "SetupResources\French.isl"
Name: "spanish"; MessagesFile: "SetupResources\Spanish.isl"
Name: "hindi"; MessagesFile: "SetupResources\Hindi.isl"
Name: "arabic"; MessagesFile: "SetupResources\Arabic.isl"
Name: "russian"; MessagesFile: "SetupResources\Russian.isl"
Name: "slovenian"; MessagesFile: "SetupResources\Slovenian.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
Name: "autostart"; Description: "{cm:AutoStartTask}"; GroupDescription: "{cm:AdditionalIcons}"
Name: "install_webview2"; Description: "{cm:InstallWebView2}"; GroupDescription: "{cm:Dependencies}"
Name: "install_vcredist"; Description: "{cm:InstallVCRedist}"; GroupDescription: "{cm:Dependencies}"
[Files]
Source: "Payload\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "Redist\VC_redist.x64.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion
Source: "Redist\MicrosoftEdgeWebview2Setup.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion
[Registry]
Root: HKCU; Subkey: "Software\Classes\*\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
Root: HKCU; Subkey: "Software\Classes\Directory\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
[UninstallDelete]
Type: filesandordirs; Name: "{commonappdata}\PYAS"
[Icons]
Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
Name: "{group}\Uninstall {#AppName}"; Filename: "{uninstallexe}"
[Run]
Filename: "{tmp}\PYAS_Redist\VC_redist.x64.exe"; Parameters: "/quiet /norestart"; Flags: waituntilterminated runhidden; StatusMsg: "{cm:InstallingVCRuntime}"; Tasks: install_vcredist
Filename: "{tmp}\PYAS_Redist\MicrosoftEdgeWebview2Setup.exe"; Parameters: "/silent /install"; Flags: waituntilterminated runhidden; StatusMsg: "{cm:InstallingWebView2Runtime}"; Tasks: install_webview2
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser
[CustomMessages]
english.InstallingVCRuntime=Installing Microsoft Visual C++ Runtime...
chinesesimplified.InstallingVCRuntime=正在安装 Microsoft Visual C++ 运行库...
chinesetraditional.InstallingVCRuntime=正在安裝 Microsoft Visual C++ 執行庫...
english.InstallingWebView2Runtime=Installing Microsoft Edge WebView2 Runtime...
chinesesimplified.InstallingWebView2Runtime=正在安装 Microsoft Edge WebView2 Runtime...
chinesetraditional.InstallingWebView2Runtime=正在安裝 Microsoft Edge WebView2 Runtime...
english.AutoStartTask=Run PYAS automatically at system startup
chinesesimplified.AutoStartTask=开机时自动运行 PYAS
chinesetraditional.AutoStartTask=開機時自動執行 PYAS
english.Dependencies=Dependencies:
chinesesimplified.Dependencies=运行环境:
chinesetraditional.Dependencies=執行環境:
english.InstallWebView2=Install Microsoft Edge WebView2 Runtime
chinesesimplified.InstallWebView2=安装 Microsoft Edge WebView2 运行环境
chinesetraditional.InstallWebView2=安裝 Microsoft Edge WebView2 執行環境
english.InstallVCRedist=Install Microsoft Visual C++ Runtime
chinesesimplified.InstallVCRedist=安装 Microsoft Visual C++ 运行库
chinesetraditional.InstallVCRedist=安裝 Microsoft Visual C++ 執行庫
english.LegacyVersionDetected=An older version of PYAS was detected. Please uninstall it manually before installing.
chinesesimplified.LegacyVersionDetected=检测到存在旧版 PYAS。请先手动卸载旧版后,再运行本安装程序。
chinesetraditional.LegacyVersionDetected=檢測到存在舊版 PYAS。請先手動卸載舊版後,再執行本安裝程式。
[Code]
var
TasksInitialized: Boolean;
function IsWebView2Installed: Boolean;
var
RegKeyPath: string;
Version: string;
begin
RegKeyPath := 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
Result := False;
if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv', Version) then
if Length(Version) > 0 then Result := True;
if not Result and RegQueryStringValue(HKCU, RegKeyPath, 'pv', Version) then
if Length(Version) > 0 then Result := True;
if not Result and RegQueryStringValue(HKLM, RegKeyPath, 'pv', Version) then
if Length(Version) > 0 then Result := True;
end;
function IsVCRedistInstalled: Boolean;
var
Bld: Cardinal;
begin
Result := False;
if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Bld', Bld) then
if Bld > 0 then Result := True;
end;
procedure CurPageChanged(CurPageID: Integer);
var
I: Integer;
ItemText: string;
begin
if (CurPageID = wpSelectTasks) and not TasksInitialized then
begin
TasksInitialized := True;
for I := 0 to WizardForm.TasksList.Items.Count - 1 do
begin
ItemText := WizardForm.TasksList.Items[I];
if Pos('Visual C++', ItemText) > 0 then
WizardForm.TasksList.Checked[I] := not IsVCRedistInstalled
else if Pos('WebView2', ItemText) > 0 then
WizardForm.TasksList.Checked[I] := not IsWebView2Installed;
end;
end;
end;
procedure QuitOldInstance(InstallPath: string);
var
ResultCode: Integer;
ExePath: string;
begin
ExePath := InstallPath + '\{#AppExeName}';
if FileExists(ExePath) then
begin
Exec(ExePath, '-quit', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
Sleep(500);
Exec(ExpandConstant('{sys}\taskkill.exe'), '/F /IM {#AppExeName} /T', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
end;
function InitializeSetup(): Boolean;
var
OldInstallPath: string;
LegacyPath: string;
begin
LegacyPath := ExpandConstant('{pf32}\{#AppName}');
if DirExists(LegacyPath) and FileExists(LegacyPath + '\{#AppExeName}') then
begin
MsgBox(CustomMessage('LegacyVersionDetected'), mbCriticalError, MB_OK);
Result := False;
Exit;
end;
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) or
RegQueryStringValue(HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) then
begin
QuitOldInstance(OldInstallPath);
end;
Result := True;
end;
function InitializeUninstall(): Boolean;
begin
QuitOldInstance(ExpandConstant('{app}'));
Result := True;
end;
procedure TryCreateStartupTask;
var
ResultCode: Integer;
PSExe: string;
PSCommand: string;
AppPath: string;
begin
AppPath := ExpandConstant('{app}\{#AppExeName}');
PSExe := ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe');
PSCommand := '-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -Command ' +
'$Action = New-ScheduledTaskAction -Execute ''' + AppPath + ''' -Argument ''-hide''; ' +
'$Trigger = New-ScheduledTaskTrigger -AtLogOn; ' +
'$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; ' +
'Register-ScheduledTask -TaskName ''PYAS_Security_ATS'' -Action $Action -Trigger $Trigger -Settings $Settings -RunLevel Highest -Force';
if not Exec(PSExe, PSCommand, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
Log(Format('Failed to create scheduled task, Exec error code: %d', [ResultCode]));
end
else if ResultCode <> 0 then
begin
Log(Format('powershell returned exit code %d while creating the scheduled task', [ResultCode]));
end;
end;
procedure TryDeleteStartupTask;
var
ResultCode: Integer;
begin
if not Exec(ExpandConstant('{sys}\schtasks.exe'), '/Delete /TN "PYAS_Security_ATS" /F', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
Log(Format('Failed to delete scheduled task, Exec error code: %d', [ResultCode]));
end
else if ResultCode <> 0 then
begin
Log(Format('schtasks returned exit code %d while deleting the scheduled task', [ResultCode]));
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
if WizardIsTaskSelected('autostart') then
begin
TryCreateStartupTask;
end;
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
TryDeleteStartupTask;
end
else if CurUninstallStep = usPostUninstall then
begin
DelTree(ExpandConstant('{app}'), True, True, True);
end;
end;
-1579
View File
File diff suppressed because it is too large. Load diff
-14520
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -2,9 +2,9 @@ import pyinstaller_versionfile
pyinstaller_versionfile.create_versionfile(
output_file="versionfile.txt",
version='3.4.6',
version='3.6.0.0',
company_name="PYAS Security",
file_description="Python Antivirus Software",
file_description="PYAS Security Antivirus",
internal_name="PYAS",
legal_copyright="PYAS Security",
original_filename="PYAS.exe",
+10 -2
View File
@@ -101,6 +101,11 @@ typedef struct _PYAS_MESSAGE {
WCHAR Path[MAX_PATH_LEN];
} PYAS_MESSAGE, * PPYAS_MESSAGE;
typedef struct _PYAS_USER_MESSAGE {
ULONG Command;
WCHAR Path[MAX_PATH_LEN];
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
typedef struct _DRIVER_DATA {
PDRIVER_OBJECT DriverObject;
PFLT_FILTER FilterHandle;
@@ -109,8 +114,8 @@ typedef struct _DRIVER_DATA {
PEPROCESS PyasProcess;
ULONG PyasPid;
LARGE_INTEGER Cookie;
FAST_MUTEX PortMutex;
FAST_MUTEX TrackerMutex;
KSPIN_LOCK PortMutex;
KSPIN_LOCK TrackerMutex;
EX_RUNDOWN_REF PortRundown;
} DRIVER_DATA, * PDRIVER_DATA;
@@ -139,6 +144,9 @@ VOID UnloadRules();
VOID InitializeRulesEngine();
VOID UninitializeRulesEngine();
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
+61 -19
View File
@@ -1,6 +1,7 @@
#include "DriverCommon.h"
DRIVER_DATA GlobalData;
static BOOLEAN g_ImageNotifyRegistered = FALSE;
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
UNREFERENCED_PARAMETER(FltObjects);
@@ -27,9 +28,7 @@ static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTA
}
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
if (!(Flags & FLTFL_FILTER_UNLOAD_MANDATORY)) {
return STATUS_FLT_DO_NOT_DETACH;
}
UNREFERENCED_PARAMETER(Flags);
if (GlobalData.ServerPort) {
FltCloseCommunicationPort(GlobalData.ServerPort);
@@ -41,25 +40,56 @@ static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
GlobalData.FilterHandle = NULL;
}
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
if (g_ImageNotifyRegistered) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyRegistered = FALSE;
}
UninitializeRegistryProtection();
UninitializeProcessProtection();
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
UnloadRules();
UninitializeRulesEngine();
return STATUS_SUCCESS;
}
static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnOutputBufferLength) {
UNREFERENCED_PARAMETER(PortCookie);
UNREFERENCED_PARAMETER(OutputBuffer);
UNREFERENCED_PARAMETER(OutputBufferLength);
if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) {
PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer;
if (msg->Command == 1 || msg->Command == 2) {
msg->Path[MAX_PATH_LEN - 1] = L'\0';
UNICODE_STRING us;
RtlInitUnicodeString(&us, msg->Path);
if (us.Length > 0) {
if (msg->Command == 1) {
AddDynamicWhitelist(&us);
}
else {
RemoveDynamicWhitelist(&us);
}
}
}
}
if (ReturnOutputBufferLength) {
*ReturnOutputBufferLength = 0;
}
return STATUS_SUCCESS;
}
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
UNREFERENCED_PARAMETER(ServerPortCookie);
UNREFERENCED_PARAMETER(ConnectionContext);
UNREFERENCED_PARAMETER(SizeOfContext);
UNREFERENCED_PARAMETER(ConnectionPortCookie);
ExAcquireFastMutex(&GlobalData.PortMutex);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
GlobalData.ClientPort = ClientPort;
GlobalData.PyasPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
@@ -67,7 +97,7 @@ static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID
ExReInitializeRundownProtection(&GlobalData.PortRundown);
ExReleaseFastMutex(&GlobalData.PortMutex);
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
return STATUS_SUCCESS;
}
@@ -75,9 +105,14 @@ static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID
static VOID PortDisconnect(PVOID ConnectionCookie) {
UNREFERENCED_PARAMETER(ConnectionCookie);
ExAcquireFastMutex(&GlobalData.PortMutex);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
GlobalData.ClientPort = NULL;
ExReleaseFastMutex(&GlobalData.PortMutex);
GlobalData.PyasPid = 0;
GlobalData.PyasProcess = NULL;
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
}
@@ -116,8 +151,8 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
GlobalData.DriverObject = DriverObject;
ExInitializeFastMutex(&GlobalData.PortMutex);
ExInitializeFastMutex(&GlobalData.TrackerMutex);
KeInitializeSpinLock(&GlobalData.PortMutex);
KeInitializeSpinLock(&GlobalData.TrackerMutex);
ExInitializeRundownProtection(&GlobalData.PortRundown);
@@ -136,27 +171,34 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, NULL, 1);
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, PortMessage, 1);
FltFreeSecurityDescriptor(sd);
}
if (!NT_SUCCESS(status)) {
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
UnloadRules();
UninitializeRulesEngine();
return status;
}
InitializeProcessProtection();
InitializeRegistryProtection(DriverObject);
PsSetLoadImageNotifyRoutine(ImageLoadNotify);
NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
if (NT_SUCCESS(imageNotifyStatus)) {
g_ImageNotifyRegistered = TRUE;
}
status = FltStartFiltering(GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
if (g_ImageNotifyRegistered) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyRegistered = FALSE;
}
UninitializeRegistryProtection();
UninitializeProcessProtection();
+1 -1
View File
@@ -3,7 +3,7 @@ Signature = "$WINDOWS NT$"
Class = ActivityMonitor
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = %ManufacturerName%
DriverVer = 12/29/2025,1.0.0.0
DriverVer = 04/26/2026,3.5.1.0
CatalogFile = PYAS_Driver.cat
PnpLockdown = 1
+5 -6
View File
@@ -199,6 +199,7 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RE
UNREFERENCED_PARAMETER(FltObjects);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
@@ -235,12 +236,10 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RE
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
}
if (SystemBuffer) {
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
}
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
+48 -7
View File
@@ -34,6 +34,36 @@ VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INF
}
}
static BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
if (ProcessId == (HANDLE)4) return TRUE;
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
NTSTATUS status;
BOOLEAN isCritical = FALSE;
PEPROCESS Process = NULL;
PUNICODE_STRING imageFileName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return FALSE;
status = SeLocateProcessImageName(Process, &imageFileName);
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
if (WildcardMatch(L"*\\Windows\\System32\\csrss.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\lsass.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\services.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\wininit.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\smss.exe", imageFileName->Buffer, imageFileName->Length)) {
isCritical = TRUE;
}
ExFreePool(imageFileName);
}
ObDereferenceObject(Process);
return isCritical;
}
static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
@@ -79,6 +109,8 @@ static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PR
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
if (IsTargetProtected(TargetPid)) {
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
if (bIsTrusted) {
@@ -99,10 +131,10 @@ static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PR
PROCESS_SET_INFORMATION |
PROCESS_SET_QUOTA;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
}
}
@@ -128,6 +160,8 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
if (IsTargetProtected(TargetPid)) {
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
if (bIsTrusted) {
@@ -144,10 +178,10 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
THREAD_SET_INFORMATION |
THREAD_SET_THREAD_TOKEN;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
}
}
@@ -155,8 +189,7 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
}
NTSTATUS InitializeProcessProtection() {
static UNICODE_STRING Altitude = { 0 };
RtlInitUnicodeString(&Altitude, L"320000");
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob");
ObCallbacks[0].ObjectType = PsProcessType;
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
@@ -174,7 +207,15 @@ NTSTATUS InitializeProcessProtection() {
ObRegistration.RegistrationContext = NULL;
ObRegistration.OperationRegistration = ObCallbacks;
return ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
NTSTATUS status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
if (!NT_SUCCESS(status) && (status == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION || status == STATUS_OBJECT_NAME_COLLISION)) {
static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob.Fallback");
ObRegistration.Altitude = FallbackAltitude;
status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
}
return status;
}
VOID UninitializeProcessProtection() {
+49 -39
View File
@@ -149,30 +149,32 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
(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;
if (FullSize <= 0xFFFF) {
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
RtlCopyUnicodeString(&FullPath, KeyName);
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"\\");
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);
}
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
}
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(Buffer);
}
PyasFree(Buffer);
}
}
}
@@ -194,30 +196,32 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
(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;
if (FullSize <= 0xFFFF) {
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
RtlCopyUnicodeString(&FullPath, KeyName);
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"\\");
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);
}
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
}
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(Buffer);
}
PyasFree(Buffer);
}
}
}
@@ -231,9 +235,15 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
}
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject) {
UNICODE_STRING Altitude;
RtlInitUnicodeString(&Altitude, L"320000");
return CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm");
NTSTATUS status = CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
if (!NT_SUCCESS(status)) {
static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm.Fallback");
status = CmRegisterCallbackEx(RegistryCallback, &FallbackAltitude, DriverObject, NULL, &Cookie, NULL);
}
return status;
}
VOID UninitializeRegistryProtection() {
+274 -107
View File
@@ -1,4 +1,4 @@
#include "DriverCommon.h"
#include "DriverCommon.h"
constexpr auto MAX_TRACKERS = 64;
constexpr auto RANSOM_TIME_WINDOW_MS = 3000;
@@ -10,19 +10,21 @@ constexpr auto TRUST_CACHE_TTL_SEC = 300;
typedef struct _RANSOM_TRACKER {
PEPROCESS Process;
LARGE_INTEGER ProcessCreateTime;
ULONG ActivityCount;
LARGE_INTEGER LastActivityTime;
} RANSOM_TRACKER, * PRANSOM_TRACKER;
typedef struct _TRUST_CACHE_ENTRY {
PEPROCESS Process;
LARGE_INTEGER ProcessCreateTime;
BOOLEAN IsTrusted;
LARGE_INTEGER CacheTime;
} TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY;
static RANSOM_TRACKER RansomTrackers[MAX_TRACKERS];
static TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE];
static FAST_MUTEX TrustCacheLock;
static KSPIN_LOCK TrustCacheLock;
static BOOLEAN g_CacheInitialized = FALSE;
static ERESOURCE RuleLock;
@@ -57,6 +59,14 @@ static VOID FreeList(PRULE_NODE* Head) {
static VOID AddRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
if (!RuleStr || !RuleStr->Buffer) return;
PRULE_NODE Check = *Head;
while (Check) {
if (RtlEqualUnicodeString(&Check->Pattern, RuleStr, TRUE)) {
return;
}
Check = Check->Next;
}
PRULE_NODE Node = (PRULE_NODE)PyasAllocate(sizeof(RULE_NODE));
if (!Node) return;
@@ -99,7 +109,7 @@ static BOOLEAN HasSuffix(PCUNICODE_STRING String, PCWSTR Suffix) {
}
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
if (!Pattern || !String) return FALSE;
if (Pattern == NULL || String == NULL) return FALSE;
USHORT StringLenChars = StringLengthBytes / sizeof(WCHAR);
PCWSTR mp = NULL;
@@ -115,7 +125,7 @@ BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
Pattern++;
String++;
}
else if (mp) {
else if (mp != NULL) {
Pattern = mp;
String = cp++;
}
@@ -126,7 +136,8 @@ BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
while (*Pattern == L'*') {
Pattern++;
}
return !*Pattern;
return (*Pattern == L'\0') ? TRUE : FALSE;
}
static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) {
@@ -194,17 +205,23 @@ static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyN
while (Ptr < End) {
SkipWhitespace(&Ptr, End);
if (Ptr >= End) break;
if (*Ptr == ']') {
Ptr++;
if (Ptr >= End || *Ptr == ']') {
if (Ptr < End) Ptr++;
return;
}
if (*Ptr == '"') {
PCHAR StartQuote = ++Ptr;
BOOLEAN Escaped = FALSE;
while (Ptr < End) {
if (*Ptr == '"' && *(Ptr - 1) != '\\') break;
if (*Ptr == '"' && Escaped == FALSE) break;
if (*Ptr == '\\') {
Escaped = (Escaped == FALSE) ? TRUE : FALSE;
}
else {
Escaped = FALSE;
}
Ptr++;
}
@@ -234,9 +251,6 @@ static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyN
Ptr++;
}
}
else if (*Ptr == ',') {
Ptr++;
}
else {
Ptr++;
}
@@ -251,7 +265,7 @@ static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyN
VOID InitializeRulesEngine() {
ExInitializeResourceLite(&RuleLock);
ExInitializeFastMutex(&TrustCacheLock);
KeInitializeSpinLock(&TrustCacheLock);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
g_CacheInitialized = TRUE;
}
@@ -261,6 +275,64 @@ VOID UninitializeRulesEngine() {
g_CacheInitialized = FALSE;
}
static VOID RemoveRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
if (!RuleStr || !RuleStr->Buffer) return;
PRULE_NODE Current = *Head;
PRULE_NODE Previous = NULL;
while (Current) {
if (RtlEqualUnicodeString(&Current->Pattern, RuleStr, TRUE)) {
PRULE_NODE ToDelete = Current;
if (Previous) {
Previous->Next = Current->Next;
}
else {
*Head = Current->Next;
}
Current = Current->Next;
if (ToDelete->Pattern.Buffer) PyasFree(ToDelete->Pattern.Buffer);
PyasFree(ToDelete);
}
else {
Previous = Current;
Current = Current->Next;
}
}
}
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr) {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
AddRule(&g_ProcessTrustedPaths, RuleStr);
AddRule(&g_FileExceptionPaths, RuleStr);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr) {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
RemoveRule(&g_ProcessTrustedPaths, RuleStr);
RemoveRule(&g_FileExceptionPaths, RuleStr);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
NTSTATUS status = STATUS_SUCCESS;
HANDLE FileHandle = NULL;
@@ -301,58 +373,58 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
}
if (Info->Type == REG_EXPAND_SZ || Info->Type == REG_SZ) {
PathBufferSize = ResultLength + 1024;
PathBufferSize = Info->DataLength + 1024;
PathBuffer = (PWCHAR)PyasAllocate(PathBufferSize);
if (PathBuffer) {
RtlZeroMemory(PathBuffer, PathBufferSize);
PWCHAR Src = (PWCHAR)Info->Data;
if (Info->DataLength > 0) {
RtlCopyMemory(PathBuffer, Info->Data, Info->DataLength);
}
if (NT_SUCCESS(RtlStringCbCopyW(PathBuffer, PathBufferSize, Src))) {
PWCHAR LastSlash = NULL;
PWCHAR Current = PathBuffer;
while (*Current) {
if (*Current == L'\\') LastSlash = Current;
Current++;
}
PWCHAR LastSlash = NULL;
PWCHAR Current = PathBuffer;
while (*Current) {
if (*Current == L'\\') LastSlash = Current;
Current++;
}
if (LastSlash) {
*LastSlash = L'\0';
if (LastSlash) {
*LastSlash = L'\0';
SIZE_T CurrentPathLen = wcslen(PathBuffer);
const WCHAR FilterSuffix[] = L"\\Filter";
SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1;
SIZE_T CurrentPathLen = wcslen(PathBuffer);
const WCHAR FilterSuffix[] = L"\\Filter";
SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1;
if (CurrentPathLen >= FilterLen) {
PWCHAR SuffixStart = PathBuffer + CurrentPathLen - FilterLen;
BOOLEAN Match = TRUE;
for (SIZE_T i = 0; i < FilterLen; i++) {
if (RtlDowncaseUnicodeChar(SuffixStart[i]) != RtlDowncaseUnicodeChar(FilterSuffix[i])) {
Match = FALSE;
break;
}
}
if (Match) {
*SuffixStart = L'\0';
if (CurrentPathLen >= FilterLen) {
PWCHAR SuffixStart = PathBuffer + CurrentPathLen - FilterLen;
BOOLEAN Match = TRUE;
for (SIZE_T i = 0; i < FilterLen; i++) {
if (RtlDowncaseUnicodeChar(SuffixStart[i]) != RtlDowncaseUnicodeChar(FilterSuffix[i])) {
Match = FALSE;
break;
}
}
RtlStringCbCatW(PathBuffer, PathBufferSize, L"\\Rules\\Rules_Driver_P1.json");
if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 &&
wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 &&
wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) {
PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16);
if (TmpBuffer) {
RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\");
RtlStringCbCatW(TmpBuffer, PathBufferSize + 16, PathBuffer);
PyasFree(PathBuffer);
PathBuffer = TmpBuffer;
}
if (Match) {
*SuffixStart = L'\0';
}
RtlInitUnicodeString(&FinalPath, PathBuffer);
}
RtlStringCbCatW(PathBuffer, PathBufferSize, L"\\Rules\\Rules_Driver_P1.json");
if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 &&
wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 &&
wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) {
PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16);
if (TmpBuffer) {
RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\");
RtlStringCbCatW(TmpBuffer, PathBufferSize + 16, PathBuffer);
PyasFree(PathBuffer);
PathBuffer = TmpBuffer;
}
}
RtlInitUnicodeString(&FinalPath, PathBuffer);
}
}
}
@@ -377,6 +449,7 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
if (NT_SUCCESS(status)) {
((PCHAR)FileBuffer)[FileInfo.EndOfFile.LowPart] = '\0';
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_BlockList", &g_RegistryBlockList);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_TrustedList", &g_RegistryTrustedList);
@@ -386,6 +459,7 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ExceptionPaths", &g_FileExceptionPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_RansomwareExtensions", &g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
PyasFree(FileBuffer);
}
@@ -398,6 +472,7 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
}
VOID UnloadRules() {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
FreeList(&g_RegistryBlockList);
FreeList(&g_RegistryTrustedList);
@@ -407,9 +482,14 @@ VOID UnloadRules() {
FreeList(&g_FileExceptionPaths);
FreeList(&g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
return STATUS_UNSUCCESSFUL;
}
NTSTATUS status;
PEPROCESS Process = NULL;
@@ -434,82 +514,121 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
if (ProcessId == (HANDLE)4) return TRUE;
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
PEPROCESS Process = NULL;
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
return FALSE;
}
LARGE_INTEGER createTime;
createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
if (g_CacheInitialized) {
ExAcquireFastMutex(&TrustCacheLock);
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
if (TrustCache[Hash].Process == Process) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1);
if (TrustCache[Hash].Process == Process && TrustCache[Hash].ProcessCreateTime.QuadPart == createTime.QuadPart) {
LARGE_INTEGER Now;
KeQuerySystemTime(&Now);
if ((Now.QuadPart - TrustCache[Hash].CacheTime.QuadPart) < (TRUST_CACHE_TTL_SEC * 10000000LL)) {
BOOLEAN cachedResult = TrustCache[Hash].IsTrusted;
ExReleaseFastMutex(&TrustCacheLock);
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ObDereferenceObject(Process);
return cachedResult;
}
}
ExReleaseFastMutex(&TrustCacheLock);
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
}
if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
ObDereferenceObject(Process);
return FALSE;
}
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
BOOLEAN isTrusted = FALSE;
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
ExAcquireResourceSharedLite(&RuleLock, TRUE);
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
PRULE_NODE Node = g_ProcessExploitable;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
ExReleaseResourceLite(&RuleLock);
goto cleanup;
goto update_cache;
}
Node = Node->Next;
}
if (IsWindowsSystemApp(imageFileName->Buffer, imageFileName->Length)) {
isTrusted = TRUE;
ExReleaseResourceLite(&RuleLock);
goto cleanup;
goto update_cache;
}
Node = g_ProcessTrustedPaths;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
isTrusted = TRUE;
ExReleaseResourceLite(&RuleLock);
goto cleanup;
goto update_cache;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
}
cleanup:
update_cache:
if (g_CacheInitialized) {
ExAcquireFastMutex(&TrustCacheLock);
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1);
TrustCache[Hash].Process = Process;
TrustCache[Hash].ProcessCreateTime = createTime;
TrustCache[Hash].IsTrusted = isTrusted;
KeQuerySystemTime(&TrustCache[Hash].CacheTime);
ExReleaseFastMutex(&TrustCacheLock);
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
if (imageFileName) ExFreePool(imageFileName);
ObDereferenceObject(Process);
return isTrusted;
}
BOOLEAN IsTargetProtected(HANDLE ProcessId) {
// 检查是否为已连接的 PYAS 进程
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
return FALSE;
// 额外检查:通过进程路径匹配保护规则(后备保护机制)
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
if (!NT_SUCCESS(status) || !imageFileName || !imageFileName->Buffer) {
return FALSE;
}
BOOLEAN isProtected = FALSE;
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
// 检查进程路径是否匹配保护路径规则
PRULE_NODE Node = g_FileProtectedPaths;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
isProtected = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
if (imageFileName) ExFreePool(imageFileName);
return isProtected;
}
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) {
@@ -520,12 +639,14 @@ BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) {
return FALSE;
}
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE AllowNode = g_RegistryTrustedList;
while (AllowNode) {
if (WildcardMatch(AllowNode->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
AllowNode = AllowNode->Next;
@@ -541,18 +662,21 @@ BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) {
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE ExNode = g_FileExceptionPaths;
while (ExNode) {
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
ExNode = ExNode->Next;
@@ -568,6 +692,7 @@ BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName) {
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
@@ -578,12 +703,14 @@ BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) {
return FALSE;
}
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE ExNode = g_FileExceptionPaths;
while (ExNode) {
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
ExNode = ExNode->Next;
@@ -599,13 +726,16 @@ BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) {
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
static BOOLEAN IsHighEntropy(PVOID Buffer, ULONG Length) {
if (!Buffer || Length < 256) return FALSE;
ULONG Histogram[256] = { 0 };
PUSHORT Histogram = (PUSHORT)PyasAllocate(256 * sizeof(USHORT));
if (!Histogram) return FALSE;
PUCHAR Ptr = (PUCHAR)Buffer;
ULONG ScanLen = (Length > 1024) ? 1024 : Length;
@@ -615,16 +745,20 @@ static BOOLEAN IsHighEntropy(PVOID Buffer, ULONG Length) {
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
PyasFree(Histogram);
return FALSE;
}
ULONG MaxFreq = 0;
USHORT MaxFreq = 0;
for (int i = 0; i < 256; i++) {
if (Histogram[i] > MaxFreq) MaxFreq = Histogram[i];
}
ULONG ExpectedAvg = ScanLen / 256;
return (MaxFreq < (ExpectedAvg + HIGH_ENTROPY_THRESHOLD));
BOOLEAN Result = (MaxFreq < (ExpectedAvg + HIGH_ENTROPY_THRESHOLD));
PyasFree(Histogram);
return Result;
}
static BOOLEAN IsNaturallyCompressed(PCUNICODE_STRING FileName) {
@@ -652,19 +786,33 @@ static BOOLEAN IsNoisyRansomPath(PCUNICODE_STRING FileName) {
return FALSE;
}
static BOOLEAN IsUserDirectoryPath(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
return WildcardMatch(L"*\\Users\\*", FileName->Buffer, FileName->Length);
}
static BOOLEAN IsExplorerProcess(HANDLE ProcessId) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
BOOLEAN isExplorer = FALSE;
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
if (HasSuffix(imageFileName, L"\\explorer.exe")) isExplorer = TRUE;
if (NT_SUCCESS(status) && imageFileName) {
if (imageFileName->Buffer && HasSuffix(imageFileName, L"\\explorer.exe")) {
isExplorer = TRUE;
}
ExFreePool(imageFileName);
}
return isExplorer;
}
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite) {
if (!IsUserDirectoryPath(FileName)) {
return FALSE;
}
if (IsNoisyRansomPath(FileName)) {
return FALSE;
}
@@ -691,9 +839,12 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
LARGE_INTEGER Now = { 0 };
KeQuerySystemTime(&Now);
LARGE_INTEGER createTime;
createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
BOOLEAN Result = FALSE;
ExAcquireFastMutex(&GlobalData.TrackerMutex);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.TrackerMutex, &OldIrql);
PRANSOM_TRACKER Tracker = NULL;
PRANSOM_TRACKER CandidateSlot = NULL;
@@ -706,7 +857,7 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
LruSlot = Current;
}
if (Current->Process == Process) {
if (Current->Process == Process && Current->ProcessCreateTime.QuadPart == createTime.QuadPart) {
Tracker = Current;
break;
}
@@ -734,6 +885,7 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
}
Tracker->Process = Process;
Tracker->ProcessCreateTime = createTime;
Tracker->ActivityCount = 0;
Tracker->LastActivityTime = Now;
}
@@ -753,7 +905,8 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
if (Tracker->ActivityCount >= RANSOM_COUNT_THRESHOLD) {
Result = TRUE;
}
ExReleaseFastMutex(&GlobalData.TrackerMutex);
KeReleaseSpinLock(&GlobalData.TrackerMutex, OldIrql);
ObDereferenceObject(Process);
return Result;
}
@@ -761,34 +914,48 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize) {
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_UNSUCCESSFUL;
PYAS_MESSAGE msg;
RtlZeroMemory(&msg, sizeof(msg));
msg.MessageCode = Code;
msg.ProcessId = Pid;
if (Path && PathSize > 0) {
size_t MaxSize = sizeof(msg.Path) - sizeof(WCHAR);
size_t BytesToCopy = PathSize;
if (BytesToCopy > MaxSize) BytesToCopy = MaxSize;
__try {
RtlCopyMemory(msg.Path, Path, BytesToCopy);
msg.Path[BytesToCopy / sizeof(WCHAR)] = L'\0';
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return STATUS_ACCESS_VIOLATION;
}
}
if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) {
return STATUS_PORT_DISCONNECTED;
}
NTSTATUS status = STATUS_PORT_DISCONNECTED;
if (GlobalData.ClientPort) {
LARGE_INTEGER timeout;
timeout.QuadPart = -(5 * 10000);
status = FltSendMessage(GlobalData.FilterHandle, &GlobalData.ClientPort, &msg, sizeof(msg), NULL, NULL, &timeout);
PPYAS_MESSAGE msg = (PPYAS_MESSAGE)PyasAllocate(sizeof(PYAS_MESSAGE));
if (msg) {
msg->MessageCode = Code;
msg->ProcessId = Pid;
if (Path && PathSize > 0) {
size_t MaxSize = sizeof(msg->Path) - sizeof(WCHAR);
size_t BytesToCopy = PathSize > MaxSize ? MaxSize : PathSize;
__try {
RtlCopyMemory(msg->Path, Path, BytesToCopy);
msg->Path[BytesToCopy / sizeof(WCHAR)] = L'\0';
}
__except (EXCEPTION_EXECUTE_HANDLER) {
PyasFree(msg);
ExReleaseRundownProtection(&GlobalData.PortRundown);
return STATUS_ACCESS_VIOLATION;
}
}
LARGE_INTEGER timeout;
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
timeout.QuadPart = -(5 * 10000);
}
else {
timeout.QuadPart = 0;
}
status = FltSendMessage(GlobalData.FilterHandle, &GlobalData.ClientPort, msg, sizeof(PYAS_MESSAGE), NULL, NULL, &timeout);
PyasFree(msg);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
ExReleaseRundownProtection(&GlobalData.PortRundown);
+20
View File
@@ -0,0 +1,20 @@
#include <windows.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_FILEDESCRIPTION_STR "PYAS Security Driver Protect"
#define VER_INTERNALNAME_STR "PYAS"
#define VER_LEGALCOPYRIGHT_STR "PYAS Security"
#define VER_ORIGINALFILENAME_STR "PYAS_Driver.sys"
#define VER_PRODUCTNAME_STR "PYAS"
#define VER_FILEVERSION 3,5,1,0
#define VER_FILEVERSION_STR "3,5,1,0"
#define VER_PRODUCTVERSION 3,5,1,0
#define VER_PRODUCTVERSION_STR "3,5,1,0"
#define VER_COMPANYNAME_STR "PYAS Security"
#include "common.ver"
+231 -213
View File
@@ -1,215 +1,233 @@
{
"Rule_Registry_BlockList": [
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Image File Execution Options\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
"*\\Classes\\.com",
"*\\Classes\\exefile\\*",
"*\\Classes\\batfile\\*",
"*\\Classes\\cmdfile\\*",
"*\\Classes\\comfile\\*",
"*\\mouclass",
"*\\kbdclass",
"*\\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",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
"*\\BootDriverFlags",
"*\\SystemStartOptions",
"*\\CrashDumpEnabled",
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\SystemSetupInProgress",
"*\\CmdLine",
"*\\SetupType",
"*\\AutoReboot",
"*\\Userinit",
"*\\UIHost",
"*\\Debugger",
"*\\DefaultUserName",
"*\\DefaultPassword",
"*\\AltDefaultUserName",
"*\\AltDefaultPassword",
"*\\DisableCAD",
"*\\LegalNoticeCaption",
"*\\LegalNoticeText",
],
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"\\Device\\HarddiskVolume*\\Windows\\*",
"\\Device\\HarddiskVolume*\\Program Files\\*",
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
"*\\Google\\Chrome\\Application\\chrome.exe",
"*\\Microsoft\\Edge\\Application\\msedge.exe",
"*\\Mozilla Firefox\\firefox.exe",
"*\\Steam\\steam.exe",
"*\\steam\\*\\steam.exe",
"*\\Battle.net\\*\\Battle.net.exe",
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
"*\\ThunderEv.exe",
"*\\ThunderStart.exe",
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
"*\\KuGou.exe",
"*\\XLGame.exe",
"*\\XLLiveUD.exe",
"*\\PC4399_WPFLauncher.exe",
"*\\bin\\APlayer.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",
"*\\Plugins\\Rules\\Rules_Driver_P1.json",
"*\\Engine\\Pattern\\*.onnx",
"*\\Engine\\Properties\\*.onnx",
"\\Device\\HarddiskVolume*\\bootmgr",
"\\Device\\HarddiskVolume*\\boot.ini",
"\\Device\\HarddiskVolume*\\BOOTNXT",
"\\Device\\HarddiskVolume*\\EFI\\*",
"\\Device\\HarddiskVolume*\\Boot\\*",
"\\Device\\HarddiskVolume*\\Recovery\\*",
"\\Device\\HarddiskVolume*\\System Volume Information\\*",
"\\Device\\HarddiskVolume*\\Windows\\System32\\config\\*",
"*\\CON",
"*\\CON\\*",
"*\\PRN",
"*\\PRN\\*",
"*\\AUX",
"*\\AUX\\*",
"*\\NUL",
"*\\NUL\\*",
"*\\COM?",
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
],
"Rule_File_ExceptionPaths": [
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
],
"Rule_File_RansomwareExtensions": [
".zip", ".7z", ".rar", ".tar", ".gz",
".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",
]
"Rule_Registry_BlockList": [
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
"*\\Classes\\.com",
"*\\Classes\\exefile\\*",
"*\\Classes\\batfile\\*",
"*\\Classes\\cmdfile\\*",
"*\\Classes\\comfile\\*",
"*\\mouclass",
"*\\kbdclass",
"*\\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",
"*\\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",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
"*\\BootDriverFlags",
"*\\SystemStartOptions",
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\SystemSetupInProgress",
"*\\SetupType",
"*\\AutoReboot",
"*\\Userinit",
"*\\UIHost",
"*\\Debugger",
"*\\DefaultUserName",
"*\\DefaultPassword",
"*\\AltDefaultUserName",
"*\\AltDefaultPassword",
"*\\DisableCAD",
"*\\LegalNoticeCaption",
"*\\LegalNoticeText",
],
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"\\Device\\HarddiskVolume*\\Windows\\*",
"\\Device\\HarddiskVolume*\\Program Files\\*",
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
"*\\Sources\\SetupHost.exe",
"*\\Work\\*\\DismHost.exe",
"*\\Google\\Chrome\\Application\\chrome.exe",
"*\\Microsoft\\Edge\\Application\\msedge.exe",
"*\\Mozilla Firefox\\firefox.exe",
"*\\Thorium\\Application\\thorium.exe",
"*\\Steam\\steam.exe",
"*\\steam\\*\\steam.exe",
"*\\Battle.net\\*\\Battle.net.exe",
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
"*\\ThunderEv.exe",
"*\\ThunderStart.exe",
"*\\KuGou.exe",
"*\\XLGame.exe",
"*\\XLLiveUD.exe",
"*\\hrkill-*.exe",
"*\\bin\\HipsDaemon.exe",
"*\\360Safe.exe",
"*\\bilibili\\bilibili.exe",
"*\\Kingsoft\\WPS Office\\ksolaunch.exe",
"*\\Kingsoft\\WPS Office\\*\\wps.exe",
"*\\Kingsoft\\WPS Office\\*\\*.exe",
"*\\PC4399_WPFLauncher.exe",
"*\\bin\\APlayer.exe",
"*\\Neverness To Everness\\NTELauncher.exe",
"*\\Application\\AndrowsLauncher.exe",
"*\\bin\\*\\codex.exe",
"*\\bin\\codex.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",
"*\\Plugins\\Filter\\PYAS_Driver.sys",
"*\\Plugins\\Rules\\Rules_Driver_P1.json",
"*\\ProgramData\\PYAS\\*.json",
"*\\Engine\\Pattern\\*.onnx",
"*\\Engine\\Properties\\*.onnx",
"\\Device\\HarddiskVolume*\\bootmgr",
"\\Device\\HarddiskVolume*\\boot.ini",
"\\Device\\HarddiskVolume*\\BOOTNXT",
"\\Device\\HarddiskVolume*\\EFI\\*",
"\\Device\\HarddiskVolume*\\Boot\\*",
"\\Device\\HarddiskVolume*\\Recovery\\*",
"\\Device\\HarddiskVolume*\\System Volume Information\\*",
"\\Device\\HarddiskVolume*\\Windows\\System32\\config\\*",
"*\\CON",
"*\\CON\\*",
"*\\PRN",
"*\\PRN\\*",
"*\\AUX",
"*\\AUX\\*",
"*\\NUL",
"*\\NUL\\*",
"*\\COM?",
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
],
"Rule_File_ExceptionPaths": [
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
"*\\cache\\*",
"*\\Documents\\My Games\\*",
"*\\Documents\\Tencent Files\\*",
"*\\Documents\\WeChat Files\\*",
"*\\Documents\\WPS Cloud Files\\*"
],
"Rule_File_RansomwareExtensions": [
".zip", ".7z", ".rar", ".tar", ".gz",
".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",
]
}
+95 -136
View File
@@ -1,23 +1,32 @@
# PYAS
# PYAS Security
Antivirus software written in Python and C++ that blocks threats through deep learning and behavioral monitoring!
Antivirus software written in Python and C++ that blocks threats through Machine Learning and behavioral monitoring!
<img width="2245" height="1477" alt="PYAS_UI" src="https://github.com/user-attachments/assets/87d40261-7655-49ad-a19c-1ffcca60584f" />
<img width="2440" height="1600" alt="PYAS_All" src="https://github.com/user-attachments/assets/1991aad3-64cc-4266-ac67-dab70c891ce7" />
## Requirements
Python 3.10 is recommended. Other Python versions may require different pip commands.
```
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
pip install pystray
pip install pefile
pip install requests
pip install pywebview
pip install Pillow
pip install yara-python
pip install numpy
pip install onnxruntime
```
Non-essential requirements installation, only used for model training or other functions.
```
pip install pandas
pip install scikit-learn
pip install lightgbm
pip install onnxmltools
pip install orjson
```
## File Information
@@ -26,41 +35,28 @@ The following lists the storage locations of all relevant code and other related
```
PYAS/
├── Embed/
│ ├── Dockerfile # Deploy an embedded command line antivirus engine
│ └── ... # Other microservice for VirusTotal Integration
│
├── Engine/
│ ├── Pattern/
│ │ ├── convert.py # Convert executable files or other files to images
│ │ ├── train.py # TensorFlow CNN model training complete code
│ │ └── ... # Other models folders and files
│ │
├── Engine/ # Complete code for Yara signatures and AI model training
│ ├── Heuristic/
│ │ ├── rules.yar # Yara virus signature rule matching
│ │ └── ... # Other rules folders and files
│ │
│ │ └── ... # Yara Rules
│ └── Properties/
│ ├── convert.py # Convert executable files to sql database
│ ├── train.py # Lightgbm model training complete code
│ └── ... # Other models folders and files
│ └── ... # LightGBM Model
│
├── Plugins/
│ └── Filter/
│ │ ├── DriverEntry.cpp # Main driver entry and initialization logic
│ │ ├── DriverCommon.h # Global driver definitions, constants, and functions
│ │ └── ... # Other driver folders and files
│ │
├── Interface/ # Interface Interaction and Icons with WebView2
│ ├── static/
│ │ └── ... # style.css, main.js, icon.ico
│ └── templates/
│ └── ... # index.html
│
├── Plugins/ # Main filter driver protect and rules
│ ├── Filter/
│ │ └── ... # DriverCommon.h, DriverEntry.cpp, ProtectRegistry.cpp, ...
│ └── Rules/
│ └── rules.json # White, block list, and matching logic for files registry
│ └── ... # Rules_Driver_P1.json
|
├── PYAS.py # Main application entry point and UI to engine interface
├── PYAS_Config.py # Configuration loading, saving, and global parameters
├── PYAS_Engine.py # Core scanning engine: YARA, IP, ONNX model execution
├── PYAS_Interface.py # User interface components and event handling
├── PYAS_Resource.py # Static image and icon resource management
├── PYAS.py # Main antivirus application entry point
├── PYAS_Engine.py # Core scanning engine algorithm
├── PYAS_Version.py # Version metadata for packaging and updates
└── ... # Other supplementary folders and files
└── ...
```
## Architecture diagram
@@ -75,117 +71,78 @@ graph TD
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
subgraph UserSpace [User Mode Application - PYAS.exe]
direction TB
subgraph UserMode ["User Mode (Python / Web)"]
UI["Web UI (pywebview)"]:::userMode
PYASCore["PYAS Core (PYAS.py)"]:::userMode
GUI[MainWindow_Controller / UI]:::userMode
ConfigMgr[Configuration & Rule Manager]:::userMode
subgraph Engines ["Scanning Engines (PYAS_Engine.py)"]
PEScanner["PE/ML Scanner (pe_scanner)"]:::userMode
RuleScanner["YARA Scanner (rule_scanner)"]:::userMode
CloudScanner["Cloud API (cloud_scanner)"]:::userMode
SignScanner["Signature Verify (sign_scanner)"]:::userMode
end
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
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
GUI --> ConfigMgr
GUI --> DetectionEngine
GUI --> UserMonitors
UserMonitors --> DetectionEngine
CommClient["Filter Communication Client"]:::interaction
end
subgraph StorageLayer [File System / Configuration]
direction LR
JSONRules[JSON Rules Files]:::storage
ConfigJSON[Config.json]:::storage
Quarantine[Quarantine Folder]:::storage
end
subgraph KernelMode ["Kernel Mode (C++ Minifilter Driver)"]
CommServer["ALPC Port (\\PYAS_Output_Pipe)"]:::interaction
DriverCore["Driver Entry (DriverEntry.cpp)"]:::kernelMode
RuleEngine["Rules & Trust Cache (ProtectRules.cpp)"]:::kernelMode
subgraph KernelSpace [Kernel Mode Driver - PYAS_Driver.sys]
direction TB
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
subgraph Protections ["Protection Subsystems"]
FileProtect["File Minifilter (ProtectFile.cpp)"]:::kernelMode
ProcessProtect["ObRegisterCallbacks (ProtectProcess.cpp)"]:::kernelMode
RegProtect["CmRegisterCallbackEx (ProtectRegistry.cpp)"]:::kernelMode
BootProtect["IRP_MJ_DEVICE_CONTROL (ProtectBoot.cpp)"]:::kernelMode
end
DriverEntry --> GlobalData
DriverEntry --> CommServer
DriverEntry --> MiniFilter
DriverEntry --> ProcProtect
DriverEntry --> ImageLoad
DriverEntry --> RegFilter
MiniFilter --> RuleLoader
ProcProtect --> RuleLoader
RegFilter --> RuleLoader
PreWrite --> TrustCache
PreCreate --> TrustCache
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
subgraph OS ["Windows OS & Storage"]
FS["File System (FltMgr)"]:::storage
Reg["Registry (CmMgr)"]:::storage
ProcMgr["Process/Thread Manager (ObMgr)"]:::storage
Disk["Physical Disk / MBR"]:::storage
end
CloudAPI["PYAS Cloud Server"]:::storage
%% User Mode Internal
UI <-->|JS Evaluate / JSON| PYASCore
PYASCore -->|Extract Features & Scan| Engines
CloudScanner -.->|HTTPS REST API| CloudAPI
PYASCore <-->|FilterSendMessage / FilterGetMessage| CommClient
%% User-Kernel Boundary
CommClient <-->|PYAS_MESSAGE / PYAS_USER_MESSAGE| CommServer
%% Kernel Mode Internal
CommServer <-->|Message Dispatch| DriverCore
DriverCore --> RuleEngine
RuleEngine --> Protections
%% Kernel-OS Boundary
FileProtect -->|IRP_MJ_CREATE, IRP_MJ_WRITE...| FS
ProcessProtect -->|PreOpenProcess, PreOpenThread| ProcMgr
RegProtect -->|RegNtPreCreateKey, RegNtPreSetValueKey...| Reg
BootProtect -->|IOCTL_DISK_FORMAT_TRACKS...| Disk
```
## Machine Learning
The machine learning-based virus scanning engine training achieved extremely high accuracy.
<img width="895" height="1097" alt="train" src="https://github.com/user-attachments/assets/fa7cf0e2-01a1-4234-9358-718c2b8a2c9a" />
## Support System
Please ensure your computer supports Microsoft Visual C++ 2015-2022 Redistributable and Edge Webview2.
| Config | Permissions | System version | Processor | Memory | Storage |
|-----------|---------------|----------------------|-----------|--------|---------|
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 200MB | 100MB |
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 300MB | 100MB |
| Recommend | Administrator | >= Windows 10 (21H2) | 3 GHz | 500MB | 200MB |
## Packaged Releases
Download the installer. If it is incompatible with your system, you can repackage it yourself.
Packaged Download: https://github.com/87owo/PYAS/releases
## Official Website
If you are interested in this project, you can visit the website to see other related content.
@@ -196,6 +153,8 @@ Official Website : https://pyas-security.com/antivirus
Online Analyze : https://pyas-security.com/analyze
Packaged Download: https://github.com/87owo/PYAS/releases
## Project License
For any questions, needs, or bug feedback, please contact us through the following website.