Compare commits

...
39 Commits
Author SHA1 Message Date
87owo 71605ade31 Add files via upload 2026-04-19 02:21:41 +08:00
87owo ee409e4872 Add files via upload 2026-04-18 01:41:13 +08:00
87owo 91024abc4a Add files via upload 2026-04-15 19:59:41 +08:00
87owo 99de36a6cf Add files via upload 2026-04-15 19:57:35 +08:00
87owo ac1e48dc30 Add files via upload 2026-04-12 13:49:40 +08:00
87owo 494bab3380 Add files via upload 2026-04-12 13:42:38 +08:00
87owo 6696ca93ce Add files via upload 2026-04-11 16:50:33 +08:00
87owo b7e80fb8ed Add files via upload 2026-04-10 17:47:07 +08:00
87owo ce5b642d3e Add files via upload 2026-04-10 15:50:15 +08:00
87owo 76d414164a Update PYAS.py 2026-04-10 15:16:07 +08:00
87owo b6d13047ba Merge pull request #40 from MichaelMVS/fix-bare-except-clauses
Fix bare except clauses to catch only Exception types
2026-04-10 15:05:22 +08:00
87owo f1ba295796 Update README.md 2026-04-10 14:45:49 +08:00
zocomputer 415abef4fc Fix bare except clauses to catch only Exception types
Replace bare 'except:' with 'except Exception:' in three locations:
- check_process_survival (line ~1632): when accessing pe.szExeFile
- get_process_name (line ~1782): when getting process name from snapshot
- pipe_server_thread (line ~2340): when terminating malicious processes

