sl23 Posted July 24 Author Posted July 24 Quote put an _ArrayDisplay to see the results (Common debugging practice) ; Initialize previous entries and files Global $aPreviousEntries = _GetRegistryEntries() _ArrayDisplay($aPreviousEntries, "$aPreviousEntries") Great suggestion, but I'm a bit confused why you posted that. Argumentum stated earlier that "there is no "_GetRegistryEntries() " to be found" so how come you added it? I'm also clueless where to add it! The Global line is used at the top with the other "Globals" yes? But the other line, where does that go?
sl23 Posted July 24 Author Posted July 24 Quote and a friendly tip replace the real deletes with virtual ones e.g. ConsoleWrite("RegDelete(" & $sRegistryKey & ", " & $sNewEntry & ")" & @CRLF) ; RegDelete($sRegistryKey, $sNewEntry) ... ConsoleWrite("FileDelete(" & $sFolderPath & "\" & $sNewFile & ")" & @CRLF) ; FileDelete($sFolderPath & "\" & $sNewFile) until you are sure it will delete the right files Otherwise, you risk deleting everything except what you don't want. This makes sense, but... how do I know if it's actually doing anything? Thanks for the tips though
argumentum Posted July 24 Posted July 24 @sl23, do you have a virtual machine ? Hyper-V comes with windows pro. If you don't, I strongly advise to try all this in a VM as otherwise you may find yourself in a PC that don't run and without a PC to ask for help with. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
argumentum Posted July 24 Posted July 24 1 minute ago, argumentum said: you may find yourself in a PC that don't run and without a PC to ask for help with. ...just in case the advice would be: re-install the OS and restore from your last backup. PS: make backups. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
ioa747 Posted July 24 Posted July 24 Do the changes I suggested have ; *** <--- expandcollapse popup#include <File.au3> #include <MsgBoxConstants.au3> #include <Array.au3> Global $sRegistryKey = "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" Global $sFolderPath = @StartupDir ; Use the startup directory directly Global $sLogFile = @ScriptDir & "\startup_monitor_log.txt" ; Delete existing log file if it exists If FileExists($sLogFile) Then FileDelete($sLogFile) EndIf ; Initialize previous entries and files Global $aPreviousEntries = _GetRegistryEntries() _ArrayDisplay($aPreviousEntries, "$aPreviousEntries") ; *** <--- Global $aPreviousFiles = _FileListToArray($sFolderPath) _ArrayDisplay($aPreviousFiles, "$aPreviousFiles") ; *** <--- _LogChange("Starting Check for changes") ; *** <--- While True Sleep(5000) ; Check every 5 seconds ; Check for registry changes Local $aCurrentEntries = _GetRegistryEntries() If Not _ArraysEqual($aPreviousEntries, $aCurrentEntries) Then $aPreviousEntries = $aCurrentEntries Local $sNewEntry = _GetNewEntry($aPreviousEntries, $aCurrentEntries) _LogChange("A new startup entry has been detected: " & $sNewEntry) If MsgBox($MB_YESNO, "Startup Entry Detected", "A new startup entry has been detected: " & $sNewEntry & ". Do you want to allow it?") = $IDNO Then ; Remove the unauthorized entry ; RegDelete($sRegistryKey, $sNewEntry) ConsoleWrite("RegDelete(" & $sRegistryKey & ", " & $sNewEntry & ")" & @CRLF) ; *** <--- _LogChange("Denied startup entry: " & $sNewEntry) EndIf EndIf ; Check for new files in the startup folder Local $aCurrentFiles = _FileListToArray($sFolderPath) If Not _ArraysEqual($aPreviousFiles, $aCurrentFiles) Then $aPreviousFiles = $aCurrentFiles Local $sNewFile = _GetNewFile($aPreviousFiles, $aCurrentFiles) _LogChange("A new file has been detected in the startup folder: " & $sNewFile) If MsgBox($MB_YESNO, "File Detected", "A new file has been detected in the startup folder: " & $sNewFile & ". Do you want to allow it?") = $IDNO Then ; Remove the unauthorized file ; FileDelete($sFolderPath & "\" & $sNewFile) ConsoleWrite("FileDelete(" & $sFolderPath & "\" & $sNewFile & ")" & @CRLF) ; *** <--- _LogChange("Denied file: " & $sNewFile) EndIf EndIf WEnd ; Function to get registry entries Func _GetRegistryEntries() Local $aEntries[0] ; Start with an empty array Local $iIndex = 0 ; Read all values from the registry key While True Local $sValue = RegEnumVal($sRegistryKey, $iIndex) If @error Then ExitLoop ; Exit loop if no more values ReDim $aEntries[$iIndex + 1] ; Resize array to hold new entry $aEntries[$iIndex] = $sValue ; Store the entry $iIndex += 1 WEnd Return $aEntries ; Return the array of entries EndFunc ; Function to compare two arrays Func _ArraysEqual($aArray1, $aArray2) If UBound($aArray1) <> UBound($aArray2) Then Return False For $i = 0 To UBound($aArray1) - 1 If $aArray1[$i] <> $aArray2[$i] Then Return False Next Return True EndFunc ; Function to get the new entry Func _GetNewEntry($aOldEntries, $aNewEntries) For $i = 0 To UBound($aNewEntries) - 1 If Not _ArraySearch($aOldEntries, $aNewEntries[$i]) Then Return $aNewEntries[$i] EndIf Next Return "" EndFunc ; Function to get the new file Func _GetNewFile($aOldFiles, $aNewFiles) For $i = 0 To UBound($aNewFiles) - 1 If Not _ArraySearch($aOldFiles, $aNewFiles[$i]) Then Return $aNewFiles[$i] EndIf Next Return "" EndFunc ; Function to log changes Func _LogChange($sMessage) FileWrite($sLogFile, @YEAR & "-" & @MON & "-" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " - " & $sMessage) ; *** <--- EndFunc I know that I know nothing
sl23 Posted July 24 Author Posted July 24 (edited) No VM, but I have sandboxie-plus, is that good enough to test this out? ie, will it work with the registry and creating manual shortcuts in the sandboxed startup folder? @ioa747: Thanks for the help, you were too quick! I've just changed the code to adapt to use the first suggestion by Argumentum: _WinAPI_ReadDirectoryChanges($hDirectory, $iFilter, $pBuffer, but I don't know how to test for it or to get it to do what I need. $hDirectory A handle to the directory to be monitored. This directory must be opened with the $FILE_LIST_DIRECTORY access right. First off, do I put the drive/path/folder there, or do I use a variable? And what does the last part mean? Sorry for the noob questions! I won't be offended if you leave me to it! lol New code for file monitoring: removed EDIT: Updated the code with suggestions. Edited Wednesday at 08:04 PM by sl23
Nine Posted July 24 Posted July 24 Be careful, as per help file : Quote The _WinAPI_ReadDirectoryChanges() function works only in synchronous mode. It means it is a blocking function. You can use the unblocking feature but it is a tad more complicated. For an example, search the forum, there are a number of topics about it. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
argumentum Posted July 24 Posted July 24 6 minutes ago, sl23 said: No VM, but I have sandboxie-plus If you are comfortable with it, I guess. But nothing beats a VM. And don't forget to backup. And to backup your backup. Backups are more important than anything else ! 12 minutes ago, sl23 said: Argumentum: _WinAPI_ReadDirectoryChanges($hDirectory, $iFilter, $pBuffer, but I don't know how to test for it or to get it to do what I need. hmm, ..the example in the ZIP should be enough but, like @Nine said, search the forum. At this point it would be easier/faster to code what you want to have, as a birthday gift, why not. What you want to have is not that hard to put together, but very time consuming ( but am busy with other things 🤷♂️ ) Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
sl23 Posted July 24 Author Posted July 24 (edited) I tried VM but that's another beast I wasn't willing to tame due to no reason for learning it. Sandboxie is straightforward. I have plenty backups! I'm also good with reinstalling windows drivers where necessary. All my apps are portable and on D drive, so no installation required. ZIP? I missed that, but can't find it. EDIT: You mean the ForkUDF's? Ok, I'll look into that too. My birthday just gone, so, about this time next year? That's ok, I appreciate you taking time to help out. I'm going to try and read that PDF again see if I can start making sense of the code! Edited July 24 by sl23 argumentum 1
sl23 Posted Wednesday at 04:21 PM Author Posted Wednesday at 04:21 PM (edited) Just got back on this. The PDF reading is slow, but I got to chapter 5 and it's fairly basic and easy to understand so far. I've also got fed up with using DuckAI and tried Opera's ara and couple others. The limitations are seriously annoying and I can't afford or warrant a subscription to paid services. I then started using the GitHub free service which lasts for 30 days! That has given me a working script far quicker than DuckAI did. It's also error free and is actually doing it's job. Though I don't know how to make a registry entry to check it's working on that end. But adding a shortcut to windows startup folders is working exactly how I envisaged. Is there anything missing or anything that should be added like error checking, as gitAI mentioned it might need it. expandcollapse popup;***************************************** ; StartupMonitor64.au3 by sl23 ; Created with ISN AutoIt Studio v. 1.16 ; Compiled with AutoIt v3.3.16.1 ;***************************************** #AutoIt3Wrapper_icon=StartupMonitor.ico #AutoIt3Wrapper_Res_ProductVersion=0.0.11 #AutoIt3Wrapper_Res_Description=Monitor system startup entries. #AutoIt3Wrapper_Res_LegalCopyright=sl23 ; Generated by GitHub AI: #RequireAdmin #include <MsgBoxConstants.au3> #include <File.au3> #include <TrayConstants.au3> #include <Date.au3> ; ---------------------------------------------------------------------- ; StartupMonitor64 - Efficient, low-memory startup entry monitor ; ---------------------------------------------------------------------- ; --------------------------- ; Settings and File Paths ; --------------------------- Global Const $g_settingsFile = @ScriptDir & "\Settings.ini" Global Const $g_logFile = @ScriptDir & "\Log.ini" ; --------------------------- ; Create Settings File With Comments if Needed ; --------------------------- If Not FileExists($g_settingsFile) Then Local $hSet = FileOpen($g_settingsFile, $FO_OVERWRITE) If $hSet <> -1 Then FileWriteLine($hSet, "[Options]") FileWriteLine($hSet, "; Show tray icon in the notification area: 1 = show, 0 = hide") FileWriteLine($hSet, "ShowTrayIcon=1") FileWriteLine($hSet, "; New log file on start: 1 = New, 0 = Keep old log. Entries older than 30 days will be deleted.") FileWriteLine($hSet, "ClearLogOnStart=0") FileWriteLine($hSet, "; Time in milliseconds between monitoring checks. (Minimum 1500ms / Default 3000).") FileWriteLine($hSet, "MonitorTime=3000") FileClose($hSet) EndIf EndIf ; --------------------------- ; Read Settings ; --------------------------- Global $g_showTray = IniRead($g_settingsFile, "Options", "ShowTrayIcon", "1") Global $g_clearLog = IniRead($g_settingsFile, "Options", "ClearLogOnStart", "0") Global $g_monitorInterval = IniRead($g_settingsFile, "Options", "MonitorTime", "3000") If Number($g_monitorInterval) < 1500 Then $g_monitorInterval = 1500 ; --------------------------- ; Tray Icon Handling ; --------------------------- If $g_showTray = "1" Then TraySetState($TRAY_ICONSTATE_SHOW) Else TraySetState($TRAY_ICONSTATE_HIDE) EndIf ; --------------------------- ; Clear Log if Setting Enabled ; --------------------------- If $g_clearLog = "1" Then If FileExists($g_logFile) Then FileDelete($g_logFile) EndIf ; --------------------------- ; Prune Old Log Entries (Older than 30 days) ; Only keep recent log lines to save disk and memory ; --------------------------- _PruneOldLogEntries_LineByLine() ; --------------------------- ; Log program start with current tray status ; --------------------------- _LogWrite("Startup Monitor started. Tray icon is " & ($g_showTray = "1" ? "visible." : "hidden.")) ; --------------------------- ; Get Initial Startup Entries ; Uses a map/dictionary for memory efficiency and fast lookup ; --------------------------- Global $g_oldEntries = _GetAllStartupEntries_Map() ; --------------------------- ; Main Monitoring Loop ; Checks for new startup entries at user-defined interval ; --------------------------- While 1 Sleep(Number($g_monitorInterval)) ; User-configurable interval (ms), min 1500ms ; Collect all current startup entries as a map Local $newEntries = _GetAllStartupEntries_Map() ; Find any entries that are new (not in previous scan) Local $added = _MapDiff($newEntries, $g_oldEntries) ; If new entries found, prompt user for each one If $added.Count > 0 Then For $entry In $added.Keys _LogWrite("Detected new startup entry: " & $entry) Local $response = MsgBox($MB_ICONQUESTION + $MB_YESNO + $MB_TOPMOST, "Startup Entry Detected", _ "A new startup entry was added:" & @CRLF & $entry & @CRLF & "Allow?") If $response = $IDNO Then _LogWrite("User denied entry, removing: " & $entry) _RemoveStartupEntry($entry) Else _LogWrite("User allowed entry: " & $entry) EndIf Next EndIf ; Replace old entries with the latest scan for next loop iteration $g_oldEntries = $newEntries ; Release memory from local objects (optional in AutoIt, but helps clarity) $newEntries = 0 $added = 0 WEnd ; ------------------------------------------------------------------------------ ; _GetAllStartupEntries_Map: Collects all current startup entries from registry ; and startup folders, returns as a Scripting.Dictionary (map) ; ------------------------------------------------------------------------------ Func _GetAllStartupEntries_Map() Local $dict = ObjCreate("Scripting.Dictionary") _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", $dict) _GetRegEntries_Map("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", $dict) _GetFolderEntries_Map(@StartupDir, $dict) _GetFolderEntries_Map(@StartupCommonDir, $dict) Return $dict EndFunc ; ------------------------------------------------------------------------------ ; _GetRegEntries_Map: Adds value names from the given registry key to the map ; ------------------------------------------------------------------------------ Func _GetRegEntries_Map($key, ByRef $dict) Local $i = 1, $val While 1 $val = RegEnumVal($key, $i) If @error Then ExitLoop $dict.Add($key & "\" & $val, "") ; Use full path as dictionary key $i += 1 WEnd EndFunc ; ------------------------------------------------------------------------------ ; _GetFolderEntries_Map: Adds all file names from the given folder to the map ; ------------------------------------------------------------------------------ Func _GetFolderEntries_Map($folder, ByRef $dict) Local $search = FileFindFirstFile($folder & "\*.*") If $search = -1 Then Return While 1 Local $file = FileFindNextFile($search) If @error Then ExitLoop $dict.Add($folder & "\" & $file, "") WEnd FileClose($search) EndFunc ; ------------------------------------------------------------------------------ ; _MapDiff: Returns keys in dictA not present in dictB as a new map (dictionary) ; ------------------------------------------------------------------------------ Func _MapDiff($dictA, $dictB) Local $diff = ObjCreate("Scripting.Dictionary") For $key In $dictA.Keys If Not $dictB.Exists($key) Then $diff.Add($key, "") Next Return $diff EndFunc ; ------------------------------------------------------------------------------ ; _RemoveStartupEntry: Removes the specified startup entry from registry or folder ; ------------------------------------------------------------------------------ Func _RemoveStartupEntry($entry) ; Remove from registry if entry is registry path, else it's a file (folder) If StringLeft($entry, 18) = "HKEY_LOCAL_MACHINE" Then Local $remain = StringTrimLeft($entry, 19) Local $lastSep = StringInStr($remain, "\", 0, -1) Local $key = "HKEY_LOCAL_MACHINE\" & StringLeft($remain, $lastSep - 1) Local $val = StringTrimLeft($remain, $lastSep) RegDelete($key, $val) ElseIf StringLeft($entry, 17) = "HKEY_CURRENT_USER" Then Local $remain = StringTrimLeft($entry, 18) Local $lastSep = StringInStr($remain, "\", 0, -1) Local $key = "HKEY_CURRENT_USER\" & StringLeft($remain, $lastSep - 1) Local $val = StringTrimLeft($remain, $lastSep) RegDelete($key, $val) Else FileDelete($entry) EndIf EndFunc ; ------------------------------------------------------------------------------ ; _LogWrite: Appends a timestamped line to the log file, with '=' as separator ; ------------------------------------------------------------------------------ Func _LogWrite($sText) Local $hFile = FileOpen($g_logFile, $FO_APPEND) If $hFile = -1 Then Return FileWriteLine($hFile, @YEAR & "-" & @MON & "-" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " = " & $sText) FileClose($hFile) EndFunc ; ------------------------------------------------------------------------------ ; _PruneOldLogEntries_LineByLine: Keeps only log entries from the last 30 days, ; processing line-by-line (very low memory usage) ; ------------------------------------------------------------------------------ Func _PruneOldLogEntries_LineByLine() If Not FileExists($g_logFile) Then Return Local $nowTS = _DateToTS(@YEAR & "-" & @MON & "-" & @MDAY) Local $tempFile = @ScriptDir & "\_tmp_log.txt" Local $rfh = FileOpen($g_logFile, $FO_READ) If $rfh = -1 Then Return Local $wfh = FileOpen($tempFile, $FO_OVERWRITE) If $wfh = -1 Then FileClose($rfh) Return EndIf ; Process log one line at a time While 1 Local $line = FileReadLine($rfh) If @error Then ExitLoop Local $dateStr = StringLeft($line, 10) ; "YYYY-MM-DD" If StringRegExp($dateStr, "^\d{4}-\d{2}-\d{2}$") Then Local $lineTS = _DateToTS($dateStr) If $nowTS - $lineTS <= 2592000 Then ; 30 days in seconds FileWriteLine($wfh, $line) EndIf Else FileWriteLine($wfh, $line) ; Keep malformed/other lines EndIf WEnd FileClose($rfh) FileClose($wfh) FileDelete($g_logFile) FileMove($tempFile, $g_logFile, 1) EndFunc ; ------------------------------------------------------------------------------ ; _DateToTS: Convert YYYY-MM-DD to timestamp (seconds since epoch) ; ------------------------------------------------------------------------------ Func _DateToTS($sDate) Local $a = StringSplit($sDate, "-") If $a[0] <> 3 Then Return 0 Return _DateDiff("s", "1970/01/01 00:00:00", $a[1] & "/" & $a[2] & "/" & $a[3] & " 00:00:00") EndFunc It would be really helpful if someone could check it over and see if there's anything wrong or missing, thank you so much for your help and advice. After some more conversing with github AI, I managed to get the revised code above. It now has a settings file and logs all entries. There's only two settings in the file, the defaults are: [Options] ShowTrayIcon=1 ClearLogOnStart=0 MonitorTime=3000 This is looking good so far, but unless I can get some confirmation from the experts here, I am wary of using it! So Please help me to improve it's safety, as that's the priority at the moment. Thanks EDIT: Implemented better checking of registry keys before deletion. Added log trimming. Entries older than 30 days are deleted. Changed Log from TXT to INI. Added Setting for polling rate, with a safe minimum of 1500 milliseconds, anything below this will be rounded up to 1500ms. Fixed repeated same entries not being detected. StartupMonitor.ico Edited Wednesday at 08:45 PM by sl23 Revised code
sl23 Posted Wednesday at 07:45 PM Author Posted Wednesday at 07:45 PM (edited) Ok, so this code seems to be working fine. I've compiled it to it's latest version and it's detecting and deleting entries to the windows start up folders for user and all. It is also detecting and deleting registry entries that appear in the user and machine RUN sections. Here is a test version of StartupMonitor64, source code included, should anyone feel brave enough to test it! EDIT: I added the icon to the above post with the source code! Edited Wednesday at 08:45 PM by sl23
Developers Jos Posted Wednesday at 08:20 PM Developers Posted Wednesday at 08:20 PM (edited) Please do not attach zip files with compiled scripts as some AV/Search companies scan this stuff and sometimes decide to declare this website unsafe! So just publish the source in a code tag. 🙂 Your current attachment is removed. Edited Wednesday at 08:21 PM by Jos SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past.
argumentum Posted Wednesday at 08:38 PM Posted Wednesday at 08:38 PM ..I remember that there were more registry entries and asked google "all registry for Run runonce etc" and the AI thing ppoped up as said: AI Overview Configure a RunOnce task on Windows The Windows Registry contains several keys that execute commands or programs at startup. The most relevant ones are Run, RunOnce, RunServices, and RunServicesOnce, found under both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER. Run executes a command each time a user logs in, while RunOnce executes it only once, then deletes the entry. The "Services" keys are used for services that start before the user logs in. Here's a breakdown of the key locations: HKEY_LOCAL_MACHINE (HKLM): HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run: Runs commands for all users on the system at each logon. HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce: Runs commands for all users once, then deletes them. HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices: Starts services before the user logs in, for all users. HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce: Starts services once before the user logs in, then deletes them. HKEY_CURRENT_USER (HKCU): HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run: Runs commands for the specific logged-in user at each logon. HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce: Runs commands for the specific logged-in user once, then deletes them. HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices: Starts services before the specific logged-in user logs in. HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce: Starts services once for the specific logged-in user before they log in, then deletes them. Additional Keys: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute: . This key is used to specify commands that run during the very early stages of system boot, before user logon. HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\Load: . This key is used to load specific programs for the currently logged-in user upon logon, according to Super User. Note: The RunOnce keys are often used for installation programs or actions that should only happen once after a system or user-specific change. The RunServices keys are used for services that need to start very early in the boot process. The presence of these keys, and the commands they execute, can significantly impact system behavior and are often targeted by malware for persistence. and I see that you only have 2. And yes, what a mod. said: no exe in the text forum area. But the script and the icon are very welcomed ( and am one to share an icon when I make 'em too ) Also, this is a good read ( https://www.socinvestigation.com/monitor-modified-registry-keys-possible-windows-event-id/ ) and this one too ( https://stackoverflow.com/questions/56274139/task-scheduler-run-on-event-for-a-specific-task-only ) I know that am flooding you with a bunch of info, but I don't want you to get a false sense of security, thinking that is good enough. PS: "Just because you're paranoid doesn't mean they aren't out to get you" Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
sl23 Posted Wednesday at 08:41 PM Author Posted Wednesday at 08:41 PM Apologies, I didn't know that was a thing. Just thought it was standard practice. Sorry... 😟
sl23 Posted Wednesday at 08:43 PM Author Posted Wednesday at 08:43 PM Thanks argumentum. I did wonder how many there were, but never got round to asking that. I've used up 80% of my chat time on github for the month so maybe this won't get sorted for a while. but good to know about those extra entries, thank you. argumentum 1
sl23 Posted Thursday at 09:30 AM Author Posted Thursday at 09:30 AM I managed to get one last request from GitHub Copilot! I added all those locations to be monitored and added them to the settings. I did this because it may be too much in some situations where there could be too many alerts. I have also to check whether the script will detect multiple new entries to startup, but this will have to wait a month as I've run out of my 'allowance.' I'll keep looking at it to try and learn some more and read that PDF too. Using AI has been quite interesting and has helped me see the structure of things a little. When you first start out, looking at even a small script like this is daunting! So I'm happy that it turned out so well. GitHub Copilot is definitely far more useful in this context. I know it's not my own creation, but for me, it's about the end result. Anyway, here's the new 'improved' code: expandcollapse popup;***************************************** ; StartupMonitor64.au3 by sl23 ; Created with ISN AutoIt Studio v. 1.16 ; Compiled with AutoIt v3.3.16.1 ;***************************************** #AutoIt3Wrapper_icon=StartupMonitor.ico #AutoIt3Wrapper_Res_FileVersion_AutoIncrement=n #AutoIt3Wrapper_Res_ProductVersion=0.0.12 #AutoIt3Wrapper_Res_Description=Monitor system startup entries. #AutoIt3Wrapper_Res_LegalCopyright=sl23 ; Generated by GitHub AI: https://github.com/copilot/share/0a5411ba-4bc4-88e1-9941-8044400d4976 #RequireAdmin #include <MsgBoxConstants.au3> #include <File.au3> #include <TrayConstants.au3> #include <Date.au3> ; ---------------------------------------------------------------------- ; Settings and File Paths ; ---------------------------------------------------------------------- Global Const $g_settingsFile = @ScriptDir & "\Settings.ini" Global Const $g_logFile = @ScriptDir & "\Log.ini" ; ---------------------------------------------------------------------- ; Create Settings File With Comments if Needed ; ---------------------------------------------------------------------- If Not FileExists($g_settingsFile) Then Local $hSet = FileOpen($g_settingsFile, $FO_OVERWRITE) If $hSet <> -1 Then FileWriteLine($hSet, "[Options]") FileWriteLine($hSet, "; Show tray icon in the notification area: 1 = show, 0 = hide") FileWriteLine($hSet, "ShowTrayIcon=1") FileWriteLine($hSet, "; New log file on start: 1 = New, 0 = Keep old log. Entries older than 30 days will be deleted.") FileWriteLine($hSet, "ClearLogOnStart=0") FileWriteLine($hSet, "; Time in milliseconds between monitoring checks. (Minimum 1500ms / Default 3000).") FileWriteLine($hSet, "MonitorTime=3000") FileWriteLine($hSet, "") FileWriteLine($hSet, "[ExtraStartupChecks]") FileWriteLine($hSet, "; Enable (1) or disable (0) each extra startup location.") FileWriteLine($hSet, "HKCU_RunOnce=1") FileWriteLine($hSet, "HKLM_RunOnce=1") FileWriteLine($hSet, "HKCU_Explorer_UserShellFolders=1") FileWriteLine($hSet, "HKCU_Explorer_ShellFolders=1") FileWriteLine($hSet, "HKLM_Explorer_ShellFolders=1") FileWriteLine($hSet, "HKLM_Explorer_UserShellFolders=1") FileWriteLine($hSet, "HKLM_RunServicesOnce=1") FileWriteLine($hSet, "HKCU_RunServicesOnce=1") FileWriteLine($hSet, "HKLM_RunServices=1") FileWriteLine($hSet, "HKCU_RunServices=1") FileWriteLine($hSet, "HKLM_Policies_Explorer_Run=1") FileWriteLine($hSet, "HKCU_Policies_Explorer_Run=1") FileWriteLine($hSet, "HKLM_Winlogon_Userinit=1") FileWriteLine($hSet, "HKLM_Winlogon_Shell=1") FileWriteLine($hSet, "HKCU_Windows=1") FileWriteLine($hSet, "HKLM_SessionManager=1") FileClose($hSet) EndIf EndIf ; ---------------------------------------------------------------------- ; Read Settings ; ---------------------------------------------------------------------- Global $g_showTray = IniRead($g_settingsFile, "Options", "ShowTrayIcon", "1") Global $g_clearLog = IniRead($g_settingsFile, "Options", "ClearLogOnStart", "0") Global $g_monitorInterval = IniRead($g_settingsFile, "Options", "MonitorTime", "3000") If Number($g_monitorInterval) < 1500 Then $g_monitorInterval = 1500 ; Read Extra Checks Global $g_extraChecks[16] = [ _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_RunOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_RunOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Explorer_UserShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Explorer_ShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Explorer_ShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Explorer_UserShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_RunServicesOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_RunServicesOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_RunServices", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_RunServices", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Policies_Explorer_Run", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Policies_Explorer_Run", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Winlogon_Userinit", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Winlogon_Shell", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Windows", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_SessionManager", "1") _ ] ; ---------------------------------------------------------------------- ; Tray Icon Handling ; ---------------------------------------------------------------------- If $g_showTray = "1" Then TraySetState($TRAY_ICONSTATE_SHOW) Else TraySetState($TRAY_ICONSTATE_HIDE) EndIf ; ---------------------------------------------------------------------- ; Clear Log if Setting Enabled ; ---------------------------------------------------------------------- If $g_clearLog = "1" Then If FileExists($g_logFile) Then FileDelete($g_logFile) EndIf ; ---------------------------------------------------------------------- ; Prune Old Log Entries (Older than 30 days) ; ---------------------------------------------------------------------- _PruneOldLogEntries_LineByLine() ; ---------------------------------------------------------------------- ; Log program start with current tray status ; ---------------------------------------------------------------------- _LogWrite("Startup Monitor started. Tray icon is " & ($g_showTray = "1" ? "visible." : "hidden.")) ; ---------------------------------------------------------------------- ; Get Initial Startup Entries ; ---------------------------------------------------------------------- Global $g_oldEntries = _GetAllStartupEntries_Map() ; ---------------------------------------------------------------------- ; Main Monitoring Loop ; ---------------------------------------------------------------------- While 1 Sleep(Number($g_monitorInterval)) Local $newEntries = _GetAllStartupEntries_Map() Local $added = _MapDiff($newEntries, $g_oldEntries) If $added.Count > 0 Then For $entry In $added.Keys _LogWrite("Detected new startup entry: " & $entry) Local $response = MsgBox($MB_ICONQUESTION + $MB_YESNO + $MB_TOPMOST, "Startup Entry Detected", _ "A new startup entry was added:" & @CRLF & $entry & @CRLF & "Allow?") If $response = $IDNO Then _LogWrite("User denied entry, removing: " & $entry) _RemoveStartupEntry($entry) Else _LogWrite("User allowed entry: " & $entry) EndIf Next EndIf $g_oldEntries = $newEntries $newEntries = 0 $added = 0 WEnd ; ------------------------------------------------------------------------------ ; _GetAllStartupEntries_Map: Collect all current startup entries from registry ; and startup folders, and from optionally enabled extra registry locations ; ------------------------------------------------------------------------------ Func _GetAllStartupEntries_Map() Local $dict = ObjCreate("Scripting.Dictionary") _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", $dict) _GetRegEntries_Map("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", $dict) _GetFolderEntries_Map(@StartupDir, $dict) _GetFolderEntries_Map(@StartupCommonDir, $dict) ; Check extra locations based on settings (order matches $g_extraChecks) If $g_extraChecks[0] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce", $dict) If $g_extraChecks[1] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce", $dict) If $g_extraChecks[2] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", $dict) If $g_extraChecks[3] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", $dict) If $g_extraChecks[4] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", $dict) If $g_extraChecks[5] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", $dict) If $g_extraChecks[6] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce", $dict) If $g_extraChecks[7] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce", $dict) If $g_extraChecks[8] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices", $dict) If $g_extraChecks[9] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices", $dict) If $g_extraChecks[10] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run", $dict) If $g_extraChecks[11] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run", $dict) If $g_extraChecks[12] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit", $dict) If $g_extraChecks[13] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell", $dict) If $g_extraChecks[14] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows", $dict) If $g_extraChecks[15] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager", $dict) Return $dict EndFunc ; ------------------------------------------------------------------------------ ; _GetRegEntries_Map: Adds value names from the given registry key to the map ; ------------------------------------------------------------------------------ Func _GetRegEntries_Map($key, ByRef $dict) Local $i = 1, $val While 1 $val = RegEnumVal($key, $i) If @error Then ExitLoop $dict.Add($key & "\" & $val, "") $i += 1 WEnd EndFunc ; ------------------------------------------------------------------------------ ; _GetFolderEntries_Map: Adds all file names from the given folder to the map ; ------------------------------------------------------------------------------ Func _GetFolderEntries_Map($folder, ByRef $dict) Local $search = FileFindFirstFile($folder & "\*.*") If $search = -1 Then Return While 1 Local $file = FileFindNextFile($search) If @error Then ExitLoop $dict.Add($folder & "\" & $file, "") WEnd FileClose($search) EndFunc ; ------------------------------------------------------------------------------ ; _MapDiff: Returns keys in dictA not present in dictB as a new map (dictionary) ; ------------------------------------------------------------------------------ Func _MapDiff($dictA, $dictB) Local $diff = ObjCreate("Scripting.Dictionary") For $key In $dictA.Keys If Not $dictB.Exists($key) Then $diff.Add($key, "") Next Return $diff EndFunc ; ------------------------------------------------------------------------------ ; _RemoveStartupEntry: Removes the specified startup entry from registry or folder ; ------------------------------------------------------------------------------ Func _RemoveStartupEntry($entry) ; Remove from registry if entry is registry path, else it's a file (folder) If StringLeft($entry, 18) = "HKEY_LOCAL_MACHINE" Then Local $remain = StringTrimLeft($entry, 19) Local $lastSep = StringInStr($remain, "\", 0, -1) Local $key = "HKEY_LOCAL_MACHINE\" & StringLeft($remain, $lastSep - 1) Local $val = StringTrimLeft($remain, $lastSep) RegDelete($key, $val) ElseIf StringLeft($entry, 17) = "HKEY_CURRENT_USER" Then Local $remain = StringTrimLeft($entry, 18) Local $lastSep = StringInStr($remain, "\", 0, -1) Local $key = "HKEY_CURRENT_USER\" & StringLeft($remain, $lastSep - 1) Local $val = StringTrimLeft($remain, $lastSep) RegDelete($key, $val) Else FileDelete($entry) EndIf EndFunc ; ------------------------------------------------------------------------------ ; _LogWrite: Appends a timestamped line to the log file, with '=' as separator ; ------------------------------------------------------------------------------ Func _LogWrite($sText) Local $hFile = FileOpen($g_logFile, $FO_APPEND) If $hFile = -1 Then Return FileWriteLine($hFile, @YEAR & "-" & @MON & "-" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " = " & $sText) FileClose($hFile) EndFunc ; ------------------------------------------------------------------------------ ; _PruneOldLogEntries_LineByLine: Keeps only log entries from the last 30 days ; ------------------------------------------------------------------------------ Func _PruneOldLogEntries_LineByLine() If Not FileExists($g_logFile) Then Return Local $nowTS = _DateToTS(@YEAR & "-" & @MON & "-" & @MDAY) Local $tempFile = @ScriptDir & "\_tmp_log.txt" Local $rfh = FileOpen($g_logFile, $FO_READ) If $rfh = -1 Then Return Local $wfh = FileOpen($tempFile, $FO_OVERWRITE) If $wfh = -1 Then FileClose($rfh) Return EndIf While 1 Local $line = FileReadLine($rfh) If @error Then ExitLoop Local $dateStr = StringLeft($line, 10) If StringRegExp($dateStr, "^\d{4}-\d{2}-\d{2}$") Then Local $lineTS = _DateToTS($dateStr) If $nowTS - $lineTS <= 2592000 Then FileWriteLine($wfh, $line) EndIf Else FileWriteLine($wfh, $line) EndIf WEnd FileClose($rfh) FileClose($wfh) FileDelete($g_logFile) FileMove($tempFile, $g_logFile, 1) EndFunc ; ------------------------------------------------------------------------------ ; _DateToTS: Convert YYYY-MM-DD to timestamp (seconds since epoch) ; ------------------------------------------------------------------------------ Func _DateToTS($sDate) Local $a = StringSplit($sDate, "-") If $a[0] <> 3 Then Return 0 Return _DateDiff("s", "1970/01/01 00:00:00", $a[1] & "/" & $a[2] & "/" & $a[3] & " 00:00:00") EndFunc The only issue I have is to know if it's working, ie, how to add startup entries on purpose. Perhaps a PS1 script could help with that? Btw, this was never meant to be a security app as such, I just found it useful to stop things like edge taking over. But with the option to enable/disable specific locations to monitor, I think this is a better version. When I can use the GitAI again, I will add tasks to be monitored too, cos I have portablised DriverBooster and it keeps adding settings there and it's really annoying!!! Even though I sorta solved it through X-Launcher config. See you in a month, or maybe sooner! lol Thanks for the tips and advice.
argumentum Posted Thursday at 01:38 PM Posted Thursday at 01:38 PM ... ; ---------------------------------------------------------------------- ; Get Initial Startup Entries ; ---------------------------------------------------------------------- Global $g_oldEntries = _GetAllStartupEntries_Map() ... It would be good to save those entries to an organized file and load from there because, if you run this sporadically or something changes between your script running and something else, on reboot or whatnot, then you'll be able to see that there was a changed. Mostly if you're looking for sneaky things like Edge sl23 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
sl23 Posted 14 hours ago Author Posted 14 hours ago Just to get this right, are you saying that the program should create a second log of all existing and adding future allowed items using that as a basis for it's checks? So, for example, first run checks current state of startup, logs that to a file, adds any new entries that you allow and uses that as a basis for monitoring? But doesn't that leave it open to possible errors? Or do I have it wrong? Could you explain a little please.
argumentum Posted 10 hours ago Posted 10 hours ago (edited) You are comparing what you have now with what you'll have in X seconds, all in memory. When you load your script again, it'll do the same. If you saved the last known state, next time you load the script you will know for a fact if anything changed since you last run the script because you have it in a file with the last known state, and not just what changed while running the script That will necessitate comparing each entry and not just a count of how many. Also, as a bad actor, one could replace an entry to keep the count intact or other reasons to just edit an entry. Spoiler Spoiler ..unless I've got it wrong ( don't remember the code clearly ) Edited 10 hours ago by argumentum sl23 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
sl23 Posted 10 hours ago Author Posted 10 hours ago (edited) Ok, so I've been at it again and added a load of stuff! I'll try and list what's changed: Added monitoring of scheduled tasks with Whitelist/Blacklists. Every alert will add to one or the other. Added option to alert user if a blacklisted item is added, this alert can be disabled. Added persistence between sessions. Added option to disable persistence, in case you just want a simple app to check for basic startup additions. I have tested with windows folders and the two original registry locations but not the rest listed. I also tested the tasks and DriverBooster tasks are alerted and deleted upon user request, they are also added to the blacklist while allowed items are being correctly added to whitelist. Anything in the Blacklist will be auto-deleted and alert users with a message, you can disable the message in the settings file. I think that's about it, as well as some cosmetic changes like text, spacing, icon change to the warning icon. Is the code sound? Anything else you think should be added? I did ask AI about recommendations and if the code was reliable and efficient. I got seevral answers and requested they be added, but not 100% sure everything has been added. It mentioned about error checking, and some other stuff, will have to check the chat to see what. The compiled EXE uses around 7.5MB in use and varies between 0% and 0.1% for CPU. What do you think? expandcollapse popup;***************************************** ; StartupMonitor64.au3 by sl23 ; Created with ISN AutoIt Studio v1.16 ; Compiled with AutoIt v3.3.16.1 ;***************************************** #AutoIt3Wrapper_icon=StartupMonitor.ico #AutoIt3Wrapper_Res_FileVersion_AutoIncrement=y #AutoIt3Wrapper_Res_Fileversion=0.0.0.31 #AutoIt3Wrapper_Res_ProductVersion=3.3.16.1 #AutoIt3Wrapper_Res_Description=StartupMonitor64 #AutoIt3Wrapper_Res_LegalCopyright=sl23 ; Generated by GitHub Copilot AI: #RequireAdmin #SingleInstance Force #include <MsgBoxConstants.au3> #include <File.au3> #include <TrayConstants.au3> #include <Date.au3> #include <Array.au3> ; ---------------------------------------------------------------------- ; Settings and File Paths ; ---------------------------------------------------------------------- Global Const $g_settingsFile = @ScriptDir & "\Settings.ini" Global Const $g_logFile = @ScriptDir & "\Log.ini" Global Const $g_whitelistFile = @ScriptDir & "\Whitelist.ini" Global Const $g_blacklistFile = @ScriptDir & "\Blacklist.ini" Global Const $g_initialStartupFile = @ScriptDir & "\InitialStartupEntries.ini" Global Const $g_initialTasksFile = @ScriptDir & "\InitialScheduledTasks.ini" ; ---------------------------------------------------------------------- ; Create Settings File With Comments if Needed ; ---------------------------------------------------------------------- If Not FileExists($g_settingsFile) Then Local $hSet = FileOpen($g_settingsFile, $FO_OVERWRITE) If $hSet <> -1 Then FileWriteLine($hSet, "[Options]") FileWriteLine($hSet, "; Show tray icon in the notification area: Show (1), Hide (0)") FileWriteLine($hSet, "ShowTrayIcon=1") FileWriteLine($hSet, "; New log file on start: New (1), Keep old log (0). Entries older than 30 days will be deleted.") FileWriteLine($hSet, "ClearLogOnStart=0") FileWriteLine($hSet, "; Time in milliseconds between monitoring checks. (Minimum 1500ms / Default 3000).") FileWriteLine($hSet, "MonitorTime=3000") FileWriteLine($hSet, "; Show messagebox for auto-removed blacklist items: Enable (1), Disable (0)") FileWriteLine($hSet, "ShowBlacklistMsgBox=1") FileWriteLine($hSet, "; Save/load persistent baseline snapshot of startup entries and scheduled tasks: Enable (1), Disable (0)") FileWriteLine($hSet, "PersistentBaseline=1") FileWriteLine($hSet, "; Monitor scheduled tasks: Enable (1), Disable (0)") FileWriteLine($hSet, "MonitorTasks=1") FileWriteLine($hSet, "") FileWriteLine($hSet, "[ExtraStartupChecks]") FileWriteLine($hSet, "; Enable (1) or disable (0) each extra startup location.") FileWriteLine($hSet, "HKCU_RunOnce=1") FileWriteLine($hSet, "HKLM_RunOnce=1") FileWriteLine($hSet, "HKCU_Explorer_UserShellFolders=1") FileWriteLine($hSet, "HKCU_Explorer_ShellFolders=1") FileWriteLine($hSet, "HKLM_Explorer_ShellFolders=1") FileWriteLine($hSet, "HKLM_Explorer_UserShellFolders=1") FileWriteLine($hSet, "HKLM_RunServicesOnce=1") FileWriteLine($hSet, "HKCU_RunServicesOnce=1") FileWriteLine($hSet, "HKLM_RunServices=1") FileWriteLine($hSet, "HKCU_RunServices=1") FileWriteLine($hSet, "HKLM_Policies_Explorer_Run=1") FileWriteLine($hSet, "HKCU_Policies_Explorer_Run=1") FileWriteLine($hSet, "HKLM_Winlogon_Userinit=1") FileWriteLine($hSet, "HKLM_Winlogon_Shell=1") FileWriteLine($hSet, "HKCU_Windows=1") FileWriteLine($hSet, "HKLM_SessionManager=1") FileClose($hSet) EndIf EndIf ; ---------------------------------------------------------------------- ; Create Whitelist File if Needed ; ---------------------------------------------------------------------- If Not FileExists($g_whitelistFile) Then Local $hWhite = FileOpen($g_whitelistFile, $FO_OVERWRITE) If $hWhite <> -1 Then FileWriteLine($hWhite, "; Whitelisted startup/task entries for StartupMonitor64") FileClose($hWhite) EndIf EndIf ; ---------------------------------------------------------------------- ; Create Blacklist File if Needed ; ---------------------------------------------------------------------- If Not FileExists($g_blacklistFile) Then Local $hBlack = FileOpen($g_blacklistFile, $FO_OVERWRITE) If $hBlack <> -1 Then FileWriteLine($hBlack, "; Blacklisted startup/task entries for StartupMonitor64") FileWriteLine($hBlack, "; Add entries under [Startup] or [Task] sections, one per line as a key (value can be left blank)") FileWriteLine($hBlack, "[Startup]") FileWriteLine($hBlack, ";Example: badprogram.exe") FileWriteLine($hBlack, "[Task]") FileWriteLine($hBlack, ";Example: bad task name") FileClose($hBlack) EndIf EndIf ; ---------------------------------------------------------------------- ; Read Settings ; ---------------------------------------------------------------------- Global $g_showTray = IniRead($g_settingsFile, "Options", "ShowTrayIcon", "1") Global $g_clearLog = IniRead($g_settingsFile, "Options", "ClearLogOnStart", "0") Global $g_monitorInterval = IniRead($g_settingsFile, "Options", "MonitorTime", "3000") Global $g_showBlacklistMsgBox = IniRead($g_settingsFile, "Options", "ShowBlacklistMsgBox", "1") Global $g_persistentBaseline = IniRead($g_settingsFile, "Options", "PersistentBaseline", "1") Global $g_monitorTasks = IniRead($g_settingsFile, "Options", "MonitorTasks", "1") If Number($g_monitorInterval) < 1500 Then $g_monitorInterval = 1500 ; Read Extra Checks Global $g_extraChecks = [ _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_RunOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_RunOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Explorer_UserShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Explorer_ShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Explorer_ShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Explorer_UserShellFolders", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_RunServicesOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_RunServicesOnce", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_RunServices", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_RunServices", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Policies_Explorer_Run", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Policies_Explorer_Run", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Winlogon_Userinit", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_Winlogon_Shell", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKCU_Windows", "1"), _ IniRead($g_settingsFile, "ExtraStartupChecks", "HKLM_SessionManager", "1") _ ] ; ---------------------------------------------------------------------- ; Tray Icon Handling ; ---------------------------------------------------------------------- If $g_showTray = "1" Then TraySetState($TRAY_ICONSTATE_SHOW) Else TraySetState($TRAY_ICONSTATE_HIDE) EndIf ; ---------------------------------------------------------------------- ; Clear Log if Setting Enabled ; ---------------------------------------------------------------------- If $g_clearLog = "1" Then If FileExists($g_logFile) Then FileDelete($g_logFile) EndIf ; ---------------------------------------------------------------------- ; Prune Old Log Entries (Older than 30 days) ; ---------------------------------------------------------------------- _PruneOldLogEntries_LineByLine() ; ---------------------------------------------------------------------- ; Log program start with current tray status ; ---------------------------------------------------------------------- _LogWrite("Startup Monitor started. Tray icon is " & ($g_showTray = "1" ? "visible." : "hidden.")) ; ---------------------------------------------------------------------- ; Persistent Baseline Snapshot Management ; ---------------------------------------------------------------------- Global $g_initialEntries, $g_initialTasks If $g_persistentBaseline = "1" Then ; -- Startup Entries Baseline -- If FileExists($g_initialStartupFile) Then $g_initialEntries = _LoadSnapshotEntries($g_initialStartupFile) Else $g_initialEntries = _GetAllStartupEntries_Map(True) _SaveSnapshotEntries($g_initialEntries, $g_initialStartupFile) EndIf ; -- Scheduled Tasks Baseline -- If $g_monitorTasks = "1" Then If FileExists($g_initialTasksFile) Then $g_initialTasks = _LoadSnapshotEntries($g_initialTasksFile) Else $g_initialTasks = _GetAllScheduledTasks_Map(True) _SaveSnapshotEntries($g_initialTasks, $g_initialTasksFile) EndIf EndIf EndIf ; ---------------------------------------------------------------------- ; Get Initial Startup Entries and Scheduled Tasks (with metadata) ; ---------------------------------------------------------------------- Global $g_oldEntries = _GetAllStartupEntries_Map(True) Global $g_oldTasks If $g_monitorTasks = "1" Then $g_oldTasks = _GetAllScheduledTasks_Map(True) EndIf ; ---------------------------------------------------------------------- ; Baseline diff detection on startup (immediate detection of changes made while not running) ; ---------------------------------------------------------------------- If $g_persistentBaseline = "1" Then _ReportBaselineDiff("Startup", $g_initialEntries, $g_oldEntries) If $g_monitorTasks = "1" Then _ReportBaselineDiff("Task", $g_initialTasks, $g_oldTasks) EndIf EndIf ; ---------------------------------------------------------------------- ; Main Monitoring Loop ; ---------------------------------------------------------------------- While 1 Sleep(Number($g_monitorInterval)) ; --- Startup Entries Monitoring --- Local $newEntries = _GetAllStartupEntries_Map(True) _CheckAndHandleChanges("Startup", $g_oldEntries, $newEntries) If $g_persistentBaseline = "1" Then _ReportBaselineDiff("Startup", $g_initialEntries, $newEntries) EndIf $g_oldEntries = $newEntries ; --- Scheduled Tasks Monitoring --- If $g_monitorTasks = "1" Then Local $newTasks = _GetAllScheduledTasks_Map(True) _CheckAndHandleChanges("Task", $g_oldTasks, $newTasks) If $g_persistentBaseline = "1" Then _ReportBaselineDiff("Task", $g_initialTasks, $newTasks) EndIf $g_oldTasks = $newTasks EndIf WEnd ; ------------------------------------------------------------------------------ ; Persistent Baseline: Save and Load (for both startup and tasks) ; ------------------------------------------------------------------------------ Func _SaveSnapshotEntries($dict, $file) Local $h = FileOpen($file, $FO_OVERWRITE) If $h = -1 Then Return For $key In $dict.Keys Local $val = $dict.Item($key) FileWriteLine($h, $key & "=" & $val[0]) Next FileClose($h) EndFunc Func _LoadSnapshotEntries($file) Local $dict = ObjCreate("Scripting.Dictionary") If Not FileExists($file) Then Return $dict Local $arr = FileReadToArray($file) If @error Then Return $dict For $i = 0 To UBound($arr)-1 Local $line = $arr[$i] If StringLeft($line, 1) = ";" Or StringStripWS($line, 8) = "" Then ContinueLoop Local $parts = StringSplit($line, "=", 2) If UBound($parts) = 2 Then Local $key = $parts[0] Local $val = $parts[1] Local $meta[2] $meta[0] = $val $meta[1] = _StringHash($val) $dict.Add($key, $meta) EndIf Next Return $dict EndFunc Func _ReportBaselineDiff($type, $baseline, $current) Local $added = _MapDiff($current, $baseline) Local $removed = _MapDiff($baseline, $current) ; Track processed items to show each only once Local $processed = ObjCreate("Scripting.Dictionary") ; Handle "added" entries (present now but not in baseline) For $key In $added.Keys If $processed.Exists($key) Then ContinueLoop Local $meta = $current.Item($key) Local $target = $meta[0] Local $programName = _ExtractProgramName($target) Local $msg = "*** New " & ($type = "Startup" ? "startup entry" : "scheduled task") & " detected since original baseline! ***" & @CRLF & @CRLF & _ "- Program Name: " & @CRLF & $programName & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Location" : "Task File Path") & ": " & @CRLF & $key & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Target" : "Task Action") & ": " & @CRLF & $target & @CRLF & @CRLF & _ "- Do you wish to allow this change?" Local $response = MsgBox($MB_ICONWARNING + $MB_YESNO + $MB_TOPMOST, "StartupMonitor64: Baseline Difference", $msg) If $response = $IDNO Then _LogWrite("User denied baseline-detected " & ($type = "Startup" ? "entry" : "scheduled task") & ", removing: " & $key & " | Target: " & $target) _AddToBlackList($type, $key, $target) If $type = "Startup" Then _RemoveStartupEntry($key) ElseIf $type = "Task" Then _RemoveScheduledTask($key) EndIf Else _LogWrite("User allowed baseline-detected " & ($type = "Startup" ? "entry" : "scheduled task") & ": " & $key & " | Target: " & $target) _AddToWhiteList($type, $key, $target) EndIf $processed.Add($key, True) Next ; Handle "removed" entries (present in baseline but missing now) ; You may want to prompt or just log. Here we prompt user as well: For $key In $removed.Keys If $processed.Exists($key) Then ContinueLoop Local $meta = $baseline.Item($key) Local $target = $meta[0] Local $programName = _ExtractProgramName($target) Local $msg = "*** " & ($type = "Startup" ? "Startup entry" : "Scheduled task") & " REMOVED since original baseline! ***" & @CRLF & @CRLF & _ "- Program Name: " & @CRLF & $programName & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Location" : "Task File Path") & ": " & @CRLF & $key & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Target" : "Task Action") & ": " & @CRLF & $target & @CRLF & @CRLF & _ "- Do you wish to allow this removal?" & @CRLF & "(No = blacklist it from being deleted in future)" Local $response = MsgBox($MB_ICONWARNING + $MB_YESNO + $MB_TOPMOST, "StartupMonitor64: Baseline Removal", $msg) If $response = $IDNO Then _LogWrite("User denied REMOVAL of baseline " & ($type = "Startup" ? "entry" : "scheduled task") & ": " & $key & " | Target: " & $target) _AddToBlackList($type, $key, $target) Else _LogWrite("User allowed removal of baseline " & ($type = "Startup" ? "entry" : "scheduled task") & ": " & $key & " | Target: " & $target) _AddToWhiteList($type, $key, $target) EndIf $processed.Add($key, True) Next EndFunc ; ------------------------------------------------------------------------------ ; _CheckAndHandleChanges: Now adds to Blacklist/Whitelist automatically and shows msgbox for blacklist if enabled ; ------------------------------------------------------------------------------ Func _CheckAndHandleChanges($type, $oldDict, $newDict) Local $processed = ObjCreate("Scripting.Dictionary") ; Detect additions Local $added = _MapDiff($newDict, $oldDict) For $entry In $added.Keys If $processed.Exists($entry) Then ContinueLoop Local $meta = $newDict.Item($entry) Local $target = $meta[0] Local $programName = _ExtractProgramName($target) ; --- Blacklist check --- If _IsBlackListed($type, $entry, $target) Then If $g_showBlacklistMsgBox = "1" Then Local $alertMsg = "*** BLACKLISTED " & ($type = "Startup" ? "startup entry" : "scheduled task") & " auto-removed! ***" & @CRLF & @CRLF & _ "- Program Name: " & @CRLF & $programName & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Location" : "Task File Path") & ": " & @CRLF & $entry & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Target" : "Task Action") & ": " & @CRLF & $target MsgBox($MB_ICONWARNING + $MB_TOPMOST, "StartupMonitor64: Blacklist", $alertMsg) EndIf _LogWrite("BLACKLISTED " & ($type = "Startup" ? "entry" : "scheduled task") & ", auto-removing: " & $entry & " | Target: " & $target) If $type = "Startup" Then _RemoveStartupEntry($entry) ElseIf $type = "Task" Then _RemoveScheduledTask($entry) EndIf $processed.Add($entry, True) If IsObj($newDict) And $newDict.Exists($entry) Then $newDict.Remove($entry) ContinueLoop EndIf ; --- Whitelist check --- If _IsWhiteListed($type, $entry, $target) Then ContinueLoop ; --- Normal prompt --- Local $msg = "*** A new " & ($type = "Startup" ? "startup entry" : "scheduled task") & " was added: ***" & @CRLF & @CRLF & _ "- Program Name: " & @CRLF & $programName & @CRLF & @CRLF & _ "- New " & ($type = "Startup" ? "Startup Location" : "Task File Path") & ": " & @CRLF & $entry & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Target" : "Task Action") & ": " & @CRLF & $target & @CRLF & @CRLF & _ "- Do you wish to allow this change?" Local $response = MsgBox($MB_ICONWARNING + $MB_YESNO + $MB_TOPMOST, "StartupMonitor64: " & ($type = "Startup" ? "Startup Entry" : "Scheduled Task") & " Detected", $msg) If $response = $IDNO Then _LogWrite("User denied " & ($type = "Startup" ? "entry" : "scheduled task") & ", removing: " & $entry & " | Target: " & $target) _AddToBlackList($type, $entry, $target) If $type = "Startup" Then _RemoveStartupEntry($entry) ElseIf $type = "Task" Then _RemoveScheduledTask($entry) EndIf Else _LogWrite("User allowed " & ($type = "Startup" ? "entry" : "scheduled task") & ": " & $entry & " | Target: " & $target) _AddToWhiteList($type, $entry, $target) EndIf Next ; Detect modifications (silent for whitelisted, prompt for others) Local $modified = _MapModified($newDict, $oldDict) For $entry In $modified.Keys If $processed.Exists($entry) Then ContinueLoop Local $meta = $newDict.Item($entry) Local $target = $meta[0] Local $programName = _ExtractProgramName($target) ; --- Blacklist check --- If _IsBlackListed($type, $entry, $target) Then If $g_showBlacklistMsgBox = "1" Then Local $alertMsg = "*** BLACKLISTED MODIFIED " & ($type = "Startup" ? "startup entry" : "scheduled task") & " auto-removed! ***" & @CRLF & @CRLF & _ "- Program Name: " & @CRLF & $programName & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Location" : "Task File Path") & ": " & @CRLF & $entry & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Target" : "Task Action") & ": " & @CRLF & $target MsgBox($MB_ICONWARNING + $MB_TOPMOST, "StartupMonitor64: Blacklist", $alertMsg) EndIf _LogWrite("BLACKLISTED MODIFIED " & ($type = "Startup" ? "entry" : "scheduled task") & ", auto-removing: " & $entry & " | Target: " & $target) If $type = "Startup" Then _RemoveStartupEntry($entry) ElseIf $type = "Task" Then _RemoveScheduledTask($entry) EndIf $processed.Add($entry, True) If IsObj($newDict) And $newDict.Exists($entry) Then $newDict.Remove($entry) ContinueLoop EndIf ; --- Whitelist check --- If _IsWhiteListed($type, $entry, $target) Then ContinueLoop ; --- Normal prompt --- Local $msg = "*** An existing " & ($type = "Startup" ? "startup entry" : "scheduled task") & " was MODIFIED: ***" & @CRLF & @CRLF & _ "- Program Name: " & @CRLF & $programName & @CRLF & @CRLF & _ "- " & ($type = "Startup" ? "Startup Location" : "Task File Path") & ": " & @CRLF & $entry & @CRLF & @CRLF & _ "- New " & ($type = "Startup" ? "Target" : "Action") & ": " & @CRLF & $target & @CRLF & @CRLF & _ "- Do you wish to allow this change?" & @CRLF & "(Selecting No will attempt to remove this entry!)" Local $response = MsgBox($MB_ICONWARNING + $MB_YESNO + $MB_TOPMOST, "StartupMonitor64: " & ($type = "Startup" ? "Startup Entry" : "Scheduled Task") & " MODIFIED", $msg) If $response = $IDNO Then _LogWrite("User denied MODIFIED " & ($type = "Startup" ? "entry" : "scheduled task") & ", removing: " & $entry & " | Target: " & $target) _AddToBlackList($type, $entry, $target) If $type = "Startup" Then _RemoveStartupEntry($entry) ElseIf $type = "Task" Then _RemoveScheduledTask($entry) EndIf Else _LogWrite("User allowed MODIFIED " & ($type = "Startup" ? "entry" : "scheduled task") & ": " & $entry & " | Target: " & $target) _AddToWhiteList($type, $entry, $target) EndIf Next EndFunc ; ------------------------------------------------------------------------------ ; _GetAllStartupEntries_Map: Collect all current startup entries from registry ; and startup folders, and from optionally enabled extra registry locations ; Returns a dictionary of [entry_path] = [value, hash] ; If $withMeta is true, stores an array: [command/value, timestamp/hash] ; ------------------------------------------------------------------------------ Func _GetAllStartupEntries_Map($withMeta=False) Local $dict = ObjCreate("Scripting.Dictionary") _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", $dict, $withMeta) _GetRegEntries_Map("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", $dict, $withMeta) _GetFolderEntries_Map(@StartupDir, $dict, False, $withMeta) _GetFolderEntries_Map(@StartupCommonDir, $dict, False, $withMeta) ; Extra locations If $g_extraChecks[0] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce", $dict, $withMeta) If $g_extraChecks[1] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce", $dict, $withMeta) If $g_extraChecks[2] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", $dict, $withMeta) If $g_extraChecks[3] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", $dict, $withMeta) If $g_extraChecks[4] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", $dict, $withMeta) If $g_extraChecks[5] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", $dict, $withMeta) If $g_extraChecks[6] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce", $dict, $withMeta) If $g_extraChecks[7] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce", $dict, $withMeta) If $g_extraChecks[8] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices", $dict, $withMeta) If $g_extraChecks[9] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices", $dict, $withMeta) If $g_extraChecks[10] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run", $dict, $withMeta) If $g_extraChecks[11] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run", $dict, $withMeta) If $g_extraChecks[12] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit", $dict, $withMeta) If $g_extraChecks[13] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell", $dict, $withMeta) If $g_extraChecks[14] = "1" Then _GetRegEntries_Map("HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows", $dict, $withMeta) If $g_extraChecks[15] = "1" Then _GetRegEntries_Map("HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager", $dict, $withMeta) Return $dict EndFunc ; ------------------------------------------------------------------------------ ; _GetRegEntries_Map: Adds value names & content from registry key to the map ; ------------------------------------------------------------------------------ Func _GetRegEntries_Map($key, ByRef $dict, $withMeta=False) Local $i = 1, $val, $v While 1 $val = RegEnumVal($key, $i) If @error Then ExitLoop $v = RegRead($key, $val) If $withMeta Then Local $arr[2] $arr[0] = $v $arr[1] = _StringHash($v) $dict.Add($key & "\" & $val, $arr) Else $dict.Add($key & "\" & $val, $v) EndIf $i += 1 WEnd EndFunc ; ------------------------------------------------------------------------------ ; _GetFolderEntries_Map: Adds all file names (and hash/mtime) from folder to map ; ------------------------------------------------------------------------------ Func _GetFolderEntries_Map($folder, ByRef $dict, $recursive=False, $withMeta=False) Local $search = FileFindFirstFile($folder & "\*") If $search = -1 Then Return While 1 Local $file = FileFindNextFile($search) If @error Then ExitLoop If @extended = 1 Then ; Folder If $recursive Then _GetFolderEntries_Map($folder & "\" & $file, $dict, $recursive, $withMeta) Else Local $path = $folder & "\" & $file If $withMeta Then Local $hash = "" If StringRight($file, 4) = ".lnk" Then $hash = _StringHash(_GetShortcutTarget($path)) Else $hash = FileGetTime($path, 0, 1) ; last write time EndIf Local $arr[2] $arr[0] = $path $arr[1] = $hash $dict.Add($path, $arr) Else $dict.Add($path, $path) EndIf EndIf WEnd FileClose($search) EndFunc ; ------------------------------------------------------------------------------ ; _GetAllScheduledTasks_Map: Collects all current scheduled tasks (recursively) ; Returns a dictionary of [taskfile_path] = [command, hash] ; ------------------------------------------------------------------------------ Func _GetAllScheduledTasks_Map($withMeta=False) Local $dict = ObjCreate("Scripting.Dictionary") _GetFolderEntries_Map("C:\Windows\System32\Tasks", $dict, True, $withMeta) If $withMeta Then For $k In $dict.Keys Local $command = _GetTaskAction($k) Local $arr[2] $arr[0] = $command $arr[1] = _StringHash($command) $dict.Item($k) = $arr Next EndIf Return $dict EndFunc ; ------------------------------------------------------------------------------ ; _MapDiff: Returns keys in dictA not present in dictB as a new map (dictionary) ; ------------------------------------------------------------------------------ Func _MapDiff($dictA, $dictB) Local $diff = ObjCreate("Scripting.Dictionary") For $key In $dictA.Keys If Not $dictB.Exists($key) Then $diff.Add($key, $dictA.Item($key)) Next Return $diff EndFunc ; ------------------------------------------------------------------------------ ; _MapModified: Returns keys in dictA and dictB that have different values/hashes ; ------------------------------------------------------------------------------ Func _MapModified($dictA, $dictB) Local $diff = ObjCreate("Scripting.Dictionary") For $key In $dictA.Keys If $dictB.Exists($key) Then If $dictA.Item($key)[1] <> $dictB.Item($key)[1] Then $diff.Add($key, $dictA.Item($key)) EndIf Next Return $diff EndFunc ; ------------------------------------------------------------------------------ ; Blacklist Management: Add and Check ; ------------------------------------------------------------------------------ Func _IsBlackListed($type, $entry, $target) Local $section = $type Local $arr = IniReadSection($g_blacklistFile, $section) If @error Then Return False For $i = 1 To $arr[0][0] Local $black = $arr[$i][0] If $black = "" Or StringLeft($black, 1) = ";" Then ContinueLoop If StringInStr($entry, $black, 0) Or StringInStr($target, $black, 0) Then Return True Next Return False EndFunc Func _AddToBlackList($type, $entry, $target) If Not _IsBlackListed($type, $entry, $target) Then IniWrite($g_blacklistFile, $type, $entry, $target) EndIf EndFunc ; ------------------------------------------------------------------------------ ; Whitelist Management: Add and Check ; ------------------------------------------------------------------------------ Func _AddToWhiteList($type, $entry, $target) If Not _IsWhiteListed($type, $entry, $target) Then IniWrite($g_whitelistFile, $type, $entry, $target) EndIf EndFunc Func _IsWhiteListed($type, $entry, $target) Local $val = IniRead($g_whitelistFile, $type, $entry, "") Return $val = $target EndFunc ; ------------------------------------------------------------------------------ ; _RemoveStartupEntry: Removes the specified startup entry from registry or folder ; ------------------------------------------------------------------------------ Func _RemoveStartupEntry($entry) If StringLeft($entry, 18) = "HKEY_LOCAL_MACHINE" Then Local $remain = StringTrimLeft($entry, 19) Local $lastSep = StringInStr($remain, "\", 0, -1) Local $key = "HKEY_LOCAL_MACHINE\" & StringLeft($remain, $lastSep - 1) Local $val = StringTrimLeft($remain, $lastSep) RegDelete($key, $val) ElseIf StringLeft($entry, 17) = "HKEY_CURRENT_USER" Then Local $remain = StringTrimLeft($entry, 18) Local $lastSep = StringInStr($remain, "\", 0, -1) Local $key = "HKEY_CURRENT_USER\" & StringLeft($remain, $lastSep - 1) Local $val = StringTrimLeft($remain, $lastSep) RegDelete($key, $val) Else FileDelete($entry) EndIf EndFunc ; ------------------------------------------------------------------------------ ; _RemoveScheduledTask: Safely removes scheduled tasks using schtasks.exe ; ------------------------------------------------------------------------------ Func _RemoveScheduledTask($entry) Local $taskRoot = "C:\Windows\System32\Tasks" If StringLeft($entry, StringLen($taskRoot)) = $taskRoot Then Local $rel = StringTrimLeft($entry, StringLen($taskRoot)) If StringLeft($rel, 1) = "\" Then $rel = StringTrimLeft($rel, 1) $rel = StringReplace($rel, "/", "\") Local $cmd = 'schtasks /Delete /TN "' & $rel & '" /F' RunWait(@ComSpec & " /c " & $cmd, "", @SW_HIDE) EndIf EndFunc ; ------------------------------------------------------------------------------ ; _LogWrite: Appends a timestamped line to the log file, with '=' as separator ; ------------------------------------------------------------------------------ Func _LogWrite($sText) Local $hFile = FileOpen($g_logFile, $FO_APPEND) If $hFile = -1 Then Return FileWriteLine($hFile, @YEAR & "-" & @MON & "-" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " = " & $sText) FileClose($hFile) EndFunc ; ------------------------------------------------------------------------------ ; _PruneOldLogEntries_LineByLine: Keeps only log entries from the last 30 days ; ------------------------------------------------------------------------------ Func _PruneOldLogEntries_LineByLine() If Not FileExists($g_logFile) Then Return Local $nowTS = _DateToTS(@YEAR & "-" & @MON & "-" & @MDAY) Local $tempFile = @ScriptDir & "\_tmp_log.txt" Local $rfh = FileOpen($g_logFile, $FO_READ) If $rfh = -1 Then Return Local $wfh = FileOpen($tempFile, $FO_OVERWRITE) If $wfh = -1 Then FileClose($rfh) Return EndIf While 1 Local $line = FileReadLine($rfh) If @error Then ExitLoop Local $dateStr = StringLeft($line, 10) If StringRegExp($dateStr, "^\d{4}-\d{2}-\d{2}$") Then Local $lineTS = _DateToTS($dateStr) If $nowTS - $lineTS <= 2592000 Then FileWriteLine($wfh, $line) EndIf Else FileWriteLine($wfh, $line) EndIf WEnd FileClose($rfh) FileClose($wfh) FileDelete($g_logFile) FileMove($tempFile, $g_logFile, 1) EndFunc ; ------------------------------------------------------------------------------ ; _DateToTS: Convert YYYY-MM-DD to timestamp (seconds since epoch) ; ------------------------------------------------------------------------------ Func _DateToTS($sDate) Local $a = StringSplit($sDate, "-") If $a[0] <> 3 Then Return 0 Return _DateDiff("s", "1970/01/01 00:00:00", $a[1] & "/" & $a[2] & "/" & $a[3] & " 00:00:00") EndFunc ; ------------------------------------------------------------------------------ ; _GetStartupEntryTarget: Gets the command/target for a startup entry (reg/file) ; ------------------------------------------------------------------------------ Func _GetStartupEntryTarget($entry) If StringLeft($entry, 18) = "HKEY_LOCAL_MACHINE" Or StringLeft($entry, 17) = "HKEY_CURRENT_USER" Then Local $remain = StringInStr($entry, "\", 0, -1) If $remain = 0 Then Return "" Local $key = StringLeft($entry, $remain - 1) Local $val = StringTrimLeft($entry, $remain) Return RegRead($key, $val) Else If StringRight($entry, 4) = ".lnk" Then Return _GetShortcutTarget($entry) Else Return $entry EndIf EndIf EndFunc ; ------------------------------------------------------------------------------ ; _GetShortcutTarget: Get target path from a Windows shortcut (.lnk) ; ------------------------------------------------------------------------------ Func _GetShortcutTarget($lnkFile) Local $objShell = ObjCreate("WScript.Shell") If Not IsObj($objShell) Then Return "" Local $objShortcut = $objShell.CreateShortcut($lnkFile) If Not IsObj($objShortcut) Then Return "" Return $objShortcut.TargetPath EndFunc ; ------------------------------------------------------------------------------ ; _ExtractProgramName: Extracts program name from a command/target path ; ------------------------------------------------------------------------------ Func _ExtractProgramName($target) If $target = "" Then Return "" Local $re = StringRegExp($target, '[^\\\/]+\.exe', 1) If IsArray($re) Then Return $re[UBound($re) - 1] EndIf Local $lastSlash = StringInStr($target, "\", 0, -1) If $lastSlash > 0 Then Return StringTrimLeft($target, $lastSlash) EndIf Return $target EndFunc ; ------------------------------------------------------------------------------ ; _GetTaskAction: Extracts the "Action" (target) from a Scheduled Task XML file ; ------------------------------------------------------------------------------ Func _GetTaskAction($taskPath) Local $xml = FileRead($taskPath) If @error Or $xml = "" Then Return "" Local $re = StringRegExp($xml, "<Command>(.*?)</Command>", 1) If IsArray($re) Then Return $re[0] EndIf Return "" EndFunc ; ------------------------------------------------------------------------------ ; _StringHash: Returns a simple hash of a string (for change detection, not crypto) ; ------------------------------------------------------------------------------ Func _StringHash($s) Local $i, $h = 0 For $i = 1 To StringLen($s) $h = BitXOR($h, Asc(StringMid($s, $i, 1))) $h = BitAND($h * 31, 0xFFFFFFFF) Next Return $h EndFunc I will likely do some tailoring to make some light changes to filenames and such, but other than that, for now, I think this is doing a pretty good job? The AI told me about many other locations for malware, but tbh, this was only meant to be a light application for checking basic startup locations, like the original. But as it turned out this well, I am really happy it deletes those pesky DriverBooster tasks! PS: I know I cheated by using AI, I am no programmer. I have been reading the PDF still, and trying to gauge how this script works. I haven't honestly picked up a vast amount of knowledge, but I am starting to get the basics and learnt some simple things like loops, data types, arrays, sorta make sense, but need to reread when not so exhausted. I'm at chapter 9 which explains GUI's. Anyway, hope this is ok, please add some feedback Edited 9 hours ago by sl23 Revised code
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now