From 4eabd04bc6c0edd176112ee3d48a28fb6073bd53 Mon Sep 17 00:00:00 2001 From: 87owo <85057800+87owo@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:44:24 +0800 Subject: [PATCH] Add files via upload --- PYAS.py | 51 +++++--- PYAS_Engine.py | 339 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 361 insertions(+), 29 deletions(-) diff --git a/PYAS.py b/PYAS.py index d72b810..864db34 100644 --- a/PYAS.py +++ b/PYAS.py @@ -120,8 +120,9 @@ class MainWindow_Controller(QMainWindow): self.sign = sign_scanner() self.sign.init_windll(["wintrust"]) - self.model = model_scanner() - self.rule = rule_scanner() + self.pattern = cnn_scanner() + self.heuristic = rule_scanner() + self.properties = pe_scanner() self.cloud = cloud_scanner() self.cloud_queue = queue.Queue() self.init_cloud_worker() @@ -149,8 +150,8 @@ class MainWindow_Controller(QMainWindow): "api_host": "https://pyas-security.com/", "api_key": "fBRZxYS1UxykM-qzNOlKOEl63WILzlvgNMn6QfsG6FXCAAIktCrOPTAfY5_hEyuZ", "suffix": [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui", ".cpl"], + "block": [2001, 3001, 4001, 5001, 6001], "size": 256 * 1024 * 1024, - "sensitive": 95, "language": "english_switch", "theme": "white_switch", "process_switch": True, @@ -158,6 +159,8 @@ class MainWindow_Controller(QMainWindow): "system_switch": True, "driver_switch": True, "network_switch": True, + "extension_switch": False, + "sensitive_switch": False, "custom_rule": [], } self.pass_windows = [ @@ -187,7 +190,6 @@ class MainWindow_Controller(QMainWindow): 5001: "RANSOM_BLOCK", 6001: "INJECT_BLOCK" } - self.kill_codes = {4001, 5001} #################################################################################################### @@ -206,8 +208,9 @@ class MainWindow_Controller(QMainWindow): self.path_systemp = os.path.join(self.path_system, "Temp") self.file_config = os.path.join(self.path_config, "PYAS", "Config.json") - self.path_models = os.path.join(self.path_pyas, "Engine", "Models") - self.path_rules = os.path.join(self.path_pyas, "Engine", "Rules") + self.path_properties = os.path.join(self.path_pyas, "Engine", "Properties") + self.path_pattern = os.path.join(self.path_pyas, "Engine", "Pattern") + self.path_heuristic = os.path.join(self.path_pyas, "Engine", "Heuristic") self.path_protect = os.path.join(self.path_pyas, "Plugins", "Filter") self.path_drivers = os.path.join(self.path_protect, "PYAS_Driver.sys") @@ -305,8 +308,9 @@ class MainWindow_Controller(QMainWindow): def init_engine_thread(self): try: - self.rule.load_path(self.path_rules, callback=lambda x: self.init_log_signal.emit(os.path.basename(x))) - self.model.load_path(self.path_models, callback=lambda x: self.init_log_signal.emit(os.path.basename(x))) + self.heuristic.load_path(self.path_heuristic, callback=lambda x: self.init_log_signal.emit(os.path.basename(x))) + self.pattern.load_path(self.path_pattern, callback=lambda x: self.init_log_signal.emit(os.path.basename(x))) + self.properties.load_path(self.path_properties, callback=lambda x: self.init_log_signal.emit(os.path.basename(x))) self.init_log_signal.emit("引擎加載完成") except Exception as e: self.send_message(f"init_engine_thread | {e}", "warn", False) @@ -401,7 +405,7 @@ class MainWindow_Controller(QMainWindow): def save_state(self, name, checked): self.pyas_config[name] = bool(checked) if name == "sensitive_switch": - self.pyas_config["sensitive"] = 0 if checked else 95 + pass elif name == "extension_switch": pass elif name == "process_switch": @@ -899,22 +903,27 @@ class MainWindow_Controller(QMainWindow): def scan_engine(self, file_path): try: - result = False + if self.sign.sign_verify(file_path): + return False - label, level = self.model.model_scan(file_path) - if label and level >= self.pyas_config.get("sensitive", 95): - if not self.sign.sign_verify(file_path): - result = f"{label}.{level}" + pe_label, pe_score = self.properties.pe_scan(file_path) + if pe_label: + return pe_label if self.pyas_config.get("extension_switch", False): - types, detail = self.rule.yara_scan(file_path) - if types and detail: - result = f"{types}.{detail}" + yara_label, yara_level = self.heuristic.yara_scan(file_path) + if yara_label: + return yara_label - return result + if self.pyas_config.get("sensitive_switch", False): + cnn_label, cnn_score = self.pattern.model_scan(file_path) + if cnn_label: + return cnn_label + + return False except Exception as e: self.send_message(f"scan_engine | {e}", "warn", False) - return False + return Falses #################################################################################################### @@ -2019,7 +2028,7 @@ class MainWindow_Controller(QMainWindow): file_path = self.norm_path(self.get_process_file(h)) if file_path and not self.is_in_whitelist(file_path): - if hasattr(self.rule, "network") and remote_ip in self.rule.network: + if hasattr(self.heuristic, "network") and remote_ip in self.heuristic.network: self.kernel32.TerminateProcess(h, 0) self.send_message(f"網路防護 | 規則列表攔截 | {pid} | {file_path} | {remote_ip}", "notify", True) self.kernel32.CloseHandle(h) @@ -2117,7 +2126,7 @@ class MainWindow_Controller(QMainWindow): display_rule = self.block_replace.get(rule_name, rule_name) self.send_message(f"驅動防護 | {display_rule} | {pid} | {raw_path} | {target}", "notify", True) - if code in self.kill_codes and not self.is_in_whitelist(raw_path): + if code in self.pyas_config.get("block", []) and not self.is_in_whitelist(raw_path): try: h = self.kernel32.OpenProcess(0x1F0FFF, False, pid) if h: diff --git a/PYAS_Engine.py b/PYAS_Engine.py index 1a07469..2a6b0bd 100644 --- a/PYAS_Engine.py +++ b/PYAS_Engine.py @@ -1,4 +1,4 @@ -import os, yara, time, numpy, base64, requests +import os, yara, time, numpy, base64, requests, pefile, math import ctypes, ctypes.wintypes, hashlib, onnxruntime from PIL import Image @@ -94,6 +94,8 @@ class rule_scanner: if yara_files: self.compile_all_rules(yara_files) +#################################################################################################### + def load_compiled_rule(self, file): try: self.rules = yara.load(file) @@ -113,6 +115,8 @@ class rule_scanner: except Exception: pass +#################################################################################################### + def yara_scan(self, file_path): try: if not self.rules: @@ -124,20 +128,22 @@ class rule_scanner: types = rule_name.split("_")[0] label = rule_name.split("_")[1] level = rule_name.split("_")[2] - return f"{types}/{label}", level + return f"{label}:WinPE/Unknown.A!rb", level return False, False except Exception: return False, False #################################################################################################### -class model_scanner: +class cnn_scanner: def __init__(self): self.models = [] - self.labels = ["Pefile/White", "Pefile/General"] - self.detect_set = {"Pefile/General"} + self.labels = ["White:WinPE/Unknown", "General:WinPE/Unknown"] + self.detect_set = {"General:WinPE/Unknown"} self.resize = (224, 224) +#################################################################################################### + def load_path(self, path, callback=None): for root, _, files in os.walk(path): for file in files: @@ -149,11 +155,13 @@ class model_scanner: def load_file(self, file): if file.lower().endswith('.onnx'): try: - session = onnxruntime.InferenceSession(file) + session = onnxruntime.InferenceSession(file, providers=['CPUExecutionProvider']) self.models.append(session) except Exception: return False +#################################################################################################### + def model_scan(self, file_path, full_output=False): if not self.models: return (False, False) if not full_output else ([], str(file_path), None) @@ -199,7 +207,7 @@ class model_scanner: if label in self.detect_set: if best_malicious is None or conf > best_malicious[1]: - best_malicious = (label, int(conf)) + best_malicious = (f"{label}.{int(conf)}!dl", int(conf)) if full_output: results.sort(key=lambda x: (x[1] not in self.detect_set, -x[2])) @@ -209,6 +217,8 @@ class model_scanner: return best_malicious if best_malicious else (False, False) +#################################################################################################### + def pil_to_base64(self, img): buf = BytesIO() img.save(buf, format='PNG') @@ -232,6 +242,315 @@ class model_scanner: #################################################################################################### +class pe_scanner: + def __init__(self): + self.model = None + self.input_name = None + self.signer = sign_scanner() + self.signer.init_windll(["wintrust"]) + self.SCHEMA = [ + "FileEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit", "Machine", "Magic", + "TimeDateStamp", "CheckSum", "ImageBase", "SizeOfImage", "SizeOfHeaders", + "Characteristics", "DllCharacteristics", "Subsystem", "LoaderFlags", + "MajorLinkerVersion", "MinorLinkerVersion", "SizeOfCode", + "SizeOfInitializedData", "SizeOfUninitializedData", "AddressOfEntryPoint", + "BaseOfCode", "SectionAlignment", "FileAlignment", + "MajorOperatingSystemVersion", "MinorOperatingSystemVersion", + "MajorImageVersion", "MinorImageVersion", + "MajorSubsystemVersion", "MinorSubsystemVersion", + "SizeOfStackReserve", "SizeOfStackCommit", "SizeOfHeapReserve", "SizeOfHeapCommit", + "NumberOfSections", "NumberOfRvaAndSizes", "PointerToSymbolTable", "NumberOfSymbols", + "SizeOfOptionalHeader", + "TextSection", "TextSizeRatio", "DataSection", "DataSizeRatio", + "RsrcSection", "RsrcSizeRatio", "SectionCount", "ExecutableSections", + "WritableSections", "ReadableSections", "SectionException", + "IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount", + "FileDescriptionLength", "FileVersionLength", "ProductNameLength", + "ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength", + "CommentsLength", "InternalNameLength", "LegalTrademarksLength", + "SpecialBuildLength", "PrivateBuildLength", + "TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease", + "IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks", + "HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException", + "_CorExeMain", "_CorDllMain", + "GetDC", "CreateDCW", "CreateDCA", "BitBlt", "StretchBlt", "CreateCompatibleDC", + "CreateCompatibleBitmap", "SelectObject", "DeleteDC", "DeleteObject", "GetDeviceCaps", + "GetSystemMetrics", + "SetWindowsHookExA", "SetWindowsHookExW", "UnhookWindowsHookEx", + "GetAsyncKeyState", "GetKeyState", "GetKeyboardState", + "MapVirtualKeyA", "MapVirtualKeyW", "MapVirtualKeyExA", "MapVirtualKeyExW", + "ToAscii", "ToAsciiEx", "ToUnicode", "ToUnicodeEx", "KeybdEvent", "SendInput", + "GetCursorPos", "SetCursorPos", "MouseEvent", "GetDoubleClickTime", + "GetCapture", "SetCapture", + "WaveInOpen", "WaveInClose", "WaveInStart", "WaveInStop", + "CapCreateCaptureWindowA", "CapCreateCaptureWindowW", + "OpenClipboard", "CloseClipboard", "GetClipboardData", "SetClipboardData", + "GetDesktopWindow", "GetForegroundWindow", "GetWindowDC", "GetWindowRect", + "GetClientRect", "PrintWindow", "SetWindowDisplayAffinity", "FindWindowA", "FindWindowW", + "EnumWindows", "EnumChildWindows", + "Direct3DCreate9", "D3D11CreateDevice", "GdipSaveImageToFile", + "CreateProcessA", "CreateProcessW", "CreateThread", "ResumeThread", "SuspendThread", + "OpenProcess", "TerminateProcess", "GetCurrentProcess", "GetCurrentProcessId", + "ExitProcess", "WinExec", "ShellExecuteA", "ShellExecuteW", + "CreateRemoteThread", "QueueUserAPC", "VirtualAlloc", "VirtualAllocEx", + "VirtualProtect", "VirtualProtectEx", "WriteProcessMemory", "ReadProcessMemory", + "NtUnmapViewOfSection", "SetThreadContext", "GetThreadContext", "Wow64SetThreadContext", + "ZwUnmapViewOfSection", "CreateFileMappingA", "CreateFileMappingW", "MapViewOfFile", + "UnmapViewOfSection", + "LoadLibraryA", "LoadLibraryW", "LoadLibraryExA", "LoadLibraryExW", + "GetProcAddress", "GetModuleHandleA", "GetModuleHandleW", "FreeLibrary", + "CreateFileA", "CreateFileW", "WriteFile", "ReadFile", "DeleteFileA", "DeleteFileW", + "CopyFileA", "CopyFileW", "MoveFileA", "MoveFileW", "FindFirstFileA", "FindNextFileA", + "GetTempPathA", "GetTempPathW", "GetTempFileNameA", "GetTempFileNameW", + "SetFileAttributesA", "SetFileAttributesW", "DeviceIoControl", "SetFileTime", + "RegOpenKeyExA", "RegOpenKeyExW", "RegSetValueExA", "RegSetValueExW", + "RegCreateKeyExA", "RegCreateKeyExW", "RegDeleteKeyA", "RegDeleteKeyW", + "RegEnumValueA", "RegEnumValueW", "RegQueryValueExA", "RegQueryValueExW", + "RegDeleteValueA", "RegDeleteValueW", + "OpenSCManagerA", "OpenSCManagerW", "CreateServiceA", "CreateServiceW", + "StartServiceA", "StartServiceW", "ControlService", "DeleteService", + "AdjustTokenPrivileges", "LookupPrivilegeValueA", "LookupPrivilegeValueW", + "OpenProcessToken", "NetUserAdd", "NetLocalGroupAddMembers", + "Socket", "Connect", "Send", "Recv", "WSAStartup", "gethostbyname", "getaddrinfo", + "WSAIoctl", "WSASocketA", "WSASocketW", + "URLDownloadToFileA", "URLDownloadToFileW", + "InternetOpenA", "InternetOpenW", "InternetConnectA", "InternetConnectW", + "InternetOpenUrlA", "InternetOpenUrlW", + "HttpOpenRequestA", "HttpOpenRequestW", "HttpSendRequestA", "HttpSendRequestW", + "InternetReadFile", "DnsQuery_A", "DnsQuery_W", + "IsDebuggerPresent", "CheckRemoteDebuggerPresent", "OutputDebugStringA", "OutputDebugStringW", + "GetTickCount", "QueryPerformanceCounter", "Sleep", "GetSystemTimeAsFileTime", + "GetLocalTime", "GlobalMemoryStatus", "GetVersionExA", "GetVersionExW", + "GetComputerNameA", "GetComputerNameW", "GetUserNameA", "GetUserNameW", + "CryptAcquireContextA", "CryptAcquireContextW", "CryptCreateHash", "CryptHashData", + "CryptDeriveKey", "CryptEncrypt", "CryptDecrypt", "CryptDestroyKey", "CryptDestroyHash", + "CryptReleaseContext", "CryptGenKey", "CryptImportKey", "CryptExportKey", + "RtlDecompressBuffer", "ConnectNamedPipe", "PeekNamedPipe" + ] + self.PACKERS = { + 'upx', 'aspack', 'asprotect', 'pecompact', 'upack', 'fsg', 'mew', + 'mpress', 'ezip', 'pklt', 'shrink', 'petite', 'telock', + 'themida', 'winlicense', 'tmd', 'vmp', 'enigma', 'obsidium', + 'pelock', 'exestealth', 'yoda', 'armadillo', 'zprotect', + 'sforce', 'starforce', 'qihoo', 'wisevec', 'megastop', + } + self.INSTALLER_SIGS = [b"Nullsoft.NSIS", b"Inno Setup", b"7-Zip.7zip", b"InstallShield"] + self.SCHEMA_SET = set(self.SCHEMA) + +#################################################################################################### + + def load_path(self, path, callback=None): + for root, _, files in os.walk(path): + for file in files: + full_path = os.path.join(root, file) + if callback: + callback(full_path) + self.load_model(full_path) + + def load_model(self, model_path): + if not os.path.exists(model_path) or not model_path.endswith('.onnx'): + return + try: + self.model = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider']) + self.input_name = self.model.get_inputs()[0].name + except Exception: + pass + +#################################################################################################### + + def _safe_float(self, val): + try: + f = float(val) + if math.isinf(f) or math.isnan(f): return 0.0 + return f + except: + return 0.0 + + def _calc_entropy(self, data): + if not data: return 0.0 + arr = numpy.frombuffer(data, dtype=numpy.uint8) + counts = numpy.bincount(arr, minlength=256) + probs = counts[counts > 0] / len(arr) + return float(-numpy.sum(probs * numpy.log2(probs))) + +#################################################################################################### + + def extract_features(self, file_path): + fts = {k: 0 for k in self.SCHEMA} + pe = None + try: + fsize = os.path.getsize(file_path) + if fsize == 0: return None + + pe = pefile.PE(file_path, fast_load=True) + fts['TrustSigned'] = 1 if self.signer.sign_verify(file_path) else 0 + + fh = pe.FILE_HEADER + fts['Machine'] = fh.Machine + fts['NumberOfSections'] = fh.NumberOfSections + fts['TimeDateStamp'] = fh.TimeDateStamp + fts['PointerToSymbolTable'] = fh.PointerToSymbolTable + fts['NumberOfSymbols'] = fh.NumberOfSymbols + fts['SizeOfOptionalHeader'] = fh.SizeOfOptionalHeader + fts['Characteristics'] = fh.Characteristics + + curr_ts = time.time() + fts['HasInvalidTimestamp'] = 1 if (fh.TimeDateStamp < 631152000 or fh.TimeDateStamp > curr_ts + 2592000) else 0 + + if hasattr(pe, 'OPTIONAL_HEADER'): + op = pe.OPTIONAL_HEADER + fts['Magic'] = op.Magic + fts['MajorLinkerVersion'] = op.MajorLinkerVersion + fts['MinorLinkerVersion'] = op.MinorLinkerVersion + fts['SizeOfCode'] = op.SizeOfCode + fts['SizeOfInitializedData'] = op.SizeOfInitializedData + fts['SizeOfUninitializedData'] = op.SizeOfUninitializedData + fts['AddressOfEntryPoint'] = op.AddressOfEntryPoint + fts['BaseOfCode'] = op.BaseOfCode + fts['ImageBase'] = op.ImageBase + fts['SectionAlignment'] = op.SectionAlignment + fts['FileAlignment'] = op.FileAlignment + fts['MajorOperatingSystemVersion'] = op.MajorOperatingSystemVersion + fts['MinorOperatingSystemVersion'] = op.MinorOperatingSystemVersion + fts['MajorImageVersion'] = op.MajorImageVersion + fts['MinorImageVersion'] = op.MinorImageVersion + fts['MajorSubsystemVersion'] = op.MajorSubsystemVersion + fts['MinorSubsystemVersion'] = op.MinorSubsystemVersion + fts['SizeOfImage'] = op.SizeOfImage + fts['SizeOfHeaders'] = op.SizeOfHeaders + fts['CheckSum'] = op.CheckSum + fts['Subsystem'] = op.Subsystem + fts['DllCharacteristics'] = op.DllCharacteristics + fts['SizeOfStackReserve'] = op.SizeOfStackReserve + fts['SizeOfStackCommit'] = op.SizeOfStackCommit + fts['SizeOfHeapReserve'] = op.SizeOfHeapReserve + fts['SizeOfHeapCommit'] = op.SizeOfHeapCommit + fts['LoaderFlags'] = op.LoaderFlags + fts['NumberOfRvaAndSizes'] = op.NumberOfRvaAndSizes + + fts['Is64Bit'] = 1 if fh.Machine == 0x8664 else 0 + fts['IsExe'] = 1 if pe.is_exe() else 0 + fts['IsDll'] = 1 if pe.is_dll() else 0 + fts['IsDriver'] = 1 if pe.is_driver() else 0 + + if len(op.DATA_DIRECTORY) > 9 and op.DATA_DIRECTORY[9].VirtualAddress > 0: + fts['HasTlsCallbacks'] = 1 + if len(op.DATA_DIRECTORY) > 5 and op.DATA_DIRECTORY[5].Size > 0: + fts['HasRelocationDirectory'] = 1 + + pe.parse_sections(pe.FILE_HEADER.NumberOfSections) + file_data = pe.get_memory_mapped_image() + fts['FileEntropy'] = self._calc_entropy(file_data) + fts['SectionCount'] = len(pe.sections) + + for section in pe.sections: + try: + name = section.Name.decode('ascii', 'ignore').strip('\x00') + except: + name = "" + s_data = section.get_data() + s_entropy = self._calc_entropy(s_data) + + if any(p in name.lower() for p in self.PACKERS): + fts['HasPacked'] = 1 + if section.Characteristics & 0x20000000: + fts['ExecutableSections'] += 1 + if section.Characteristics & 0x80000000: + fts['WritableSections'] += 1 + if section.Characteristics & 0x40000000: + fts['ReadableSections'] += 1 + if section.SizeOfRawData + section.PointerToRawData > fsize: + fts['SectionException'] = 1 + + ratio = section.SizeOfRawData / fsize if fsize > 0 else 0 + if name.startswith('.text'): + fts['TextSection'] = s_entropy + fts['TextSizeRatio'] = ratio + elif name.startswith('.data'): + fts['DataSection'] = s_entropy + fts['DataSizeRatio'] = ratio + elif name.startswith('.rsrc'): + fts['RsrcSection'] = s_entropy + fts['RsrcSizeRatio'] = ratio + if b"requireAdministrator" in s_data: + fts['IsAdmin'] = 1 + for sig in self.INSTALLER_SIGS: + if sig in s_data: + fts['IsInstall'] = 1 + break + + pe.parse_data_directories(directories=[ + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'], + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'], + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'], + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'] + ]) + + if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): + for entry in pe.DIRECTORY_ENTRY_IMPORT: + for imp in entry.imports: + fts['ApiCount'] += 1 + if not imp or not hasattr(imp, 'name') or not imp.name: + continue + try: + name = imp.name.decode('ascii', 'ignore') + if name in self.SCHEMA_SET: + fts[name] = 1 + except: + pass + + if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'): + fts['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols) + + if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'): + for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries: + if entry.id == 3 and hasattr(entry, 'directory'): + fts['IconCount'] = len(entry.directory.entries) + break + + if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'): + fts['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG) + + return {k: self._safe_float(v) for k, v in fts.items()} + + except Exception: + return None + finally: + if pe: pe.close() + +#################################################################################################### + + def pe_scan(self, file_path): + if not self.model: + return False, False + + try: + data = self.extract_features(file_path) + if not data: + return False, False + + vec = numpy.array( + [data.get(feat, 0.0) for feat in self.SCHEMA], + dtype=numpy.float32 + ).reshape(1, -1) + + outputs = self.model.run(None, {self.input_name: vec}) + result = outputs[1] + prob = 0.0 + if isinstance(result, list): + prob = float(result[0].get(1, 0.0)) + + score = int(prob * 100) + if prob > 0.5: + return f"General:WinPE/Unknown.{score}!ml", score + + return False, False + + except Exception: + return False, False + +#################################################################################################### + class cloud_scanner: def __init__(self): pass @@ -246,6 +565,8 @@ class cloud_scanner: except Exception: return "" +#################################################################################################### + def upload_file(self, file_path, api_host, api_key): try: sha256 = self.calc_hash(file_path) @@ -276,6 +597,8 @@ class cloud_scanner: except Exception: return False, None +#################################################################################################### + def get_result(self, sha256, api_host, api_key): try: if not sha256: @@ -316,7 +639,7 @@ class cloud_scanner: sim_malicious_count += 1 if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count): - return True + return f"General:WinPE/Unknown.{score}!cl" except Exception: pass return False