Bare except clauses also catch SystemExit and KeyboardInterrupt,
which can prevent proper shutdown and mask serious issues.
Using 'except Exception:' allows the code to properly handle
expected errors while letting system-exiting events propagate.
2026-04-10 06:40:26 +00:00
87owo 95af2014d8 Update README.md 2026-04-10 14:39:38 +08:00
87owo f75b039daf Add files via upload 2026-04-10 14:35:02 +08:00
87owo 65b1ac78f3 Rename train_vt.py to train_server_lfp.py 2026-04-10 14:27:44 +08:00
87owo b067591882 Add files via upload 2026-04-10 14:24:58 +08:00
87owo 7941d76fd7 Delete Engine/Properties/train.py 2026-04-10 14:13:24 +08:00
87owo 7bff601beb Add files via upload 2026-04-10 14:12:27 +08:00
87owo 7b4da32bee Update README.md 2026-04-10 14:00:42 +08:00
87owo bbf2974703 Update README.md 2026-04-10 13:50:28 +08:00
87owo f5abaf06de Add files via upload 2026-04-08 17:57:14 +08:00
87owo 88023dd09e Add files via upload 2026-04-08 01:20:52 +08:00
87owo eec2996fab Add files via upload 2026-04-07 23:53:35 +08:00
87owo 97ca200c29 Add files via upload 2026-04-07 23:52:55 +08:00
87owo f0600ea3a3 Add files via upload 2026-04-07 23:52:20 +08:00
87owo 19b3eb51d7 Add files via upload 2026-04-04 16:02:36 +08:00
87owo 871dabf142 Add files via upload 2026-04-02 01:30:02 +08:00
87owo 58795b9349 Add files via upload 2026-04-01 23:42:12 +08:00
87owo 1ef769842b Add files via upload 2026-04-01 22:45:57 +08:00
87owo 6051570414 Add files via upload 2026-04-01 22:45:18 +08:00
87owo da5d5d0dc7 Add files via upload 2026-03-31 19:39:18 +08:00
87owo 782aa22d4d Add files via upload 2026-03-31 19:14:08 +08:00
87owo 0084867f38 Add files via upload 2026-03-29 21:14:20 +08:00
87owo 5a27858f86 Add files via upload 2026-03-27 02:17:54 +08:00
87owo 7905d8375f Add files via upload 2026-03-25 15:59:26 +08:00
87owo ba8aa63d75 Add files via upload 2026-03-16 15:10:16 +08:00
87owo ac82778e26 Add files via upload 2026-03-11 17:55:19 +08:00
87owo 55d2661e38 Add files via upload 2026-03-11 17:37:54 +08:00
20 changed files with 1488 additions and 302 deletions

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
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
@@ -0,0 +1 @@
["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
@@ -0,0 +1,611 @@
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
@@ -0,0 +1,4 @@
onnxruntime
numpy
pefile
signify[full]
+1 -1
View File
@@ -6,7 +6,7 @@ import ctypes.wintypes
DB_PATH = "pe_features.db"
BATCH_SIZE = 1000
MAX_FILE_SIZE = 256 * 1024 * 1024
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
TARGET_EXTENSIONS = {
'.exe', '.dll', '.sys', '.ocx', '.scr', '.efi', '.acm', '.ax', '.cpl', '.drv', '.com', '.mui', '.pyd'
}
@@ -21,7 +21,7 @@ LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 31,
'num_leaves': 512,
'learning_rate': 0.05,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
@@ -114,7 +114,7 @@ def train_process(X, y):
n_neg = (y_train == 0).sum()
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
weight_ratio_target = 0.01
weight_ratio_target = 0.1
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Balance Report: Safe={n_neg}, Malware={n_pos}")
@@ -214,4 +214,3 @@ if __name__ == "__main__":
print("\n\n[-] Training interrupted.")
except Exception as e:
print(f"\n[-] Critical Error: {e}")
+216
View File
@@ -0,0 +1,216 @@
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}")
+216
View File
@@ -0,0 +1,216 @@
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 = "Pefile_General_T1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 63,
'learning_rate': 0.05,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 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}")
+120 -35
View File
@@ -151,12 +151,12 @@ class MainWindow_Controller(QMainWindow):
"hindi_switch", "arabic_switch", "russian_switch", "slovenian_switch",
]
self.pyas_default = {
"version": "3.4.2",
"version": "3.4.6",
"api_host": "https://pyas-security.com/",
"api_key": "fBRZxYS1UxykM-qzNOlKOEl63WILzlvgNMn6QfsG6FXCAAIktCrOPTAfY5_hEyuZ",
"suffix": [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd"],
"block": [2001, 3001, 4001, 5001, 6001],
"size": 256 * 1024 * 1024,
"block": [3001, 5001, 6001],
"size": 100 * 1024 * 1024,
"language": "english_switch",
"theme": "white_switch",
"process_switch": True,
@@ -178,6 +178,7 @@ class MainWindow_Controller(QMainWindow):
self.block_replace = {
"FILE_BLOCK": "檔案行為攔截",
"RANSOM_BLOCK": "勒索行為攔截",
"RANSOM_ROLLBACK": "勒索檔案回滾",
"BOOT_BLOCK": "引導行為攔截",
"REG_BLOCK": "註冊表行為攔截",
"CLR_BLOCK": "執行環境攔截",
@@ -194,7 +195,8 @@ class MainWindow_Controller(QMainWindow):
3001: "REG_BLOCK",
4001: "BOOT_BLOCK",
5001: "RANSOM_BLOCK",
6001: "INJECT_BLOCK"
6001: "INJECT_BLOCK",
7002: "RANSOM_ROLLBACK",
}
####################################################################################################
@@ -309,9 +311,32 @@ class MainWindow_Controller(QMainWindow):
if k.endswith("_button") and isinstance(v, bool) and k.replace("_button", "_window") in self.widgets:
continue
filter_config[k] = v
path = os.path.dirname(file_path)
if not os.path.exists(path):
os.makedirs(path)
if os.path.exists(file_path):
old_config = self.read_config(file_path, {})
changes = []
for k, v in filter_config.items():
if k not in old_config:
changes.append(f"增加 [{k}]")
elif old_config[k] != v:
if isinstance(v, (list, dict)):
changes.append(f"更新 [{k}]")
else:
changes.append(f"更新 [{k}]: {old_config[k]} -> {v}")
for k in old_config:
if k not in filter_config:
changes.append(f"刪除 [{k}]")
if changes:
self.send_message(f"配置更新 | {', '.join(changes)}", "config", True)
with open(file_path, "w", encoding="utf-8", errors="ignore") as f:
f.write(json.dumps(filter_config, indent=4, ensure_ascii=False))
except Exception as e:
@@ -374,11 +399,15 @@ class MainWindow_Controller(QMainWindow):
"taskmgr_window", "tool_window", "scan_window", "home_window"]]
for w in self.sub_window:
if w: w.hide()
if w:
w.hide()
for w in self.main_window + self.menu_window:
if w: w.raise_()
if w:
w.raise_()
for w in self.menu_window + self.blank_window:
if w: w.setGraphicsEffect(self.create_shadow())
if w:
w.setAttribute(Qt.WA_StyledBackground, True)
w.setGraphicsEffect(self.create_shadow())
self.last_widget = self.widgets.get("home_window")
self.setup_theme_mapping()
@@ -451,33 +480,53 @@ class MainWindow_Controller(QMainWindow):
def save_state(self, name, checked):
self.pyas_config[name] = bool(checked)
if name == "sensitive_switch":
pass
if checked:
if not self.isVisible():
self.save_config(self.file_config, self.pyas_config)
elif self.send_message("增強模式可能會誤報檔案,您確定要啟用嗎?", "quest", True):
self.save_config(self.file_config, self.pyas_config)
else:
self.pyas_config[name] = False
if name in self.widgets:
QTimer.singleShot(0, lambda: self.widgets[name].setChecked(False))
else:
self.save_config(self.file_config, self.pyas_config)
elif name == "extension_switch":
pass
self.save_config(self.file_config, self.pyas_config)
elif name == "cloud_switch":
pass
self.save_config(self.file_config, self.pyas_config)
elif name == "process_switch":
if checked:
self.start_daemon_thread(self.protect_proc_thread)
self.save_config(self.file_config, self.pyas_config)
elif name == "document_switch":
if checked:
self.start_daemon_thread(self.protect_file_thread)
self.save_config(self.file_config, self.pyas_config)
elif name == "system_switch":
if checked:
self.start_daemon_thread(self.protect_system_thread)
elif name == "driver_switch":
if not checked:
self.stop_system_driver()
elif self.install_system_driver():
self.start_daemon_thread(self.pipe_server_thread)
else:
self.send_message("驅動防護啟用失敗,請重啟裝置來修復錯誤", "warn", True)
self.save_config(self.file_config, self.pyas_config)
elif name == "network_switch":
if checked:
self.start_daemon_thread(self.protect_net_thread)
self.save_config(self.file_config, self.pyas_config)
elif name == "driver_switch":
if checked:
if self.install_system_driver():
self.start_daemon_thread(self.pipe_server_thread)
else:
self.pyas_config[name] = False
if name in self.widgets:
QTimer.singleShot(0, lambda: self.widgets[name].setChecked(False))
self.send_message("驅動防護啟用失敗", "warn", True)
else:
self.stop_system_driver()
self.save_config(self.file_config, self.pyas_config)
else:
self.send_message(f"此功能不支持使用", "info", True)
self.save_config(self.file_config, self.pyas_config)
def init_thread(self):
self.start_daemon_thread(self.popup_intercept_thread)
@@ -540,7 +589,20 @@ class MainWindow_Controller(QMainWindow):
style = widget._origin_style
for k, v in theme_map.items():
style = style.replace(str(k), str(v))
widget.setStyleSheet(style)
widget.style().unpolish(widget)
widget.style().polish(widget)
try:
widget.update()
except TypeError:
if hasattr(widget, "viewport"):
widget.viewport().update()
effect = widget.graphicsEffect()
if effect:
effect.update()
mapping = getattr(widget, "_theme_mapping", None)
if isinstance(mapping, dict):
@@ -632,7 +694,7 @@ class MainWindow_Controller(QMainWindow):
if widget in (scan_win, method_win):
if getattr(self, "scan_finished", False):
self.widgets["solve_button"].setVisible(len(self.virus_results) > 0)
self.widgets["solve_button"].setVisible(False)
self.widgets["stop_button"].hide()
self.widgets["method_button"].show()
@@ -697,6 +759,9 @@ class MainWindow_Controller(QMainWindow):
self.init_connect()
self.apply_settings()
current_version = self.pyas_config.get("version", "None")
self.send_message(f"當前版本 | {current_version}", "version", True)
param = self.args_pyas[0].replace("/", "-") if self.args_pyas else ""
if "-h" not in param:
self.show_button()
@@ -861,7 +926,7 @@ class MainWindow_Controller(QMainWindow):
@Slot()
def slot_scan_reset(self):
count = len(self.virus_results)
self.widgets["solve_button"].setVisible(count > 0)
self.widgets["solve_button"].setVisible(False)
self.widgets["virus_list"].clear()
self.widgets["method_window"].hide()
self.widgets["stop_button"].setVisible(self.scan_running)
@@ -974,6 +1039,7 @@ class MainWindow_Controller(QMainWindow):
self.virus_results.append(norm_path)
self.scan_add_virus_signal.emit(f"[{result}] > {norm_path}")
self.start_daemon_thread(self.cloud_check, norm_path)
self.send_message(f"病毒掃描 | scan_worker | None | None | {norm_path}", "scan", True)
lang = self.pyas_config.get("language", "english_switch")
self.progress_title_signal.emit(self.trans(lang, "正在掃描"))
@@ -1035,7 +1101,7 @@ class MainWindow_Controller(QMainWindow):
####################################################################################################
def init_cloud_worker(self):
for _ in range(1):
for _ in range(4):
t = threading.Thread(target=self.cloud_worker, daemon=True)
t.start()
@@ -1068,7 +1134,7 @@ class MainWindow_Controller(QMainWindow):
was_locked = True
size = os.path.getsize(file_path)
if size > self.pyas_config.get("size", 256 * 1024 * 1024):
if size > self.pyas_config.get("size", 100 * 1024 * 1024):
if was_locked:
self.lock_file(file_path, True)
return False
@@ -1101,6 +1167,7 @@ class MainWindow_Controller(QMainWindow):
def solve_button(self):
if self.send_message("您確定要刪除檔案嗎?", "quest"):
self.widgets["solve_button"].setVisible(False)
deleted = 0
virus_list = self.widgets["virus_list"]
start_ts = time.time()
@@ -1208,9 +1275,6 @@ class MainWindow_Controller(QMainWindow):
except Exception as e:
self.send_message(f"show_scan_menu | {e}", "warn", False)
count = len(self.virus_results)
self.widgets["solve_button"].setVisible(count > 0)
####################################################################################################
def list_process(self):
@@ -1567,7 +1631,7 @@ class MainWindow_Controller(QMainWindow):
try:
name = pe.szExeFile.lower()
running.add(name)
except:
except Exception:
pass
if not self.kernel32.Process32NextW(snapshot, ctypes.byref(pe)):
break
@@ -1717,7 +1781,7 @@ class MainWindow_Controller(QMainWindow):
self.kernel32.CloseHandle(snapshot)
return result_name
except:
except Exception:
pass
return result_name
@@ -1761,8 +1825,8 @@ class MainWindow_Controller(QMainWindow):
if not p:
return False
file_hash = self.calc_file_hash(p)
return any(os.path.normcase(item.get("file","")) == os.path.normcase(p) and
item.get("hash","") == file_hash for item in self.pyas_config.get("white_list", []))
return any(os.path.normcase(item["file"]) == p and
item.get("hash", "") == file_hash for item in self.pyas_config.get("white_list", []))
def init_whitelist(self):
self.manage_named_list("white_list", [self.file_pyas], action="add", with_hash=True)
@@ -1829,9 +1893,10 @@ class MainWindow_Controller(QMainWindow):
current = str(self.pyas_config.get("version", "")).strip()
page = "https://github.com/87owo/PYAS/releases"
latest = ""
try:
j = requests.get("https://api.github.com/repos/87owo/PYAS/releases/latest", headers={"Accept": "application/vnd.github+json", "User-Agent": "PYAS"}, timeout=10).json()
latest = (j.get("tag_name") or j.get("name") or "").strip()
latest = str(j.get("tag_name") or j.get("name") or "").strip()
page = j.get("html_url") or page
except Exception:
try:
@@ -1842,10 +1907,15 @@ class MainWindow_Controller(QMainWindow):
except Exception:
pass
rl = re.sub(r"^[vV]\s*", "", str(latest))
cl = re.sub(r"^[vV]\s*", "", str(current))
if not latest:
self.send_message("檢查更新失敗", "error", True)
return False
rl = re.sub(r"^[vV]\s*", "", latest)
cl = re.sub(r"^[vV]\s*", "", current)
tr = tuple(int(x) for x in re.findall(r"\d+", rl))
tl = tuple(int(x) for x in re.findall(r"\d+", cl))
if tr and (not tl or tr > tl):
if self.send_message(f"發現新版本 {latest},您確定要前往更新嗎?", "quest", True):
webbrowser.open(page)
@@ -1855,8 +1925,9 @@ class MainWindow_Controller(QMainWindow):
else:
webbrowser.open(page)
return True
except Exception as e:
self.send_message(f"update_button | {e}", "warn", False)
self.send_message(f"update_button | {e}", "error", False)
return False
####################################################################################################
@@ -2197,7 +2268,7 @@ class MainWindow_Controller(QMainWindow):
winreg.SetValueEx(key, "Flags", 0, winreg.REG_DWORD, 0)
subprocess.run(["sc", "start", service_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
return True
return self.check_system_driver()
except Exception as e:
self.send_message(f"install_system_driver | {e}", "warn", False)
return False
@@ -2219,6 +2290,20 @@ class MainWindow_Controller(QMainWindow):
except Exception:
return False
def check_system_driver(self):
try:
service_name = "PYAS_Driver"
result = subprocess.run(
["sc", "query", service_name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
shell=True
)
return "RUNNING" in result.stdout
except Exception:
return False
####################################################################################################
def pipe_server_thread(self):
@@ -2254,7 +2339,7 @@ class MainWindow_Controller(QMainWindow):
if h:
self.kernel32.TerminateProcess(h, 0)
self.kernel32.CloseHandle(h)
except:
except Exception:
pass
else:
break
+100 -80
View File
@@ -538,10 +538,12 @@ class pe_scanner:
pe = None
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > 268435456:
if fsize == 0 or fsize > 512 * 1024 * 1024:
return None
with open(file_path, "rb") as f:
file_bytes = f.read()
pe = pefile.PE(data=file_bytes, fast_load=True)
fts['TrustSigned'] = 1 if self.signer.sign_verify(file_path) else 0
fh = pe.FILE_HEADER
@@ -800,7 +802,26 @@ class pe_scanner:
class cloud_scanner:
def __init__(self):
pass
self.session = None
self.api_host = None
self.api_key = None
self.timeout = 30
def _init_session(self, api_host, api_key):
if self.session is None or self.api_host != api_host or self.api_key != api_key:
self.api_host = api_host.rstrip('/')
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({"X-API-Key": api_key, "User-Agent": "PYAS-Engine/1.2"})
def _request(self, method, endpoint, **kwargs):
try:
r = self.session.request(method, f"{self.api_host}{endpoint}", timeout=self.timeout, **kwargs)
if r.status_code == 200:
return r
except Exception:
pass
return None
def calc_hash(self, file_path, block_size=65536):
try:
@@ -814,107 +835,106 @@ class cloud_scanner:
####################################################################################################
def upload_file(self, file_path, api_host, api_key):
def rescan(self, sha256, api_host, api_key):
self._init_session(api_host, api_key)
r = self._request("POST", f"/api/rescan/{sha256}")
return r is not None and r.json().get('status') == 'success'
def upload_file(self, file_path, api_host, api_key, chunk_size=5242880):
try:
self._init_session(api_host, api_key)
sha256 = self.calc_hash(file_path)
if not sha256:
return False, None
base = api_host.rstrip('/')
base_headers = {"X-API-Key": api_key, "User-Agent": "PYAS-Client/1.0"}
status_req = self._request("GET", f"/api/processing_status/{sha256}")
if status_req and status_req.json().get('status') != 'missing':
self.rescan(sha256, api_host, api_key)
return True, sha256
def _req(m, ep, files=None, req_timeout=5, **k):
try:
return requests.request(m, f"{base}{ep}", headers=base_headers, files=files, timeout=req_timeout, **k)
except:
return None
file_size = os.path.getsize(file_path)
if file_size > 104857600:
return False, sha256
r = _req("GET", f"/api/processing_status/{sha256}", req_timeout=15)
status = 'missing'
if r and r.status_code == 200:
try:
status = r.json().get('status', 'missing')
except Exception:
pass
if status in ['failed', 'error']:
status = 'missing'
if status == 'missing':
if file_size > chunk_size:
total_chunks = (file_size + chunk_size - 1) // chunk_size
upload_id = os.urandom(16).hex()
with open(file_path, 'rb') as f:
r = _req("POST", "/api/upload", files={'file': f}, req_timeout=30)
if not r or r.status_code != 200:
return False, sha256
for i in range(total_chunks):
chunk_data = f.read(chunk_size)
headers = {
"X-Chunk-Index": str(i),
"X-Total-Chunks": str(total_chunks),
"X-Upload-ID": upload_id
}
chunk_success = False
for _ in range(3):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
if r:
chunk_success = True
break
time.sleep(1)
if not chunk_success:
return False, sha256
return True, sha256
return True, sha256
with open(file_path, 'rb') as f:
file_data = f.read()
for _ in range(3):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), file_data)})
if r:
return True, sha256
time.sleep(1)
return False, sha256
except Exception:
return False, None
####################################################################################################
def get_result(self, sha256, api_host, api_key):
def get_result(self, sha256, api_host, api_key, max_retries=6, interval=10):
try:
if not sha256:
return False
self._init_session(api_host, api_key)
base = api_host.rstrip('/')
headers = {"X-API-Key": api_key, "User-Agent": "PYAS-Client/1.0"}
def _req(m, ep, req_timeout, **k):
try:
return requests.request(m, f"{base}{ep}", headers=headers, timeout=req_timeout, **k)
except requests.exceptions.Timeout:
return "timeout"
except Exception:
return None
timeout_count = 0
for _ in range(30):
r = _req("GET", f"/api/processing_status/{sha256}", req_timeout=15)
if r == "timeout" or r is None:
timeout_count += 1
if timeout_count >= 3:
is_done = False
for _ in range(max_retries):
r = self._request("GET", f"/api/processing_status/{sha256}")
if r:
st = r.json().get('status', 'error')
if st == 'done':
is_done = True
break
if st in ['error', 'failed']:
return False
time.sleep(1)
continue
time.sleep(interval)
st = 'error'
if r.status_code == 200:
try:
st = r.json().get('status', 'error')
except Exception:
pass
if not is_done:
return False
if st == 'done':
break
if st in ['error', 'failed']:
return False
time.sleep(1)
r = self._request("GET", f"/api/report/{sha256}")
if r:
data = r.json().get('data', {})
metadata = data.get('metadata', {})
label = metadata.get('label', 'Unsupport')
score = metadata.get('score', 0)
sims = data.get('similar', [])
r = _req("GET", f"/api/report/{sha256}", req_timeout=30)
if r and r != "timeout" and r.status_code == 200:
try:
data = r.json().get('data', {})
res = data.get('detection', {}).get('results', {}).get('PYAS', {})
label = res.get('label', 'Unsupport')
score = res.get('score', 100)
sims = data.get('similar', [])
is_malicious = 'General' in label
sim_malicious_count = 0
valid_sim_count = 0
for s in sims:
if s.get('similarity', 0) > 80:
valid_sim_count += 1
if "General" in s.get('label', ''):
sim_malicious_count += 1
is_malicious = 'General' in label
sim_malicious_count = 0
valid_sim_count = 0
for s in sims:
if s.get('similarity', 0) > 80:
valid_sim_count += 1
if "General" in s.get('label', ''):
sim_malicious_count += 1
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
return f"General:WinPE/Unknown.{score}!cl"
except Exception:
pass
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
return f"General:WinPE/Unknown.{score}!cl"
except Exception:
pass
return False
+1 -1
View File
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
pyinstaller_versionfile.create_versionfile(
output_file="versionfile.txt",
version='3.4.2',
version='3.4.6',
company_name="PYAS Security",
file_description="Python Antivirus Software",
internal_name="PYAS",
+10 -13
View File
@@ -43,25 +43,25 @@ constexpr auto PYAS_POOL_TAG = 'SAYP';
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#endif
#ifndef PROCESS_SET_QUOTA
#define PROCESS_SET_QUOTA (0x0100)
#define PROCESS_SET_QUOTA (0x0100)
#endif
#ifndef THREAD_TERMINATE
#define THREAD_TERMINATE (0x0001)
#define THREAD_TERMINATE (0x0001)
#endif
#ifndef THREAD_SUSPEND_RESUME
#define THREAD_SUSPEND_RESUME (0x0002)
#define THREAD_SUSPEND_RESUME (0x0002)
#endif
#ifndef THREAD_SET_CONTEXT
#define THREAD_SET_CONTEXT (0x0010)
#define THREAD_SET_CONTEXT (0x0010)
#endif
#ifndef THREAD_SET_INFORMATION
#define THREAD_SET_INFORMATION (0x0020)
#define THREAD_SET_INFORMATION (0x0020)
#endif
#ifndef THREAD_SET_THREAD_TOKEN
#define THREAD_SET_THREAD_TOKEN (0x0080)
#define THREAD_SET_THREAD_TOKEN (0x0080)
#endif
#ifndef POOL_FLAG_NON_PAGED
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
#endif
#ifndef FSCTL_MANAGE_BYPASS_IO
@@ -136,6 +136,9 @@ VOID UninitializeRegistryProtection();
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
VOID UnloadRules();
VOID InitializeRulesEngine();
VOID UninitializeRulesEngine();
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
@@ -152,13 +155,7 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
#if (NTDDI_VERSION >= NTDDI_WIN10_VB)
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
#else
PVOID Buffer = ExAllocatePoolWithTag(NonPagedPoolNx, Size, PYAS_POOL_TAG);
if (Buffer) RtlZeroMemory(Buffer, Size);
return Buffer;
#endif
}
FORCEINLINE VOID PyasFree(PVOID Ptr) {
+6
View File
@@ -48,6 +48,7 @@ static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
UnloadRules();
UninitializeRulesEngine();
return STATUS_SUCCESS;
}
@@ -120,11 +121,14 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
ExInitializeRundownProtection(&GlobalData.PortRundown);
InitializeRulesEngine();
status = LoadRulesFromDisk(RegistryPath);
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
UnloadRules();
UninitializeRulesEngine();
return status;
}
@@ -142,6 +146,7 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
GlobalData.FilterHandle = NULL;
}
UnloadRules();
UninitializeRulesEngine();
return status;
}
@@ -166,6 +171,7 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
}
UnloadRules();
UninitializeRulesEngine();
}
return status;
+71 -61
View File
@@ -123,39 +123,38 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_
}
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) {
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
if (CheckProtectedPathRule(&nameInfo->Name)) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_COMPLETE;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
if (CheckFileExtensionRule(&nameInfo->Name)) {
if (CheckRansomActivity(Pid, &nameInfo->Name, NULL, 0, FALSE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_COMPLETE;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
@@ -166,15 +165,13 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) {
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
SECURITY_INFORMATION SecurityInfo = Data->Iopb->Parameters.SetSecurity.SecurityInformation;
@@ -184,18 +181,22 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_COMPLETE;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(CompletionContext);
UNREFERENCED_PARAMETER(FltObjects);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
@@ -207,43 +208,45 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RE
if (WriteLength == 0) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
if (NT_SUCCESS(status) && nameInfo) {
status = FltParseFileNameInformation(nameInfo);
if (NT_SUCCESS(status) && nameInfo->Name.Buffer) {
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
if (CheckFileExtensionRule(&nameInfo->Name)) {
HANDLE Pid = PsGetCurrentProcessId();
if (CheckFileExtensionRule(&nameInfo->Name)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
PVOID SystemBuffer = NULL;
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
if (!IsProcessTrusted(Pid)) {
PVOID SystemBuffer = NULL;
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
if (!Mdl) {
if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
if (NT_SUCCESS(FltLockUserBuffer(Data))) {
Mdl = Data->Iopb->Parameters.Write.MdlAddress;
}
}
}
if (Mdl) {
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
}
if (SystemBuffer) {
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
}
if (!Mdl) {
if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
if (NT_SUCCESS(FltLockUserBuffer(Data))) {
Mdl = Data->Iopb->Parameters.Write.MdlAddress;
}
}
}
if (Mdl) {
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
}
if (SystemBuffer) {
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
@@ -260,24 +263,31 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data,
PFS_BPIO_INPUT InputBuffer = (PFS_BPIO_INPUT)Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer;
if (!InputBuffer) return FLT_PREOP_SUCCESS_NO_CALLBACK;
FS_BPIO_OPERATIONS Operation;
__try {
if (InputBuffer->Operation == FS_BPIO_OP_ENABLE) {
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) {
if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) {
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
Data->IoStatus.Status = STATUS_NOT_SUPPORTED;
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_COMPLETE;
}
}
FltReleaseFileNameInformation(nameInfo);
}
}
Operation = InputBuffer->Operation;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Operation == FS_BPIO_OP_ENABLE) {
if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) {
if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) {
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
Data->IoStatus.Status = STATUS_NOT_SUPPORTED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
}
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
-4
View File
@@ -100,11 +100,9 @@ static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PR
PROCESS_SET_QUOTA;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
}
}
@@ -147,11 +145,9 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
THREAD_SET_THREAD_TOKEN;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~DenyMask;
OperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
}
}
+50 -46
View File
@@ -142,36 +142,38 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName && KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
if (KeyName) {
if (KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
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);
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
@@ -185,36 +187,38 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName && KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
if (KeyName) {
if (KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
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);
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
+14 -13
View File
@@ -101,9 +101,10 @@ static BOOLEAN HasSuffix(PCUNICODE_STRING String, PCWSTR Suffix) {
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
if (!Pattern || !String) return FALSE;
USHORT StringLenChars = StringLengthBytes / sizeof(WCHAR);
PCWSTR mp = NULL;
PCWSTR cp = NULL;
PCWSTR StringEnd = (PCWSTR)((PUCHAR)String + StringLengthBytes);
PCWSTR StringEnd = String + StringLenChars;
while (String < StringEnd) {
if (*Pattern == L'*') {
@@ -248,6 +249,18 @@ static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyN
}
}
VOID InitializeRulesEngine() {
ExInitializeResourceLite(&RuleLock);
ExInitializeFastMutex(&TrustCacheLock);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
g_CacheInitialized = TRUE;
}
VOID UninitializeRulesEngine() {
ExDeleteResourceLite(&RuleLock);
g_CacheInitialized = FALSE;
}
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
NTSTATUS status = STATUS_SUCCESS;
HANDLE FileHandle = NULL;
@@ -263,7 +276,6 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
UNICODE_STRING FinalPath = { 0 };
RtlInitUnicodeString(&ImagePathName, L"ImagePath");
ExInitializeResourceLite(&RuleLock);
InitializeObjectAttributes(&RegOa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwOpenKey(&RegHandle, KEY_READ, &RegOa);
@@ -395,7 +407,6 @@ VOID UnloadRules() {
FreeList(&g_FileExceptionPaths);
FreeList(&g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
ExDeleteResourceLite(&RuleLock);
}
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) {
@@ -412,13 +423,6 @@ NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) {
return status;
}
static VOID InitTrustCache() {
if (g_CacheInitialized) return;
ExInitializeFastMutex(&TrustCacheLock);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
g_CacheInitialized = TRUE;
}
static BOOLEAN IsWindowsSystemApp(PCWSTR Buffer, USHORT Length) {
if (WildcardMatch(L"*\\Windows\\SystemApps\\*", Buffer, Length)) return TRUE;
if (WildcardMatch(L"*\\Windows\\ImmersiveControlPanel\\*", Buffer, Length)) return TRUE;
@@ -452,9 +456,6 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
}
ExReleaseFastMutex(&TrustCacheLock);
}
else {
InitTrustCache();
}
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
+39 -43
View File
@@ -3,11 +3,7 @@
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Image File Execution Options\\*",
"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\NetWire\\*",
"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\Remcos*\\*",
"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\DC3_FEXEC\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
@@ -19,7 +15,6 @@
"*\\mouclass",
"*\\kbdclass",
"*\\DefaultIcon",
"*\\DisableAntiSpyware",
"*\\DisableWindowsUpdateAccess",
"*\\EnableLUA",
@@ -79,7 +74,6 @@
"*\\NoInteractiveServices",
"*\\ShutdownWithoutLogon",
"*\\AutoAdminLogon",
"*\\ImagePath",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
@@ -89,7 +83,6 @@
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\PendingFileRenameOperations",
"*\\SystemSetupInProgress",
"*\\CmdLine",
"*\\SetupType",
@@ -108,17 +101,30 @@
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"*\\Windows\\*",
"*\\Windows Defender\\*",
"*\\Windows Defender Advanced Threat Protection\\*",
"*\\Program Files\\*",
"*\\Program Files (x86)\\*",
"\\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"
"*\\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",
@@ -153,31 +159,24 @@
"*\\taskkill.exe",
"*\\ProcessHacker.exe",
"*\\procmon.exe",
"*\\procexp.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",
"*\\bootmgr",
"*\\boot.ini",
"*\\BOOTNXT",
"*\\BCD",
"*\\EFI\\*",
"*\\Boot\\*",
"*\\Recovery\\*",
"*\\Windows\\*.exe",
"*\\Windows\\*.dll",
"*\\Windows\\*.sys",
"*\\Windows\\System32\\*.exe",
"*\\Windows\\System32\\*.dll",
"*\\Windows\\System32\\*.sys",
"*\\Windows\\SysWOW64\\*.exe",
"*\\Windows\\SysWOW64\\*.dll",
"*\\Windows\\SysWOW64\\*.sys",
"*\\System Volume Information\\*",
"\\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\\*",
@@ -191,29 +190,26 @@
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
"*\\*:*",
],
"Rule_File_ExceptionPaths": [
"*\\AppData\\*"
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
],
"Rule_File_RansomwareExtensions": [
".exe", ".dll", ".sys", ".com", ".scr",
".zip", ".7z", ".rar", ".tar", ".gz",
".js", ".bat", ".cmd", ".ps1", ".vbs",
".ppt", ".pptx", ".wps", ".txt", ".rtf",
".pdf", ".xls", ".xlsx", ".doc", ".docx",
".jpg", ".jpeg", ".png", ".webp", ".gif",
".mp3", ".wav", ".aac", ".ogg", ".flac",
".mp4", ".avi", ".mov", ".wmv", ".mkv",
".aux", ".cur", ".mui", ".ttf", ".efi",
".cpp", ".h", ".c", ".hpp", ".cs", ".java", ".py", ".go", ".rs",
".php", ".html", ".css", ".xml", ".sql", ".db"
]
}
+10 -2
View File
@@ -26,6 +26,10 @@ 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
@@ -35,7 +39,11 @@ PYAS/
│ ├── Heuristic/
│ │ ├── rules.yar # Yara virus signature rule matching
│ │ └── ... # Other rules folders and files
│ └── ... # Other engine folders and files
│ │
│ └── Properties/
│ ├── convert.py # Convert executable files to sql database
│ ├── train.py # Lightgbm model training complete code
│ └── ... # Other models folders and files
│
├── Plugins/
│ └── Filter/
@@ -194,4 +202,4 @@ For any questions, needs, or bug feedback, please contact us through the followi
Source Issues : https://github.com/87owo/PYAS/issues
Official Email : mailto:service.pyas@gmail.com
Official Email : service@pyas-security.com