Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
630dc67058 | ||
|
|
591507512c | ||
|
|
ff49cafc74 | ||
|
|
b077ba06bd | ||
|
|
a2b66aec64 | ||
|
|
4b692b0c87 | ||
|
|
99d1b20d0f | ||
|
|
fae5edb57b | ||
|
|
6db91483a0 | ||
|
|
90383f3d09 | ||
|
|
966a5a3592 | ||
|
|
48e14cd143 | ||
|
|
4264f50ba3 | ||
|
|
887e0718d0 | ||
|
|
4870a5c480 | ||
|
|
e8cadf0537 | ||
|
|
f5124e0ef2 | ||
|
|
da9a520632 | ||
|
|
81bebeda63 | ||
|
|
4df1432be1 | ||
|
|
e699be6b2e | ||
|
|
e8dda40e7f | ||
|
|
5b4cd6e879 | ||
|
|
316b061d34 | ||
|
|
6ff27679e7 | ||
|
|
8f3adbc870 | ||
|
|
5798a35456 | ||
|
|
a8a2060978 | ||
|
|
7cf2763511 | ||
|
|
fc59619518 | ||
|
|
d01cfd2dd6 | ||
|
|
9563a4806a | ||
|
|
fa1be109e3 | ||
|
|
89a5a5928c | ||
|
|
7830378f23 | ||
|
|
043f616469 | ||
|
|
3bf631313a | ||
|
|
a416e2d4a8 |
No files matched your search
+1101
-744
File diff suppressed because it is too large.
Load diff
+1156
-798
File diff suppressed because it is too large.
Load diff
@@ -58,15 +58,14 @@ def analyze_schema(jsonl_path):
|
||||
for line in f:
|
||||
num_lines += 1
|
||||
|
||||
if not base_keys:
|
||||
try:
|
||||
data = JSON_LOADS(line)
|
||||
base_data = data.get('Base')
|
||||
if base_data:
|
||||
base_keys.update(base_data.keys())
|
||||
try:
|
||||
data = JSON_LOADS(line)
|
||||
base_data = data.get('Base')
|
||||
if base_data:
|
||||
base_keys.update(base_data.keys())
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
|
||||
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
|
||||
|
||||
@@ -58,15 +58,14 @@ def analyze_schema(jsonl_path):
|
||||
for line in f:
|
||||
num_lines += 1
|
||||
|
||||
if not base_keys:
|
||||
try:
|
||||
data = JSON_LOADS(line)
|
||||
base_data = data.get('Base')
|
||||
if base_data:
|
||||
base_keys.update(base_data.keys())
|
||||
try:
|
||||
data = JSON_LOADS(line)
|
||||
base_data = data.get('Base')
|
||||
if base_data:
|
||||
base_keys.update(base_data.keys())
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
|
||||
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
* {
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -14,6 +14,7 @@
|
||||
--bg-hover: rgba(0, 0, 0, 0.05);
|
||||
--text-primary: rgb(29, 29, 31);
|
||||
--text-secondary: rgb(134, 134, 139);
|
||||
--text-on-accent: rgb(255, 255, 255);
|
||||
--border-color: rgb(210, 210, 215);
|
||||
--accent-color: rgb(0, 122, 255);
|
||||
--accent-alpha: rgba(0, 122, 255, 0.1);
|
||||
@@ -82,7 +83,7 @@ body {
|
||||
}
|
||||
|
||||
.ctrl-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
|
||||
#close_button:hover { background-color: #ff3b30; color: white; }
|
||||
#close_button:hover { background-color: #ff3b30; color: var(--text-on-accent); }
|
||||
|
||||
.theme-svg {
|
||||
width: 16px;
|
||||
@@ -258,7 +259,7 @@ body {
|
||||
|
||||
.primary-btn {
|
||||
background-color: var(--accent-color);
|
||||
color: white;
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
box-shadow: 0 2px 4px var(--accent-shadow);
|
||||
}
|
||||
|
||||
+29
-19
@@ -1,4 +1,4 @@
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
document.addEventListener(eventName, e => {
|
||||
e.preventDefault();
|
||||
});
|
||||
@@ -88,9 +88,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
setTimeout(() => {
|
||||
if (!startupOverlayClosed) {
|
||||
closeStartupOverlay();
|
||||
showStartupError("PYAS WebView2 initialization is taking too long. The application will retry automatically.");
|
||||
}
|
||||
}, 12000);
|
||||
}, 20000);
|
||||
|
||||
const formatMsg = (key, ...args) => {
|
||||
let msg = getMsg(key);
|
||||
@@ -104,12 +104,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const themes = {
|
||||
"white_switch": { "--bg-window": "245, 245, 247", "--bg-nav": "255, 255, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,0,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"210, 210, 215", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" },
|
||||
"black_switch": { "--bg-window": "0, 0, 0", "--bg-nav": "28, 28, 30", "--bg-panel": "28, 28, 30", "--bg-hover": "rgba(255,255,255,0.1)", "--text-primary": "245, 245, 247", "--text-secondary": "134, 134, 139", "--border-color":"44, 44, 46", "--accent-color":"10, 132, 255", "--accent-alpha":"rgba(10, 132, 255, 0.1)", "--accent-shadow":"rgba(10, 132, 255, 0.2)", "--accent-hover":"rgba(10, 132, 255, 0.3)" },
|
||||
"red_switch": { "--bg-window": "253, 246, 246", "--bg-nav": "255, 240, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,59,48,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"250, 220, 220", "--accent-color":"255, 59, 48", "--accent-alpha":"rgba(255, 59, 48, 0.1)", "--accent-shadow":"rgba(255, 59, 48, 0.2)", "--accent-hover":"rgba(255, 59, 48, 0.3)" },
|
||||
"yellow_switch": { "--bg-window": "254, 251, 243", "--bg-nav": "255, 248, 230", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,149,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"250, 240, 215", "--accent-color":"255, 149, 0", "--accent-alpha":"rgba(255, 149, 0, 0.1)", "--accent-shadow":"rgba(255, 149, 0, 0.2)", "--accent-hover":"rgba(255, 149, 0, 0.3)" },
|
||||
"green_switch": { "--bg-window": "246, 253, 248", "--bg-nav": "235, 250, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(52,199,89,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"220, 240, 225", "--accent-color":"52, 199, 89", "--accent-alpha":"rgba(52, 199, 89, 0.1)", "--accent-shadow":"rgba(52, 199, 89, 0.2)", "--accent-hover":"rgba(52, 199, 89, 0.3)" },
|
||||
"blue_switch": { "--bg-window": "246, 249, 253", "--bg-nav": "235, 244, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,122,255,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"220, 230, 250", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" }
|
||||
"white_switch": { "--bg-window": "245, 245, 247", "--bg-nav": "255, 255, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,0,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--text-on-accent": "255, 255, 255", "--border-color":"210, 210, 215", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" },
|
||||
"black_switch": { "--bg-window": "0, 0, 0", "--bg-nav": "28, 28, 30", "--bg-panel": "28, 28, 30", "--bg-hover": "rgba(255,255,255,0.1)", "--text-primary": "216, 216, 220", "--text-secondary": "134, 134, 139", "--text-on-accent": "216, 216, 220", "--border-color":"44, 44, 46", "--accent-color":"10, 132, 255", "--accent-alpha":"rgba(10, 132, 255, 0.1)", "--accent-shadow":"rgba(10, 132, 255, 0.2)", "--accent-hover":"rgba(10, 132, 255, 0.3)" },
|
||||
"red_switch": { "--bg-window": "253, 246, 246", "--bg-nav": "255, 240, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,59,48,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--text-on-accent": "255, 255, 255", "--border-color":"250, 220, 220", "--accent-color":"255, 59, 48", "--accent-alpha":"rgba(255, 59, 48, 0.1)", "--accent-shadow":"rgba(255, 59, 48, 0.2)", "--accent-hover":"rgba(255, 59, 48, 0.3)" },
|
||||
"yellow_switch": { "--bg-window": "254, 251, 243", "--bg-nav": "255, 248, 230", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,149,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--text-on-accent": "255, 255, 255", "--border-color":"250, 240, 215", "--accent-color":"255, 149, 0", "--accent-alpha":"rgba(255, 149, 0, 0.1)", "--accent-shadow":"rgba(255, 149, 0, 0.2)", "--accent-hover":"rgba(255, 149, 0, 0.3)" },
|
||||
"green_switch": { "--bg-window": "246, 253, 248", "--bg-nav": "235, 250, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(52,199,89,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--text-on-accent": "255, 255, 255", "--border-color":"220, 240, 225", "--accent-color":"52, 199, 89", "--accent-alpha":"rgba(52, 199, 89, 0.1)", "--accent-shadow":"rgba(52, 199, 89, 0.2)", "--accent-hover":"rgba(52, 199, 89, 0.3)" },
|
||||
"blue_switch": { "--bg-window": "246, 249, 253", "--bg-nav": "235, 244, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,122,255,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--text-on-accent": "255, 255, 255", "--border-color":"220, 230, 250", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" }
|
||||
};
|
||||
|
||||
const cols = {
|
||||
@@ -1120,8 +1120,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
renderDataGrid('#log_export_window', [], cols.log, 'id', true);
|
||||
renderDataGrid('#netmon_window', [], cols.netmon, 'pid', false);
|
||||
|
||||
window.addEventListener('pywebviewready', () => {
|
||||
window.pywebview.api.get_config().then(cfg => {
|
||||
let pywebviewInitStarted = false;
|
||||
|
||||
const initializePywebview = () => {
|
||||
if (pywebviewInitStarted || !window.pywebview?.api) return;
|
||||
pywebviewInitStarted = true;
|
||||
|
||||
const configTimeout = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error("Configuration request timed out")), 10000);
|
||||
});
|
||||
|
||||
Promise.race([window.pywebview.api.get_config(), configTimeout]).then(cfg => {
|
||||
const theme = cfg.theme || "white_switch";
|
||||
const lang = cfg.language || "english_switch";
|
||||
appState.firstLaunch = cfg.first_launch;
|
||||
@@ -1132,24 +1141,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
if (appState.firstLaunch) {
|
||||
toggle.checked = false;
|
||||
toggle.disabled = true;
|
||||
if (appState.firstLaunch) {
|
||||
toggle.checked = false;
|
||||
toggle.disabled = true;
|
||||
} else if (switchMap[index]) {
|
||||
toggle.checked = !!cfg[switchMap[index]];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
closeStartupOverlay();
|
||||
window.pywebview.api.init_ui_ready();
|
||||
}).catch(err => {
|
||||
pywebviewInitStarted = false;
|
||||
console.error("Config load failed:", err);
|
||||
showStartupError(`Configuration failed to load: ${err?.message || err}`);
|
||||
try {
|
||||
window.pywebview.api.init_ui_ready();
|
||||
} catch (e) {}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('pywebviewready', initializePywebview);
|
||||
initializePywebview();
|
||||
|
||||
window.onEngineReady = async () => {
|
||||
if (!appState.firstLaunch) return;
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
window.setTimeout(function () {
|
||||
var app = document.querySelector('.app-container');
|
||||
var overlay = document.getElementById('loading_overlay');
|
||||
if (app) app.classList.add('fade-in');
|
||||
if (overlay && !overlay.classList.contains('fade-out')) overlay.classList.add('fade-out');
|
||||
}, 15000);
|
||||
if (overlay && !overlay.classList.contains('fade-out') && !overlay.classList.contains('startup-error')) {
|
||||
overlay.classList.add('startup-error');
|
||||
overlay.innerHTML = '<div class="startup-error-box"><h1>PYAS failed to open</h1><p>WebView2 initialization timed out. PYAS will retry automatically.</p></div>';
|
||||
}
|
||||
}, 20000);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
@@ -319,7 +320,7 @@
|
||||
<div class="license-widget">
|
||||
<textarea class="license-text" readonly>87owo Publicly Available Source Code License Agreement
|
||||
|
||||
Last Updated: 2026-06-09
|
||||
Last Updated: 2026-07-18
|
||||
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
|
||||
|
||||
1. Definitions and Interpretation
|
||||
@@ -371,6 +372,7 @@ IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE L
|
||||
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
|
||||
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
|
||||
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
|
||||
- 7.6. Driver Signature: Certain kernel-mode drivers distributed with this software may be digitally signed using code signing methods that are not certified by Microsoft Windows Hardware Quality Labs (WHQL). By installing or using this software, you acknowledge this and decide for yourself whether to install and trust such drivers on your system.
|
||||
|
||||
8. Warranty Disclaimer and Limitation of Liability
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
87owo Publicly Available Source Code License Agreement
|
||||
|
||||
Last Updated: 2026-06-09
|
||||
Last Updated: 2026-07-18
|
||||
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
|
||||
|
||||
1. Definitions and Interpretation
|
||||
@@ -52,6 +52,7 @@ IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE L
|
||||
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
|
||||
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
|
||||
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
|
||||
- 7.6. Driver Signature: Certain kernel-mode drivers distributed with this software may be digitally signed using code signing methods that are not certified by Microsoft Windows Hardware Quality Labs (WHQL). By installing or using this software, you acknowledge this and decide for yourself whether to install and trust such drivers on your system.
|
||||
|
||||
8. Warranty Disclaimer and Limitation of Liability
|
||||
|
||||
|
||||
+1306
-970
File diff suppressed because it is too large.
Load diff
+216
-154
@@ -1,6 +1,6 @@
|
||||
#define AppId "{{a7d7bac3-93b8-4630-8308-c7a56bf7fdf4}"
|
||||
#define AppName "PYAS"
|
||||
#define AppVersion "3.6.3.0"
|
||||
#define AppVersion "3.6.8.0"
|
||||
#define AppPublisher "PYAS Security"
|
||||
#define AppURL "https://github.com/87owo/PYAS"
|
||||
#define AppExeName "PYAS.exe"
|
||||
@@ -52,20 +52,22 @@ Name: "arabic"; MessagesFile: "SetupResources\Arabic.isl"
|
||||
Name: "russian"; MessagesFile: "SetupResources\Russian.isl"
|
||||
Name: "slovenian"; MessagesFile: "SetupResources\Slovenian.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
Name: "autostart"; Description: "{cm:AutoStartTask}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
Name: "install_webview2"; Description: "{cm:InstallWebView2}"; GroupDescription: "{cm:Dependencies}"
|
||||
Name: "install_vcredist"; Description: "{cm:InstallVCRedist}"; GroupDescription: "{cm:Dependencies}"
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
|
||||
[Files]
|
||||
Source: "Payload\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "Redist\VC_redist.x64.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion
|
||||
Source: "Redist\MicrosoftEdgeWebview2Setup.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion
|
||||
Source: "Redist\VC_redist.x64.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion; AfterInstall: EnsureVCRedist
|
||||
Source: "Redist\MicrosoftEdgeWebview2Setup.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion; AfterInstall: EnsureWebView2
|
||||
Source: "Payload\Engine\*"; DestDir: "{app}\Engine"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "Payload\Interface\*"; DestDir: "{app}\Interface"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "Payload\License\*"; DestDir: "{app}\License"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "Payload\PYAS.exe"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall: FinalizeDriverRemoval
|
||||
Source: "Payload\Plugins\*"; DestDir: "{app}\Plugins"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "Software\Classes\*\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
|
||||
Root: HKCU; Subkey: "Software\Classes\Directory\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "Software\Classes\*\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
|
||||
Root: HKCU; Subkey: "Software\Classes\Directory\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
|
||||
Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Run"; ValueName: "PYAS_Security"; Flags: uninsdeletevalue dontcreatekey
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{commonappdata}\PYAS"
|
||||
@@ -76,9 +78,7 @@ Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
|
||||
Name: "{group}\Uninstall {#AppName}"; Filename: "{uninstallexe}"
|
||||
|
||||
[Run]
|
||||
Filename: "{tmp}\PYAS_Redist\VC_redist.x64.exe"; Parameters: "/quiet /norestart"; Flags: waituntilterminated runhidden; StatusMsg: "{cm:InstallingVCRuntime}"; Tasks: install_vcredist
|
||||
Filename: "{tmp}\PYAS_Redist\MicrosoftEdgeWebview2Setup.exe"; Parameters: "/silent /install"; Flags: waituntilterminated runhidden; StatusMsg: "{cm:InstallingWebView2Runtime}"; Tasks: install_webview2
|
||||
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser
|
||||
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser; Check: CanLaunchApp
|
||||
|
||||
[CustomMessages]
|
||||
english.InstallingVCRuntime=Installing Microsoft Visual C++ Runtime...
|
||||
@@ -87,9 +87,6 @@ chinesetraditional.InstallingVCRuntime=正在安裝 Microsoft Visual C++ 執行
|
||||
english.InstallingWebView2Runtime=Installing Microsoft Edge WebView2 Runtime...
|
||||
chinesesimplified.InstallingWebView2Runtime=正在安装 Microsoft Edge WebView2 Runtime...
|
||||
chinesetraditional.InstallingWebView2Runtime=正在安裝 Microsoft Edge WebView2 Runtime...
|
||||
english.AutoStartTask=Run PYAS automatically at system startup
|
||||
chinesesimplified.AutoStartTask=开机时自动运行 PYAS
|
||||
chinesetraditional.AutoStartTask=開機時自動執行 PYAS
|
||||
english.Dependencies=Dependencies:
|
||||
chinesesimplified.Dependencies=运行环境:
|
||||
chinesetraditional.Dependencies=執行環境:
|
||||
@@ -100,154 +97,219 @@ english.InstallVCRedist=Install Microsoft Visual C++ Runtime
|
||||
chinesesimplified.InstallVCRedist=安装 Microsoft Visual C++ 运行库
|
||||
chinesetraditional.InstallVCRedist=安裝 Microsoft Visual C++ 執行庫
|
||||
english.LegacyVersionDetected=An older version of PYAS was detected. Please uninstall it manually before installing.
|
||||
english.QuitFailed=PYAS or its driver could not be stopped safely. Restart Windows and run Setup again.
|
||||
english.DependencyInstallFailed=Required Microsoft runtime installation failed. Setup cannot continue.
|
||||
chinesesimplified.LegacyVersionDetected=检测到存在旧版 PYAS。请先手动卸载旧版后,再运行本安装程序。
|
||||
chinesetraditional.LegacyVersionDetected=檢測到存在舊版 PYAS。請先手動卸載舊版後,再執行本安裝程式。
|
||||
|
||||
[Code]
|
||||
var
|
||||
TasksInitialized: Boolean;
|
||||
|
||||
function IsWebView2Installed: Boolean;
|
||||
var
|
||||
RegKeyPath: string;
|
||||
Version: string;
|
||||
begin
|
||||
RegKeyPath := 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
|
||||
Result := False;
|
||||
if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv', Version) then
|
||||
if Length(Version) > 0 then Result := True;
|
||||
if not Result and RegQueryStringValue(HKCU, RegKeyPath, 'pv', Version) then
|
||||
if Length(Version) > 0 then Result := True;
|
||||
if not Result and RegQueryStringValue(HKLM, RegKeyPath, 'pv', Version) then
|
||||
if Length(Version) > 0 then Result := True;
|
||||
end;
|
||||
|
||||
function IsVCRedistInstalled: Boolean;
|
||||
var
|
||||
Bld: Cardinal;
|
||||
begin
|
||||
Result := False;
|
||||
if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Bld', Bld) then
|
||||
if Bld > 0 then Result := True;
|
||||
end;
|
||||
|
||||
procedure CurPageChanged(CurPageID: Integer);
|
||||
var
|
||||
I: Integer;
|
||||
ItemText: string;
|
||||
begin
|
||||
if (CurPageID = wpSelectTasks) and not TasksInitialized then
|
||||
begin
|
||||
TasksInitialized := True;
|
||||
for I := 0 to WizardForm.TasksList.Items.Count - 1 do
|
||||
begin
|
||||
ItemText := WizardForm.TasksList.Items[I];
|
||||
if Pos('Visual C++', ItemText) > 0 then
|
||||
WizardForm.TasksList.Checked[I] := not IsVCRedistInstalled
|
||||
else if Pos('WebView2', ItemText) > 0 then
|
||||
WizardForm.TasksList.Checked[I] := not IsWebView2Installed;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure QuitOldInstance(InstallPath: string);
|
||||
var
|
||||
ResultCode: Integer;
|
||||
ExePath: string;
|
||||
begin
|
||||
ExePath := InstallPath + '\{#AppExeName}';
|
||||
if FileExists(ExePath) then
|
||||
begin
|
||||
Exec(ExePath, '-quit', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
Sleep(500);
|
||||
Exec(ExpandConstant('{sys}\taskkill.exe'), '/F /IM {#AppExeName} /T', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
var
|
||||
OldInstallPath: string;
|
||||
LegacyPath: string;
|
||||
begin
|
||||
LegacyPath := ExpandConstant('{pf32}\{#AppName}');
|
||||
if DirExists(LegacyPath) and FileExists(LegacyPath + '\{#AppExeName}') then
|
||||
begin
|
||||
MsgBox(CustomMessage('LegacyVersionDetected'), mbCriticalError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) or
|
||||
RegQueryStringValue(HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) then
|
||||
begin
|
||||
QuitOldInstance(OldInstallPath);
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function InitializeUninstall(): Boolean;
|
||||
begin
|
||||
QuitOldInstance(ExpandConstant('{app}'));
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
procedure TryCreateStartupTask;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
PSExe: string;
|
||||
PSCommand: string;
|
||||
AppPath: string;
|
||||
begin
|
||||
AppPath := ExpandConstant('{app}\{#AppExeName}');
|
||||
PSExe := ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe');
|
||||
PSCommand := '-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -Command ' +
|
||||
'$Action = New-ScheduledTaskAction -Execute ''' + AppPath + ''' -Argument ''-hide''; ' +
|
||||
'$Trigger = New-ScheduledTaskTrigger -AtLogOn; ' +
|
||||
'$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; ' +
|
||||
'Register-ScheduledTask -TaskName ''PYAS_Security_ATS'' -Action $Action -Trigger $Trigger -Settings $Settings -RunLevel Highest -Force';
|
||||
if not Exec(PSExe, PSCommand, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
|
||||
begin
|
||||
Log(Format('Failed to create scheduled task, Exec error code: %d', [ResultCode]));
|
||||
end
|
||||
else if ResultCode <> 0 then
|
||||
begin
|
||||
Log(Format('powershell returned exit code %d while creating the scheduled task', [ResultCode]));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TryDeleteStartupTask;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
if not Exec(ExpandConstant('{sys}\schtasks.exe'), '/Delete /TN "PYAS_Security_ATS" /F', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
|
||||
begin
|
||||
Log(Format('Failed to delete scheduled task, Exec error code: %d', [ResultCode]));
|
||||
end
|
||||
else if ResultCode <> 0 then
|
||||
begin
|
||||
Log(Format('schtasks returned exit code %d while deleting the scheduled task', [ResultCode]));
|
||||
end;
|
||||
end;
|
||||
[Code]
|
||||
var
|
||||
DependencyRestartRequired: Boolean;
|
||||
DependenciesReady: Boolean;
|
||||
|
||||
function FindWindow(lpClassName: LongWord; lpWindowName: string): HWND;
|
||||
external '[email protected] stdcall';
|
||||
|
||||
function GetTickCount: Cardinal;
|
||||
external '[email protected] stdcall';
|
||||
|
||||
function IsValidRuntimeVersion(Version: string): Boolean;
|
||||
begin
|
||||
Result := (Length(Trim(Version)) > 0) and (Trim(Version) <> '0.0.0.0');
|
||||
end;
|
||||
|
||||
function IsWebView2Installed: Boolean;
|
||||
var
|
||||
Version: string;
|
||||
begin
|
||||
Result := False;
|
||||
if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv', Version) then
|
||||
Result := IsValidRuntimeVersion(Version);
|
||||
if not Result and RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv', Version) then
|
||||
Result := IsValidRuntimeVersion(Version);
|
||||
if not Result and RegQueryStringValue(HKCU, 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv', Version) then
|
||||
Result := IsValidRuntimeVersion(Version);
|
||||
end;
|
||||
|
||||
function IsVCRedistInstalled: Boolean;
|
||||
var
|
||||
Installed: Cardinal;
|
||||
Version: string;
|
||||
begin
|
||||
Result := False;
|
||||
if RegQueryDWordValue(HKLM64, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Installed', Installed) and (Installed = 1) then
|
||||
begin
|
||||
if RegQueryStringValue(HKLM64, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Version', Version) then
|
||||
Result := IsValidRuntimeVersion(Version)
|
||||
else
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
function DependencyExitCodeSucceeded(ResultCode: Integer): Boolean;
|
||||
begin
|
||||
Result := (ResultCode = 0) or (ResultCode = 1641) or (ResultCode = 3010);
|
||||
if (ResultCode = 1641) or (ResultCode = 3010) then
|
||||
DependencyRestartRequired := True;
|
||||
end;
|
||||
|
||||
procedure EnsureVCRedist;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
InstallerPath: string;
|
||||
begin
|
||||
if IsVCRedistInstalled then Exit;
|
||||
InstallerPath := ExpandConstant('{tmp}\PYAS_Redist\VC_redist.x64.exe');
|
||||
WizardForm.StatusLabel.Caption := CustomMessage('InstallingVCRuntime');
|
||||
if not Exec(InstallerPath, '/quiet /norestart', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) or
|
||||
not DependencyExitCodeSucceeded(ResultCode) or not IsVCRedistInstalled then
|
||||
begin
|
||||
DependenciesReady := False;
|
||||
RaiseException(CustomMessage('DependencyInstallFailed'));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure EnsureWebView2;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
InstallerPath: string;
|
||||
begin
|
||||
if IsWebView2Installed then Exit;
|
||||
InstallerPath := ExpandConstant('{tmp}\PYAS_Redist\MicrosoftEdgeWebview2Setup.exe');
|
||||
WizardForm.StatusLabel.Caption := CustomMessage('InstallingWebView2Runtime');
|
||||
if not Exec(InstallerPath, '/silent /install', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) or
|
||||
not DependencyExitCodeSucceeded(ResultCode) or not IsWebView2Installed then
|
||||
begin
|
||||
DependenciesReady := False;
|
||||
RaiseException(CustomMessage('DependencyInstallFailed'));
|
||||
end;
|
||||
end;
|
||||
|
||||
function WaitForMainWindowToClose(TimeoutMs: Cardinal): Boolean;
|
||||
var
|
||||
Deadline: Cardinal;
|
||||
begin
|
||||
Deadline := GetTickCount + TimeoutMs;
|
||||
repeat
|
||||
if FindWindow(0, 'PYAS Security') = 0 then
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
Sleep(100);
|
||||
until GetTickCount >= Deadline;
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function QuitOldInstance(InstallPath: string): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
ExePath: string;
|
||||
begin
|
||||
Result := True;
|
||||
ExePath := InstallPath + '\{#AppExeName}';
|
||||
if not FileExists(ExePath) then Exit;
|
||||
|
||||
if not Exec(ExePath, '-quit', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := (ResultCode = 0) and WaitForMainWindowToClose(30000);
|
||||
end;
|
||||
|
||||
procedure FinalizeDriverRemoval;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
ExePath: string;
|
||||
begin
|
||||
ExePath := ExpandConstant('{app}\{#AppExeName}');
|
||||
if not Exec(ExePath, '-quit', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) or (ResultCode <> 0) then
|
||||
RaiseException(CustomMessage('QuitFailed'));
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
var
|
||||
OldInstallPath: string;
|
||||
LegacyPath: string;
|
||||
begin
|
||||
DependencyRestartRequired := False;
|
||||
DependenciesReady := True;
|
||||
LegacyPath := ExpandConstant('{pf32}\{#AppName}');
|
||||
if DirExists(LegacyPath) and FileExists(LegacyPath + '\{#AppExeName}') then
|
||||
begin
|
||||
MsgBox(CustomMessage('LegacyVersionDetected'), mbCriticalError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) or
|
||||
RegQueryStringValue(HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) then
|
||||
begin
|
||||
if not QuitOldInstance(OldInstallPath) then
|
||||
begin
|
||||
MsgBox(CustomMessage('QuitFailed'), mbCriticalError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function InitializeUninstall(): Boolean;
|
||||
begin
|
||||
Result := QuitOldInstance(ExpandConstant('{app}'));
|
||||
if not Result then
|
||||
MsgBox(CustomMessage('QuitFailed'), mbCriticalError, MB_OK);
|
||||
end;
|
||||
|
||||
function CanLaunchApp: Boolean;
|
||||
begin
|
||||
Result := DependenciesReady and not DependencyRestartRequired;
|
||||
end;
|
||||
|
||||
function NeedRestart: Boolean;
|
||||
begin
|
||||
Result := DependencyRestartRequired;
|
||||
end;
|
||||
procedure RemoveLegacyStartupTask;
|
||||
var
|
||||
TaskService: Variant;
|
||||
RootFolder: Variant;
|
||||
begin
|
||||
try
|
||||
TaskService := CreateOleObject('Schedule.Service');
|
||||
TaskService.Connect();
|
||||
RootFolder := TaskService.GetFolder('\');
|
||||
try
|
||||
RootFolder.DeleteTask('PYAS_Security_ATS', 0);
|
||||
Log('Removed legacy PYAS scheduled startup task');
|
||||
except
|
||||
Log('Legacy PYAS scheduled startup task was not present or could not be removed');
|
||||
end;
|
||||
except
|
||||
Log('Task Scheduler COM was unavailable while cleaning the legacy startup task');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
if WizardIsTaskSelected('autostart') then
|
||||
begin
|
||||
TryCreateStartupTask;
|
||||
end;
|
||||
end;
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
RemoveLegacyStartupTask;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
begin
|
||||
if CurUninstallStep = usUninstall then
|
||||
begin
|
||||
TryDeleteStartupTask;
|
||||
if CurUninstallStep = usUninstall then
|
||||
begin
|
||||
RemoveLegacyStartupTask;
|
||||
end
|
||||
else if CurUninstallStep = usPostUninstall then
|
||||
begin
|
||||
DelTree(ExpandConstant('{app}'), True, True, True);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
+273
-119
@@ -7,8 +7,18 @@ FLT_PORT_FLAG_SYNC_HANDLE = 0x00000001
|
||||
HRESULT_IO_PENDING = 0x800703E5
|
||||
ERROR_OPERATION_ABORTED = 995
|
||||
ERROR_NOT_FOUND = 1168
|
||||
ERROR_SHARING_VIOLATION = 32
|
||||
ERROR_LOCK_VIOLATION = 33
|
||||
WAIT_OBJECT_0 = 0
|
||||
WAIT_TIMEOUT = 258
|
||||
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
|
||||
GENERIC_READ = 0x80000000
|
||||
FILE_SHARE_READ = 0x00000001
|
||||
OPEN_EXISTING = 3
|
||||
FILE_ATTRIBUTE_NORMAL = 0x00000080
|
||||
FILE_SCAN_DEBOUNCE_SECONDS = 0.35
|
||||
FILE_SCAN_RETRY_SECONDS = 0.25
|
||||
FILE_LOCK_RETRY_SECONDS = 0.5
|
||||
PYAS_CONNECTION_MAGIC = 0x53415950
|
||||
PYAS_CONNECTION_VERSION = 1
|
||||
PROCESS_TERMINATE = 0x0001
|
||||
@@ -72,97 +82,143 @@ class ProtectMixin:
|
||||
def init_whitelist(self):
|
||||
self.manage_named_list("white_list", [self.file_pyas], action="add")
|
||||
|
||||
def sync_driver_whitelist(self, file_path, is_add=True):
|
||||
def _driver_whitelist_patterns(self, file_path, is_directory=None):
|
||||
normalized_path = self.norm_path(file_path, must_exist=False)
|
||||
if not normalized_path:
|
||||
return []
|
||||
|
||||
if is_directory is None:
|
||||
is_directory = os.path.isdir(normalized_path)
|
||||
|
||||
drive_letter = os.path.splitdrive(normalized_path)[0]
|
||||
driver_path = "*" + normalized_path[len(drive_letter):] if drive_letter else normalized_path
|
||||
patterns = [driver_path]
|
||||
|
||||
if is_directory:
|
||||
descendant_pattern = driver_path.rstrip("\\/") + "\\*"
|
||||
if descendant_pattern not in patterns:
|
||||
patterns.append(descendant_pattern)
|
||||
|
||||
return patterns
|
||||
|
||||
def sync_driver_whitelist(self, file_path, is_add=True, is_directory=None):
|
||||
with self.lock_driver:
|
||||
if not self.driver_port:
|
||||
return False
|
||||
|
||||
driver_path = str(file_path)
|
||||
drive_letter = os.path.splitdrive(driver_path)[0]
|
||||
if drive_letter:
|
||||
driver_path = "*" + driver_path[len(drive_letter):]
|
||||
|
||||
msg = PYAS_USER_MESSAGE()
|
||||
msg.Command = 1 if is_add else 2
|
||||
msg.Path = driver_path
|
||||
bytes_returned = ctypes.wintypes.DWORD(0)
|
||||
|
||||
try:
|
||||
return self.fltlib.FilterSendMessage(self.driver_port, ctypes.byref(msg), ctypes.sizeof(msg), None, 0, ctypes.byref(bytes_returned)) == 0
|
||||
except Exception:
|
||||
patterns = self._driver_whitelist_patterns(file_path, is_directory)
|
||||
if not patterns:
|
||||
return False
|
||||
|
||||
success = True
|
||||
for driver_path in patterns:
|
||||
msg = PYAS_USER_MESSAGE()
|
||||
msg.Command = 1 if is_add else 2
|
||||
bytes_returned = ctypes.wintypes.DWORD(0)
|
||||
|
||||
try:
|
||||
msg.Path = driver_path
|
||||
if self.fltlib.FilterSendMessage(self.driver_port, ctypes.byref(msg), ctypes.sizeof(msg), None, 0, ctypes.byref(bytes_returned)) != 0:
|
||||
success = False
|
||||
except Exception:
|
||||
success = False
|
||||
|
||||
return success
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def lock_file(self, target_path, lock, quiet=False):
|
||||
def _try_lock_file(self, file_path):
|
||||
fd = None
|
||||
try:
|
||||
fd = os.open(file_path, os.O_RDWR | os.O_BINARY)
|
||||
try:
|
||||
size = os.path.getsize(file_path)
|
||||
except Exception:
|
||||
size = 1
|
||||
|
||||
lock_size = size if size > 0 else 1
|
||||
msvcrt.locking(fd, msvcrt.LK_NBRLCK, lock_size)
|
||||
self.virus_lock[file_path] = (fd, lock_size)
|
||||
return True, None
|
||||
except Exception as e:
|
||||
if fd is not None:
|
||||
try:
|
||||
os.close(fd)
|
||||
except Exception:
|
||||
pass
|
||||
return False, e
|
||||
|
||||
def _queue_file_lock(self, file_path):
|
||||
self._schedule_file_task("lock", file_path, self._retry_file_lock, FILE_LOCK_RETRY_SECONDS)
|
||||
|
||||
def _retry_file_lock(self, file_path):
|
||||
if not os.path.isfile(file_path):
|
||||
return
|
||||
|
||||
with self.lock_file_ops:
|
||||
if lock:
|
||||
if not os.path.exists(target_path):
|
||||
return
|
||||
if file_path in self.virus_lock:
|
||||
return
|
||||
success, _ = self._try_lock_file(file_path)
|
||||
|
||||
paths_to_lock = []
|
||||
if os.path.isdir(target_path):
|
||||
for root, _, files in os.walk(target_path):
|
||||
for f in files:
|
||||
paths_to_lock.append(os.path.join(root, f))
|
||||
else:
|
||||
paths_to_lock.append(target_path)
|
||||
if not success:
|
||||
self._queue_file_lock(file_path)
|
||||
|
||||
def lock_file(self, target_path, lock, quiet=False):
|
||||
if lock:
|
||||
if not os.path.exists(target_path):
|
||||
return False
|
||||
|
||||
paths_to_lock = []
|
||||
if os.path.isdir(target_path):
|
||||
for root, _, files in os.walk(target_path):
|
||||
for file_name in files:
|
||||
paths_to_lock.append(os.path.join(root, file_name))
|
||||
else:
|
||||
paths_to_lock.append(target_path)
|
||||
|
||||
failed_locks = []
|
||||
with self.lock_file_ops:
|
||||
for file_path in paths_to_lock:
|
||||
if file_path in self.virus_lock:
|
||||
continue
|
||||
|
||||
success = False
|
||||
last_error = None
|
||||
for _ in range(5):
|
||||
try:
|
||||
fd = os.open(file_path, os.O_RDWR | os.O_BINARY)
|
||||
try:
|
||||
try:
|
||||
size = os.path.getsize(file_path)
|
||||
except Exception:
|
||||
size = 1
|
||||
success, error = self._try_lock_file(file_path)
|
||||
if not success:
|
||||
failed_locks.append((file_path, error))
|
||||
|
||||
lock_size = size if size > 0 else 1
|
||||
msvcrt.locking(fd, msvcrt.LK_NBRLCK, lock_size)
|
||||
self.virus_lock[file_path] = (fd, lock_size)
|
||||
success = True
|
||||
break
|
||||
for file_path, error in failed_locks:
|
||||
self._queue_file_lock(file_path)
|
||||
if not quiet:
|
||||
self.write_log("INFO", "File Lock Deferred", source=file_path, detail=str(error))
|
||||
|
||||
except Exception as e:
|
||||
os.close(fd)
|
||||
raise e
|
||||
return not failed_locks
|
||||
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
time.sleep(0.1)
|
||||
self._cancel_pending_file_tasks("lock", target_path)
|
||||
with self.lock_file_ops:
|
||||
target_norm = os.path.normcase(target_path)
|
||||
target_dir = target_norm + os.sep
|
||||
keys_to_unlock = []
|
||||
|
||||
if not success and not quiet:
|
||||
self.write_log("WARN", "lock_file", source=file_path, detail=str(last_error), success=False)
|
||||
else:
|
||||
target_norm = os.path.normcase(target_path)
|
||||
target_dir = target_norm + os.sep
|
||||
keys_to_unlock = []
|
||||
for locked_path in self.virus_lock:
|
||||
locked_norm = os.path.normcase(locked_path)
|
||||
if locked_norm == target_norm or locked_norm.startswith(target_dir):
|
||||
keys_to_unlock.append(locked_path)
|
||||
|
||||
for locked_path in self.virus_lock:
|
||||
locked_norm = os.path.normcase(locked_path)
|
||||
if locked_norm == target_norm or locked_norm.startswith(target_dir):
|
||||
keys_to_unlock.append(locked_path)
|
||||
|
||||
for file_path in keys_to_unlock:
|
||||
fd, lock_size = self.virus_lock[file_path]
|
||||
for file_path in keys_to_unlock:
|
||||
fd, lock_size = self.virus_lock[file_path]
|
||||
try:
|
||||
msvcrt.locking(fd, msvcrt.LK_UNLCK, lock_size)
|
||||
except Exception as e:
|
||||
if not quiet:
|
||||
self.write_log("WARN", "unlock_file", source=file_path, detail=str(e), success=False)
|
||||
finally:
|
||||
try:
|
||||
msvcrt.locking(fd, msvcrt.LK_UNLCK, lock_size)
|
||||
except Exception as e:
|
||||
if not quiet:
|
||||
self.write_log("WARN", "unlock_file", source=file_path, detail=str(e), success=False)
|
||||
os.close(fd)
|
||||
except Exception:
|
||||
pass
|
||||
del self.virus_lock[file_path]
|
||||
|
||||
finally:
|
||||
try:
|
||||
os.close(fd)
|
||||
except Exception:
|
||||
pass
|
||||
del self.virus_lock[file_path]
|
||||
return True
|
||||
|
||||
def relock_file(self):
|
||||
while True:
|
||||
@@ -665,7 +721,7 @@ class ProtectMixin:
|
||||
suffix = self.pyas_config.get("suffix", [])
|
||||
|
||||
if not ext_filter or os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
self.protect_pool.submit(self.handle_new_file, file_path)
|
||||
self._queue_file_scan(file_path)
|
||||
|
||||
if notify.NextEntryOffset == 0:
|
||||
break
|
||||
@@ -683,33 +739,106 @@ class ProtectMixin:
|
||||
pass
|
||||
self.h_dir_file = None
|
||||
|
||||
def _cancel_pending_file_tasks(self, task_name=None, target_path=None):
|
||||
target_norm = os.path.normcase(target_path) if target_path else None
|
||||
target_dir = target_norm + os.sep if target_norm else None
|
||||
|
||||
with self.lock_file_ops:
|
||||
task_keys = []
|
||||
for task_key in self.file_task_timers:
|
||||
name, path_key = task_key
|
||||
if task_name and name != task_name:
|
||||
continue
|
||||
if target_norm and path_key != target_norm and not path_key.startswith(target_dir):
|
||||
continue
|
||||
task_keys.append(task_key)
|
||||
|
||||
timers = [self.file_task_timers.pop(task_key) for task_key in task_keys]
|
||||
for task_key in task_keys:
|
||||
self.file_task_generations.pop(task_key, None)
|
||||
|
||||
for timer in timers:
|
||||
timer.cancel()
|
||||
|
||||
def _cancel_pending_file_scans(self):
|
||||
self._cancel_pending_file_tasks("scan")
|
||||
|
||||
def _schedule_file_task(self, task_name, file_path, callback, delay):
|
||||
task_key = (task_name, os.path.normcase(file_path))
|
||||
|
||||
with self.lock_file_ops:
|
||||
previous_timer = self.file_task_timers.get(task_key)
|
||||
if previous_timer:
|
||||
previous_timer.cancel()
|
||||
|
||||
generation = self.file_task_generations.get(task_key, 0) + 1
|
||||
timer = threading.Timer(delay, self._run_file_task, args=(task_key, file_path, generation, callback))
|
||||
timer.daemon = True
|
||||
self.file_task_timers[task_key] = timer
|
||||
self.file_task_generations[task_key] = generation
|
||||
|
||||
timer.start()
|
||||
|
||||
def _run_file_task(self, task_key, file_path, generation, callback):
|
||||
with self.lock_file_ops:
|
||||
if self.file_task_generations.get(task_key) != generation:
|
||||
return
|
||||
if self.file_task_timers.get(task_key) is threading.current_thread():
|
||||
self.file_task_timers.pop(task_key, None)
|
||||
|
||||
try:
|
||||
callback(file_path)
|
||||
finally:
|
||||
with self.lock_file_ops:
|
||||
if self.file_task_generations.get(task_key) == generation:
|
||||
self.file_task_timers.pop(task_key, None)
|
||||
self.file_task_generations.pop(task_key, None)
|
||||
|
||||
def _queue_file_scan(self, file_path, delay=FILE_SCAN_DEBOUNCE_SECONDS):
|
||||
self._schedule_file_task("scan", file_path, self.handle_new_file, delay)
|
||||
|
||||
def _acquire_file_scan_gate(self, file_path):
|
||||
ctypes.set_last_error(0)
|
||||
handle = self.kernel32.CreateFileW(
|
||||
file_path,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
None
|
||||
)
|
||||
|
||||
if handle == INVALID_HANDLE_VALUE:
|
||||
return None, ctypes.get_last_error()
|
||||
|
||||
return handle, 0
|
||||
|
||||
def handle_new_file(self, file_path):
|
||||
try:
|
||||
for _ in range(5):
|
||||
try:
|
||||
fd = os.open(file_path, os.O_RDONLY | os.O_BINARY)
|
||||
os.close(fd)
|
||||
with self.lock_config:
|
||||
if not self.pyas_config.get("document_switch", False):
|
||||
return
|
||||
|
||||
size = os.path.getsize(file_path)
|
||||
if size > 0:
|
||||
break
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
gate_handle, error = self._acquire_file_scan_gate(file_path)
|
||||
if not gate_handle:
|
||||
if error in (ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION):
|
||||
self._queue_file_scan(file_path, FILE_SCAN_RETRY_SECONDS)
|
||||
return
|
||||
|
||||
with self.lock_file_ops:
|
||||
if file_path in self.virus_lock: return
|
||||
try:
|
||||
with self.lock_file_ops:
|
||||
if file_path in self.virus_lock:
|
||||
return
|
||||
|
||||
result = self.safe_scan_engine(file_path)
|
||||
self.cloud_check(file_path)
|
||||
|
||||
if result:
|
||||
if self.manage_named_list("quarantine", [file_path], action="add", lock_func=self.lock_file) > 0:
|
||||
self.write_log("BLOCK", "File Block", source=file_path, file_hash=self.calc_file_hash(file_path))
|
||||
result = self.safe_scan_engine(file_path)
|
||||
self.cloud_check(file_path)
|
||||
|
||||
if result:
|
||||
if self.manage_named_list("quarantine", [file_path], action="add", lock_func=self.lock_file) > 0:
|
||||
self.write_log("BLOCK", "File Block", source=file_path, file_hash=self.calc_file_hash(file_path))
|
||||
finally:
|
||||
self.kernel32.CloseHandle(gate_handle)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -785,7 +914,10 @@ class ProtectMixin:
|
||||
running = any(exe_name.lower() == "explorer.exe" for _, exe_name in self._enum_processes())
|
||||
|
||||
if not running:
|
||||
subprocess.Popen("explorer.exe", shell=True)
|
||||
explorer = self._find_windows_tool("explorer.exe")
|
||||
if not explorer:
|
||||
return
|
||||
subprocess.Popen([explorer])
|
||||
self.write_log("INFO", "System Restart", source="explorer.exe")
|
||||
|
||||
except Exception:
|
||||
@@ -1081,36 +1213,59 @@ class ProtectMixin:
|
||||
self.advapi32.CloseServiceHandle(service)
|
||||
self.advapi32.CloseServiceHandle(scm)
|
||||
|
||||
def _delete_driver_service(self):
|
||||
scm = self.advapi32.OpenSCManagerW(None, None, 0x0001)
|
||||
if not scm:
|
||||
return False, ctypes.get_last_error()
|
||||
def uninstall_system_driver(self):
|
||||
if not self.stop_system_driver():
|
||||
return False, 1051
|
||||
return self._delete_driver_service()
|
||||
|
||||
service = None
|
||||
try:
|
||||
service = self.advapi32.OpenServiceW(scm, "PYAS_Driver", 0x0004 | 0x00010000)
|
||||
if not service:
|
||||
error = ctypes.get_last_error()
|
||||
return (True, 0) if error in (1060, 1072) else (False, error)
|
||||
def _delete_driver_service(self, timeout=15.0):
|
||||
deadline = time.monotonic() + timeout
|
||||
last_error = 0
|
||||
delete_requested = False
|
||||
|
||||
state, error = self._query_service_state_handle(service)
|
||||
if error:
|
||||
return False, error
|
||||
if state in (2, 3, 4):
|
||||
return False, 1051
|
||||
while time.monotonic() < deadline:
|
||||
scm = self.advapi32.OpenSCManagerW(None, None, 0x0001)
|
||||
if not scm:
|
||||
return False, ctypes.get_last_error()
|
||||
|
||||
ctypes.set_last_error(0)
|
||||
if self.advapi32.DeleteService(service):
|
||||
return True, 0
|
||||
service = None
|
||||
try:
|
||||
ctypes.set_last_error(0)
|
||||
service = self.advapi32.OpenServiceW(scm, "PYAS_Driver", 0x0004 | 0x00010000)
|
||||
if not service:
|
||||
last_error = ctypes.get_last_error()
|
||||
if last_error == 1060:
|
||||
return True, 0
|
||||
if last_error != 1072:
|
||||
return False, last_error
|
||||
else:
|
||||
state, status_error = self._query_service_state_handle(service)
|
||||
if status_error:
|
||||
return False, status_error
|
||||
|
||||
error = ctypes.get_last_error()
|
||||
if error in (1060, 1072):
|
||||
return True, 0
|
||||
return False, error
|
||||
finally:
|
||||
if service:
|
||||
self.advapi32.CloseServiceHandle(service)
|
||||
self.advapi32.CloseServiceHandle(scm)
|
||||
if state not in (2, 3, 4) and not delete_requested:
|
||||
ctypes.set_last_error(0)
|
||||
if self.advapi32.DeleteService(service):
|
||||
delete_requested = True
|
||||
last_error = 1072
|
||||
else:
|
||||
last_error = ctypes.get_last_error()
|
||||
if last_error == 1060:
|
||||
return True, 0
|
||||
if last_error == 1072:
|
||||
delete_requested = True
|
||||
else:
|
||||
return False, last_error
|
||||
elif state in (2, 3, 4):
|
||||
last_error = 1051
|
||||
finally:
|
||||
if service:
|
||||
self.advapi32.CloseServiceHandle(service)
|
||||
self.advapi32.CloseServiceHandle(scm)
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
return False, last_error or 1072
|
||||
|
||||
def install_system_driver(self):
|
||||
try:
|
||||
@@ -1776,7 +1931,7 @@ class ProtectMixin:
|
||||
if self.driver_stop_event.is_set():
|
||||
break
|
||||
if isinstance(item, dict) and item.get("file"):
|
||||
self.sync_driver_whitelist(item["file"], True)
|
||||
self.sync_driver_whitelist(item["file"], True, item.get("is_dir"))
|
||||
|
||||
if self.driver_stop_event.is_set():
|
||||
break
|
||||
@@ -1843,4 +1998,3 @@ class ProtectMixin:
|
||||
with self.lock_driver:
|
||||
if self.driver_listener_thread is threading.current_thread():
|
||||
self.driver_listener_thread = None
|
||||
|
||||
+50
-13
@@ -437,6 +437,47 @@ class ScannerMixin:
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def _queue_virus_delete(self, file_path):
|
||||
self._schedule_file_task("delete", file_path, self._retry_virus_delete, 0.5)
|
||||
|
||||
def _retry_virus_delete(self, file_path):
|
||||
file_hash = self.calc_file_hash(file_path)
|
||||
try:
|
||||
with self.lock_file_ops:
|
||||
if file_path in self.virus_lock:
|
||||
self.lock_file(file_path, False)
|
||||
|
||||
target_key = os.path.normcase(self.norm_path(file_path, must_exist=False) or file_path)
|
||||
for proc in self.get_process_list():
|
||||
proc_path = proc.get("path")
|
||||
if not proc_path or proc_path == "None":
|
||||
continue
|
||||
proc_key = os.path.normcase(self.norm_path(proc_path, must_exist=False) or proc_path)
|
||||
if proc_key == target_key:
|
||||
self.kill_process(proc["pid"])
|
||||
|
||||
try:
|
||||
os.chmod(file_path, stat.S_IWRITE)
|
||||
except Exception:
|
||||
pass
|
||||
os.remove(file_path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.lock_file(file_path, True, quiet=True)
|
||||
self._queue_virus_delete(file_path)
|
||||
self.write_log("INFO", "Virus Delete Deferred", source=file_path, detail=str(e))
|
||||
return
|
||||
|
||||
self.remove_list_items("quarantine", [file_path])
|
||||
with self.lock_virus:
|
||||
target_key = os.path.normcase(self.norm_path(file_path, must_exist=False) or file_path)
|
||||
self.virus_results = [
|
||||
result for result in self.virus_results
|
||||
if os.path.normcase(self.norm_path(result, must_exist=False) or result) != target_key
|
||||
]
|
||||
self.write_log("INFO", "Virus Delete", source=file_path, file_hash=file_hash, operate=True, success=True)
|
||||
|
||||
def solve_scan(self, file_paths):
|
||||
deleted_paths = []
|
||||
proc_map = {}
|
||||
@@ -477,23 +518,19 @@ class ScannerMixin:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
delete_success = False
|
||||
for _ in range(5):
|
||||
try:
|
||||
os.remove(path)
|
||||
delete_success = True
|
||||
break
|
||||
|
||||
except Exception:
|
||||
time.sleep(0.1)
|
||||
|
||||
if not delete_success:
|
||||
file_hash = self.calc_file_hash(path)
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception as e:
|
||||
self.lock_file(path, True, quiet=True)
|
||||
self._queue_virus_delete(path)
|
||||
self.write_log("INFO", "Virus Delete Deferred", source=path, detail=str(e))
|
||||
continue
|
||||
|
||||
self.remove_list_items("quarantine", [path])
|
||||
deleted_paths.append(raw_path)
|
||||
deleted_paths.append(raw_path)
|
||||
deleted_set.add(path)
|
||||
self.write_log("INFO", "Virus Delete", source=path, file_hash=self.calc_file_hash(path), operate=True, success=True)
|
||||
self.write_log("INFO", "Virus Delete", source=path, file_hash=file_hash, operate=True, success=True)
|
||||
|
||||
except Exception as e:
|
||||
self.lock_file(path, True)
|
||||
|
||||
+336
-43
@@ -1,4 +1,4 @@
|
||||
import os, re, io, csv, time, shutil, threading
|
||||
import os, re, io, csv, json, time, shutil, threading
|
||||
import pefile, hashlib, winreg, requests, subprocess
|
||||
import ctypes, ctypes.wintypes
|
||||
|
||||
@@ -167,6 +167,186 @@ class RECT(ctypes.Structure):
|
||||
####################################################################################################
|
||||
|
||||
class ToolsMixin:
|
||||
def _find_windows_tool(self, *names):
|
||||
windows_dir = os.environ.get("SystemRoot") or os.environ.get("WINDIR") or r"C:\Windows"
|
||||
search_dirs = [
|
||||
os.path.join(windows_dir, "Sysnative"),
|
||||
os.path.join(windows_dir, "System32"),
|
||||
windows_dir
|
||||
]
|
||||
|
||||
for name in names:
|
||||
executable_name = name if name.lower().endswith(".exe") else name + ".exe"
|
||||
for directory in search_dirs:
|
||||
candidate = os.path.join(directory, executable_name)
|
||||
if os.path.isfile(candidate):
|
||||
return candidate
|
||||
|
||||
candidate = shutil.which(name)
|
||||
if candidate:
|
||||
return candidate
|
||||
|
||||
return None
|
||||
|
||||
def _run_windows_tool(self, tool_names, arguments, **kwargs):
|
||||
executable = self._find_windows_tool(*tool_names)
|
||||
if not executable:
|
||||
return None
|
||||
|
||||
options = {"creationflags": 0x08000000}
|
||||
options.update(kwargs)
|
||||
try:
|
||||
return subprocess.run([executable, *arguments], **options)
|
||||
except (FileNotFoundError, OSError):
|
||||
return None
|
||||
|
||||
def _run_powershell(self, command, **kwargs):
|
||||
return self._run_windows_tool(
|
||||
("powershell.exe", "pwsh.exe"),
|
||||
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", command],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def _set_registry_autostart(self, enable):
|
||||
run_key = r"Software\Microsoft\Windows\CurrentVersion\Run"
|
||||
value_name = "PYAS_Security"
|
||||
if enable:
|
||||
command = f'"{self.file_pyas}" -hide'
|
||||
return self._reg_write(winreg.HKEY_CURRENT_USER, run_key, value_name, winreg.REG_SZ, command)
|
||||
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, run_key, 0, winreg.KEY_SET_VALUE) as reg:
|
||||
try:
|
||||
winreg.DeleteValue(reg, value_name)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _clear_event_log(self, log_name):
|
||||
result = self._run_windows_tool(
|
||||
("wevtutil.exe",),
|
||||
["cl", log_name],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
if result is not None and result.returncode == 0:
|
||||
return True
|
||||
|
||||
try:
|
||||
wevtapi = ctypes.WinDLL("wevtapi", use_last_error=True)
|
||||
wevtapi.EvtClearLog.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.LPCWSTR, ctypes.wintypes.LPCWSTR, ctypes.wintypes.DWORD]
|
||||
wevtapi.EvtClearLog.restype = ctypes.wintypes.BOOL
|
||||
return bool(wevtapi.EvtClearLog(None, log_name, None, 0))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _manage_service_native(self, service_name, action):
|
||||
SC_MANAGER_CONNECT = 0x0001
|
||||
SERVICE_CHANGE_CONFIG = 0x0002
|
||||
DELETE = 0x00010000
|
||||
SERVICE_NO_CHANGE = 0xFFFFFFFF
|
||||
SERVICE_AUTO_START = 0x00000002
|
||||
SERVICE_DISABLED = 0x00000004
|
||||
|
||||
try:
|
||||
advapi32 = ctypes.WinDLL("advapi32", use_last_error=True)
|
||||
advapi32.OpenSCManagerW.argtypes = [ctypes.wintypes.LPCWSTR, ctypes.wintypes.LPCWSTR, ctypes.wintypes.DWORD]
|
||||
advapi32.OpenSCManagerW.restype = ctypes.wintypes.HANDLE
|
||||
advapi32.OpenServiceW.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.LPCWSTR, ctypes.wintypes.DWORD]
|
||||
advapi32.OpenServiceW.restype = ctypes.wintypes.HANDLE
|
||||
advapi32.CloseServiceHandle.argtypes = [ctypes.wintypes.HANDLE]
|
||||
advapi32.CloseServiceHandle.restype = ctypes.wintypes.BOOL
|
||||
|
||||
manager = advapi32.OpenSCManagerW(None, None, SC_MANAGER_CONNECT)
|
||||
if not manager:
|
||||
return False
|
||||
|
||||
service = None
|
||||
try:
|
||||
desired_access = DELETE if action == "delete" else SERVICE_CHANGE_CONFIG
|
||||
service = advapi32.OpenServiceW(manager, service_name, desired_access)
|
||||
if not service:
|
||||
return False
|
||||
|
||||
if action == "delete":
|
||||
advapi32.DeleteService.argtypes = [ctypes.wintypes.HANDLE]
|
||||
advapi32.DeleteService.restype = ctypes.wintypes.BOOL
|
||||
return bool(advapi32.DeleteService(service))
|
||||
|
||||
start_type = SERVICE_AUTO_START if action == "enable" else SERVICE_DISABLED
|
||||
advapi32.ChangeServiceConfigW.argtypes = [
|
||||
ctypes.wintypes.HANDLE,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.wintypes.LPCWSTR,
|
||||
ctypes.wintypes.LPCWSTR,
|
||||
ctypes.POINTER(ctypes.wintypes.DWORD),
|
||||
ctypes.wintypes.LPCWSTR,
|
||||
ctypes.wintypes.LPCWSTR,
|
||||
ctypes.wintypes.LPCWSTR,
|
||||
ctypes.wintypes.LPCWSTR
|
||||
]
|
||||
advapi32.ChangeServiceConfigW.restype = ctypes.wintypes.BOOL
|
||||
return bool(advapi32.ChangeServiceConfigW(
|
||||
service,
|
||||
SERVICE_NO_CHANGE,
|
||||
start_type,
|
||||
SERVICE_NO_CHANGE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
))
|
||||
finally:
|
||||
if service:
|
||||
advapi32.CloseServiceHandle(service)
|
||||
advapi32.CloseServiceHandle(manager)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _manage_scheduled_task(self, task_path, action):
|
||||
schtasks_action = "/delete" if action == "delete" else "/change"
|
||||
arguments = [schtasks_action, "/tn", task_path]
|
||||
if action == "delete":
|
||||
arguments.append("/f")
|
||||
else:
|
||||
arguments.append("/enable" if action == "enable" else "/disable")
|
||||
|
||||
result = self._run_windows_tool(
|
||||
("schtasks.exe",),
|
||||
arguments,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if result is not None and result.returncode == 0:
|
||||
return True
|
||||
|
||||
escaped_path = task_path.replace("'", "''")
|
||||
operation = {
|
||||
"delete": "Unregister-ScheduledTask -InputObject $Task -Confirm:$false",
|
||||
"enable": "Enable-ScheduledTask -InputObject $Task | Out-Null",
|
||||
"disable": "Disable-ScheduledTask -InputObject $Task | Out-Null"
|
||||
}.get(action)
|
||||
if not operation:
|
||||
return False
|
||||
|
||||
command = (
|
||||
f"$FullName = '{escaped_path}'; "
|
||||
"$Task = Get-ScheduledTask -ErrorAction SilentlyContinue | "
|
||||
"Where-Object { ($_.TaskPath + $_.TaskName) -eq $FullName } | Select-Object -First 1; "
|
||||
f"if ($Task) {{ {operation} }} else {{ exit 1 }}"
|
||||
)
|
||||
result = self._run_powershell(command, capture_output=True, text=True)
|
||||
return bool(result and result.returncode == 0)
|
||||
|
||||
def _reg_read(self, root, path, value_name):
|
||||
try:
|
||||
with winreg.OpenKey(root, path, 0, winreg.KEY_READ) as reg:
|
||||
@@ -203,18 +383,81 @@ class ToolsMixin:
|
||||
####################################################################################################
|
||||
|
||||
def manage_autostart(self, enable):
|
||||
try:
|
||||
task_name = "PYAS_Security_ATS"
|
||||
if enable:
|
||||
cmd = f"$Action = New-ScheduledTaskAction -Execute '{self.file_pyas}' -Argument '-hide'; $Trigger = New-ScheduledTaskTrigger -AtLogOn; $Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; Register-ScheduledTask -TaskName '{task_name}' -Action $Action -Trigger $Trigger -Settings $Settings -RunLevel Highest -Force"
|
||||
subprocess.run(["powershell", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", cmd], creationflags=0x08000000)
|
||||
else:
|
||||
subprocess.run(["schtasks", "/Delete", "/TN", task_name, "/F"], creationflags=0x08000000)
|
||||
task_name = "PYAS_Security_ATS"
|
||||
executable = os.path.normpath(self.file_pyas)
|
||||
escaped_executable = executable.replace("'", "''")
|
||||
errors = []
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "manage_autostart", detail=str(e), success=False)
|
||||
return False
|
||||
if enable:
|
||||
task_command = f'"{executable}" -hide'
|
||||
result = self._run_windows_tool(
|
||||
("schtasks.exe",),
|
||||
["/Create", "/TN", task_name, "/TR", task_command, "/SC", "ONLOGON", "/RL", "HIGHEST", "/F"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if result and result.returncode == 0:
|
||||
self._set_registry_autostart(False)
|
||||
return True
|
||||
if result:
|
||||
errors.append((result.stderr or result.stdout or f"schtasks exit {result.returncode}").strip())
|
||||
|
||||
command = (
|
||||
f"$Action = New-ScheduledTaskAction -Execute '{escaped_executable}' -Argument '-hide'; "
|
||||
"$Trigger = New-ScheduledTaskTrigger -AtLogOn; "
|
||||
"$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; "
|
||||
f"Register-ScheduledTask -TaskName '{task_name}' -Action $Action -Trigger $Trigger -Settings $Settings -RunLevel Highest -Force | Out-Null"
|
||||
)
|
||||
result = self._run_powershell(command, capture_output=True, text=True)
|
||||
if result and result.returncode == 0:
|
||||
self._set_registry_autostart(False)
|
||||
return True
|
||||
if result:
|
||||
errors.append((result.stderr or result.stdout or f"PowerShell exit {result.returncode}").strip())
|
||||
|
||||
if self._set_registry_autostart(True):
|
||||
self.write_log("INFO", "manage_autostart", detail="Scheduled Task unavailable; using HKCU Run fallback", success=True)
|
||||
return True
|
||||
else:
|
||||
task_removed = False
|
||||
query_result = self._run_windows_tool(
|
||||
("schtasks.exe",),
|
||||
["/Query", "/TN", task_name],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
result = None
|
||||
if query_result is not None and query_result.returncode != 0:
|
||||
task_removed = True
|
||||
elif query_result is not None:
|
||||
result = self._run_windows_tool(
|
||||
("schtasks.exe",),
|
||||
["/Delete", "/TN", task_name, "/F"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
task_removed = bool(result and result.returncode == 0)
|
||||
|
||||
if query_result is None or not task_removed:
|
||||
command = (
|
||||
f"$Task = Get-ScheduledTask -TaskName '{task_name}' -ErrorAction SilentlyContinue; "
|
||||
"if ($Task) { $Task | Unregister-ScheduledTask -Confirm:$false }"
|
||||
)
|
||||
powershell_result = self._run_powershell(command, capture_output=True, text=True)
|
||||
if powershell_result is not None:
|
||||
task_removed = powershell_result.returncode == 0
|
||||
result = powershell_result
|
||||
|
||||
if result and result.returncode != 0:
|
||||
errors.append((result.stderr or result.stdout or f"task removal exit {result.returncode}").strip())
|
||||
|
||||
registry_removed = self._set_registry_autostart(False)
|
||||
if registry_removed and (task_removed or query_result is None and result is None):
|
||||
return True
|
||||
|
||||
detail = "; ".join(error for error in errors if error) or "No supported autostart method was available"
|
||||
self.write_log("WARN", "manage_autostart", detail=detail, success=False)
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -292,10 +535,16 @@ class ToolsMixin:
|
||||
if lock_func:
|
||||
lock_func(path, True)
|
||||
|
||||
target_list.append({"file": path, "time": time.time()})
|
||||
new_item = {"file": path, "time": time.time()}
|
||||
is_directory = None
|
||||
if list_key == "white_list":
|
||||
is_directory = os.path.isdir(path)
|
||||
new_item["is_dir"] = is_directory
|
||||
|
||||
target_list.append(new_item)
|
||||
acted_items.append(path)
|
||||
if list_key == "white_list":
|
||||
self.sync_driver_whitelist(path, True)
|
||||
self.sync_driver_whitelist(path, True, is_directory)
|
||||
|
||||
elif action == "remove":
|
||||
norm_paths_case = {os.path.normcase(p) for p in norm_paths}
|
||||
@@ -311,7 +560,8 @@ class ToolsMixin:
|
||||
|
||||
acted_items.append(val)
|
||||
if list_key == "white_list":
|
||||
self.sync_driver_whitelist(val, False)
|
||||
is_directory = item.get("is_dir") if isinstance(item, dict) else None
|
||||
self.sync_driver_whitelist(val, False, is_directory)
|
||||
|
||||
continue
|
||||
new_list.append(item)
|
||||
@@ -356,7 +606,8 @@ class ToolsMixin:
|
||||
if np and os.path.normcase(np) in norm_paths_to_remove:
|
||||
removed_items.append(val)
|
||||
if list_key == "white_list":
|
||||
self.sync_driver_whitelist(val, False)
|
||||
is_directory = item.get("is_dir") if isinstance(item, dict) else None
|
||||
self.sync_driver_whitelist(val, False, is_directory)
|
||||
|
||||
continue
|
||||
new_list.append(item)
|
||||
@@ -888,7 +1139,7 @@ class ToolsMixin:
|
||||
log_path = os.path.join(self.path_system, "System32", "winevt", "Logs", f"{log_type}.evtx")
|
||||
|
||||
size = os.path.getsize(log_path) if os.path.exists(log_path) else 0
|
||||
if subprocess.run(["wevtutil", "cl", log_type], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=0x08000000).returncode == 0:
|
||||
if self._clear_event_log(log_type):
|
||||
total_deleted += size
|
||||
|
||||
except Exception:
|
||||
@@ -1035,20 +1286,57 @@ class ToolsMixin:
|
||||
def get_tasks():
|
||||
res = []
|
||||
try:
|
||||
proc = subprocess.run(["schtasks", "/query", "/fo", "csv", "/v"], capture_output=True, text=True, creationflags=0x08000000)
|
||||
reader = csv.reader(io.StringIO(proc.stdout))
|
||||
next(reader, None)
|
||||
proc = self._run_windows_tool(
|
||||
("schtasks.exe",),
|
||||
["/query", "/fo", "csv", "/v"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if proc is not None and proc.returncode == 0:
|
||||
reader = csv.reader(io.StringIO(proc.stdout))
|
||||
next(reader, None)
|
||||
|
||||
for row in reader:
|
||||
if len(row) > 8 and row[1].strip() and row[8].strip() and row[8] != "N/A":
|
||||
name = row[1].split('\\')[-1]
|
||||
path = row[8]
|
||||
status = row[3]
|
||||
for row in reader:
|
||||
if len(row) > 8 and row[1].strip() and row[8].strip() and row[8] != "N/A":
|
||||
name = row[1].split('\\')[-1]
|
||||
path = row[8]
|
||||
status = row[3]
|
||||
|
||||
if path and "windows" not in path.lower():
|
||||
fp = self.extract_paths_from_cmdline(path)
|
||||
fpp = fp[0] if fp else path
|
||||
res.append({"id": f"tsk|{row[1]}", "type": "type_tsk", "name": name, "status": "disabled" if status == "Disabled" else "enabled", "path": fpp})
|
||||
if path and "windows" not in path.lower():
|
||||
fp = self.extract_paths_from_cmdline(path)
|
||||
fpp = fp[0] if fp else path
|
||||
res.append({"id": f"tsk|{row[1]}", "type": "type_tsk", "name": name, "status": "disabled" if status.lower() == "disabled" else "enabled", "path": fpp})
|
||||
return res
|
||||
|
||||
command = (
|
||||
"[Console]::OutputEncoding = [Text.UTF8Encoding]::new(); "
|
||||
"Get-ScheduledTask -ErrorAction SilentlyContinue | ForEach-Object { "
|
||||
"$Action = @($_.Actions)[0]; "
|
||||
"[pscustomobject]@{Name=$_.TaskName; FullName=($_.TaskPath + $_.TaskName); "
|
||||
"State=[string]$_.State; Execute=$Action.Execute; Arguments=$Action.Arguments} "
|
||||
"} | ConvertTo-Json -Compress"
|
||||
)
|
||||
proc = self._run_powershell(command, capture_output=True, text=True, encoding="utf-8", errors="replace")
|
||||
if not proc or proc.returncode != 0 or not proc.stdout.strip():
|
||||
return res
|
||||
|
||||
task_data = json.loads(proc.stdout)
|
||||
if isinstance(task_data, dict):
|
||||
task_data = [task_data]
|
||||
|
||||
for task in task_data:
|
||||
name = str(task.get("Name") or "").strip()
|
||||
full_name = str(task.get("FullName") or name).strip()
|
||||
execute = str(task.get("Execute") or "").strip()
|
||||
arguments = str(task.get("Arguments") or "").strip()
|
||||
command_line = " ".join(part for part in (execute, arguments) if part)
|
||||
if not name or not command_line or "windows" in command_line.lower():
|
||||
continue
|
||||
|
||||
paths = self.extract_paths_from_cmdline(command_line)
|
||||
path = paths[0] if paths else execute or command_line
|
||||
status = "disabled" if str(task.get("State") or "").lower() == "disabled" else "enabled"
|
||||
res.append({"id": f"tsk|{full_name}", "type": "type_tsk", "name": name, "status": status, "path": path})
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
@@ -1068,44 +1356,49 @@ class ToolsMixin:
|
||||
def manage_startup(self, items, action):
|
||||
def process_item(item_id):
|
||||
try:
|
||||
parts = item_id.split('|')
|
||||
parts = item_id.split('|', 3)
|
||||
stype = parts[0]
|
||||
|
||||
if stype == "reg":
|
||||
root, old_path, name = int(parts[1]), parts[2], parts[3]
|
||||
if action == "delete":
|
||||
self._reg_delete(root, old_path, name)
|
||||
return self._reg_delete(root, old_path, name)
|
||||
|
||||
else:
|
||||
new_path = old_path.replace("_Disabled", "") if action == "enable" else (old_path if "_Disabled" in old_path else old_path + "_Disabled")
|
||||
if old_path != new_path:
|
||||
val = self._reg_read(root, old_path, name)
|
||||
if val:
|
||||
self._reg_write(root, new_path, name, winreg.REG_SZ, val)
|
||||
self._reg_delete(root, old_path, name)
|
||||
return self._reg_write(root, new_path, name, winreg.REG_SZ, val) and self._reg_delete(root, old_path, name)
|
||||
return True
|
||||
|
||||
elif stype == "srv":
|
||||
name = parts[1]
|
||||
if action == "delete":
|
||||
subprocess.run(["sc", "delete", name], capture_output=True, creationflags=0x08000000)
|
||||
result = self._run_windows_tool(("sc.exe",), ["delete", name], capture_output=True)
|
||||
|
||||
else:
|
||||
mode = "auto" if action == "enable" else "disabled"
|
||||
subprocess.run(["sc", "config", name, f"start=", mode], capture_output=True, creationflags=0x08000000)
|
||||
result = self._run_windows_tool(("sc.exe",), ["config", name, "start=", mode], capture_output=True)
|
||||
|
||||
if result is not None and result.returncode == 0:
|
||||
return True
|
||||
return self._manage_service_native(name, action)
|
||||
|
||||
elif stype == "tsk":
|
||||
name = parts[1]
|
||||
if action == "delete":
|
||||
subprocess.run(["schtasks", "/delete", "/tn", name, "/f"], capture_output=True, creationflags=0x08000000)
|
||||
name = item_id.partition('|')[2]
|
||||
return self._manage_scheduled_task(name, action)
|
||||
|
||||
else:
|
||||
cmd = "/enable" if action == "enable" else "/disable"
|
||||
subprocess.run(["schtasks", "/change", "/tn", name, cmd], capture_output=True, creationflags=0x08000000)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "manage_startup", detail=str(e), success=False)
|
||||
return False
|
||||
|
||||
with ThreadPoolExecutor(max_workers=5) as executor:
|
||||
list(executor.map(process_item, items))
|
||||
results = list(executor.map(process_item, items))
|
||||
|
||||
return True
|
||||
success = all(results) if results else True
|
||||
if not success:
|
||||
self.write_log("WARN", "manage_startup", detail="One or more startup items could not be updated", success=False)
|
||||
return success
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.6.3.0',
|
||||
version='3.6.8.0',
|
||||
company_name="PYAS Security",
|
||||
file_description="PYAS Security Antivirus",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -61,18 +61,24 @@
|
||||
"Category": "Process",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\PYAS_Setup.exe",
|
||||
"*\\unins000.exe",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\Temp\\*\\PYAS_Setup.tmp",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\Temp\\*\\_unins.tmp",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\sogoupdf\\pdfpopcl.exe",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\WeGame\\*\\tiny_dl\\TinyDL.exe",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\Microsoft\\OneDrive\\OneDrive.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\explorer.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\csrss.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\lsass.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\services.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\msiexec.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\SystemApps\\*\\SearchHost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Program Files\\*",
|
||||
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
|
||||
"*\\games\\Arknights Game\\Arknights.exe",
|
||||
"*\\games\\EndField Game\\EndField.exe",
|
||||
"*\\cm\\FeverGames\\*\\FeverGamesInstaller.exe",
|
||||
"*\\Davinci\\Resolve.exe",
|
||||
],
|
||||
"Target": [
|
||||
"*\\PYAS.exe",
|
||||
@@ -195,152 +201,6 @@
|
||||
],
|
||||
"Priority": 160,
|
||||
},
|
||||
{
|
||||
"Name": "Memory_Protect_Critical_Process_CreateRemoteThread",
|
||||
"Code": 1007,
|
||||
"Kill": false,
|
||||
"Category": "Memory",
|
||||
"InitiatorExclude": [
|
||||
"\\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\\*",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\lsass.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\winlogon.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\services.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\csrss.exe",
|
||||
],
|
||||
"Operations": [
|
||||
"WriteMemory",
|
||||
"VmOperation",
|
||||
"CreateThread",
|
||||
],
|
||||
"OperationMatch": "All",
|
||||
"MinimumRiskScore": 90,
|
||||
"HandleTypes": [
|
||||
"Create",
|
||||
"Duplicate",
|
||||
],
|
||||
"ObjectTypes": [
|
||||
"Process",
|
||||
],
|
||||
"Priority": 175,
|
||||
},
|
||||
{
|
||||
"Name": "Memory_Protect_Critical_Process_ThreadHijack",
|
||||
"Code": 1008,
|
||||
"Kill": false,
|
||||
"Category": "Memory",
|
||||
"InitiatorExclude": [
|
||||
"\\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\\*",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\lsass.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\winlogon.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\services.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\csrss.exe",
|
||||
],
|
||||
"Operations": [
|
||||
"WriteMemory",
|
||||
"VmOperation",
|
||||
"SetThreadContext",
|
||||
],
|
||||
"OperationMatch": "All",
|
||||
"MinimumRiskScore": 90,
|
||||
"HandleTypes": [
|
||||
"Create",
|
||||
"Duplicate",
|
||||
],
|
||||
"ObjectTypes": [
|
||||
"Process",
|
||||
],
|
||||
"Priority": 175,
|
||||
},
|
||||
{
|
||||
"Name": "Memory_Protect_Service_Process_CreateRemoteThread",
|
||||
"Code": 1009,
|
||||
"Kill": false,
|
||||
"Category": "Memory",
|
||||
"InitiatorExclude": [
|
||||
"\\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\\*",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\SysWOW64\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\spoolsv.exe",
|
||||
],
|
||||
"Operations": [
|
||||
"WriteMemory",
|
||||
"VmOperation",
|
||||
"CreateThread",
|
||||
],
|
||||
"OperationMatch": "All",
|
||||
"MinimumRiskScore": 90,
|
||||
"HandleTypes": [
|
||||
"Create",
|
||||
"Duplicate",
|
||||
],
|
||||
"ObjectTypes": [
|
||||
"Process",
|
||||
],
|
||||
"Priority": 170,
|
||||
},
|
||||
{
|
||||
"Name": "Memory_Protect_Service_Process_ThreadHijack",
|
||||
"Code": 1010,
|
||||
"Kill": false,
|
||||
"Category": "Memory",
|
||||
"InitiatorExclude": [
|
||||
"\\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\\*",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\SysWOW64\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\spoolsv.exe",
|
||||
],
|
||||
"Operations": [
|
||||
"WriteMemory",
|
||||
"VmOperation",
|
||||
"SetThreadContext",
|
||||
],
|
||||
"OperationMatch": "All",
|
||||
"MinimumRiskScore": 90,
|
||||
"HandleTypes": [
|
||||
"Create",
|
||||
"Duplicate",
|
||||
],
|
||||
"ObjectTypes": [
|
||||
"Process",
|
||||
],
|
||||
"Priority": 170,
|
||||
},
|
||||
{
|
||||
"Name": "Self_Antivirus_Protect_File",
|
||||
"Code": 2001,
|
||||
@@ -348,8 +208,6 @@
|
||||
"Category": "File",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\PYAS_Setup.exe",
|
||||
"*\\unins000.exe",
|
||||
"*\\notepad*.exe",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\Temp\\*\\PYAS_Setup.tmp",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\Temp\\*\\_unins.tmp",
|
||||
@@ -388,6 +246,7 @@
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\Huorong\\Sysdiag\\bin\\sysclean.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\explorer.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\SearchIndexer.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\lsass.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\svchost.exe",
|
||||
@@ -395,6 +254,7 @@
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\taskhostw.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\TrustedInstaller.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\WinSxS\\*\\TiWorker.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\UUS\\amd64\\wuaucltcore.exe",
|
||||
"\\Device\\HarddiskVolume*\\$WinREAgent\\Scratch\\*\\DismHost.exe",
|
||||
],
|
||||
"Target": [
|
||||
@@ -547,6 +407,8 @@
|
||||
"*\\ventoy\\Boot\\*",
|
||||
"*\\AdminRegionConfig.*",
|
||||
"*\\nt_qq\\nt_*\\*",
|
||||
"*\\nt_qq\\global\\nt_*\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\Documents\\Tencent Files\\*",
|
||||
],
|
||||
"Extensions": [
|
||||
".zip",
|
||||
@@ -585,7 +447,7 @@
|
||||
"Delete",
|
||||
"Rename",
|
||||
],
|
||||
"Threshold": 5,
|
||||
"Threshold": 10,
|
||||
"TimeWindow": 3000,
|
||||
},
|
||||
{
|
||||
@@ -597,11 +459,13 @@
|
||||
"*\\PYAS.exe",
|
||||
"*\\rufus-*.exe",
|
||||
"*\\DiskInfo*.exe",
|
||||
"\\Device\\HarddiskVolume*\\Program Files\\*\\launcher\\ca_launcher.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\diskpart.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\diskmgmt.msc",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\taskhostw.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\vds.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\format.com",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\WinSAT.exe",
|
||||
],
|
||||
"Operations": [
|
||||
"Ioctl",
|
||||
@@ -634,8 +498,6 @@
|
||||
"*\\DefaultPassword",
|
||||
"*\\AltDefaultUserName",
|
||||
"*\\AltDefaultPassword",
|
||||
"*\\LegalNoticeCaption",
|
||||
"*\\LegalNoticeText",
|
||||
"*\\AutoReboot",
|
||||
"*\\DisableAutomaticRestartOnFailure",
|
||||
"*\\SystemBiosVersion",
|
||||
@@ -723,7 +585,6 @@
|
||||
"*\\SystemStartOptions",
|
||||
"*\\Userinit",
|
||||
"*\\UIHost",
|
||||
"*\\Debugger",
|
||||
"*\\NoControlPanel",
|
||||
"*\\NoDrives",
|
||||
"*\\NoFileMenu",
|
||||
@@ -810,42 +671,20 @@
|
||||
"Priority": 170,
|
||||
},
|
||||
{
|
||||
"Name": "Registry_Protect_Pending_File_Rename",
|
||||
"Name": "Registry_Protect_OOBE_Reset",
|
||||
"Code": 3006,
|
||||
"Kill": false,
|
||||
"Category": "Registry",
|
||||
"InitiatorExclude": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\*",
|
||||
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
|
||||
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
|
||||
"*\\Sources\\SetupHost.exe",
|
||||
"*\\Work\\*\\DismHost.exe",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Control\\Session Manager\\PendingFileRenameOperations",
|
||||
"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Control\\Session Manager\\PendingFileRenameOperations2",
|
||||
],
|
||||
"Operations": [
|
||||
"Write",
|
||||
"Delete",
|
||||
"Create",
|
||||
],
|
||||
"Priority": 140,
|
||||
},
|
||||
{
|
||||
"Name": "Registry_Protect_OOBE_Reset",
|
||||
"Code": 3007,
|
||||
"Kill": true,
|
||||
"Category": "Registry",
|
||||
"InitiatorExclude": [
|
||||
"*\\Sources\\SetupHost.exe",
|
||||
"*\\Work\\*\\DismHost.exe",
|
||||
"*\\Windows\\UUS\\amd64\\MoUsoCoreWorker.exe",
|
||||
"*\\Windows\\UUS\\Packages\\Preview\\amd64\\MoUsoCoreWorker.exe",
|
||||
"*\\Windows\\System32\\MoUsoCoreWorker.exe",
|
||||
"*\\Windows\\System32\\Sysprep\\sysprep.exe",
|
||||
"*\\Windows\\System32\\oobe\\*",
|
||||
"*\\Windows\\UUS\\amd64\\MoUsoCoreWorker.exe",
|
||||
"*\\Windows\\System32\\svchost.exe",
|
||||
"*\\Windows\\explorer.exe",
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
@@ -862,6 +701,9 @@
|
||||
"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\OOBE\\*",
|
||||
"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE\\*",
|
||||
],
|
||||
"TargetExclude": [
|
||||
"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE\\TelemetryCorrelation",
|
||||
],
|
||||
"Operations": [
|
||||
"Write",
|
||||
"Delete",
|
||||
@@ -870,4 +712,4 @@
|
||||
"Priority": 175,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user