Build a Threat Detection System

Monitor Windows 11 for suspicious activity using Sysmon and PowerShell.

Introduction

30 Second Summary

Think about how many programs run on your computer every day without you knowing. Any one of them could be silently downloading files, modifying startup settings, or running hidden scripts in the background.

In this project, you will build a lightweight threat detection system that watches your Windows 11 machine for suspicious activity and alerts you in real-time with desktop notifications and structured logs.

What You'll Build

A silent, always-on security monitor that detects unknown programs creating executables, modifying registry persistence keys, or spawning unexpected processes on your machine.

By the end of this project, you'll have:

  • A custom Sysmon XML configuration tuned for low-noise security monitoring that filters out trusted system processes while capturing suspicious file creation, process execution, and registry changes.
  • A PowerShell monitoring script that reacts to events in real-time using an event-driven architecture with zero polling overhead, firing toast notifications and logging alerts to CSV.
  • An auto-starting background system via Task Scheduler that launches your monitor silently at login with no manual intervention.
  • Secret Mission: Build a daily HTML summary report that auto-generates each morning with stats on suspicious events from the past 24 hours.

Are there any prerequisites?

You need a Windows 11 machine (any edition, including Home) with administrator access. PowerShell is already included with Windows 11. No prior security or scripting experience is required.

Install Tools and Create Project Structure

Before your threat detection system can watch for suspicious activity, you need the tools that make it possible.

In this step, you'll install Sysmon (the kernel-level sensor that records system events), the BurntToast PowerShell module (for desktop toast notifications), and create the folder structure your monitor will use.

In this step, get ready to:
  • Download and verify Sysmon from the official Sysinternals page.
  • Install the BurntToast notification module for PowerShell.
  • Create the project folder structure for logs and configuration.
Open an elevated PowerShell terminal

Sysmon requires administrator privileges to install its kernel driver. You need to run PowerShell as an administrator for the rest of this project.

  • Press the Windows key to open your search bar.
  • Type PowerShell.
  • Right-click Windows PowerShell and select Run as administrator.
  • Click Yes on the User Account Control prompt that appears.

You should see a PowerShell window with the title bar showing Administrator.

Don't see Run as administrator?

If your search results don't show the right-click option, look for a panel on the right side of the Start menu with an option labelled Run as administrator. Alternatively, you can right-click the Start button and select Terminal (Admin).

Help me open an elevated PowerShell terminal on Windows 11.

Download and extract Sysmon

Sysmon is a free Sysinternals tool from Microsoft that installs a kernel driver to record process creations, file changes, and registry modifications. You'll download it and extract it to a dedicated folder.

  • Download and extract Sysmon to C:\Sysmon\ by running these commands in your elevated PowerShell:
# Download the Sysmon ZIP from Sysinternals
Invoke-WebRequest -Uri "https://download.sysinternals.com/files/Sysmon.zip" -OutFile "$env:TEMP\Sysmon.zip"

# Create the destination folder and extract
New-Item -ItemType Directory -Path "C:\Sysmon" -Force | Out-Null
Expand-Archive -Path "$env:TEMP\Sysmon.zip" -DestinationPath "C:\Sysmon" -Force

What do these commands do?

  • Invoke-WebRequest downloads the Sysmon ZIP file from the official Sysinternals download server to your temp folder.
  • New-Item creates the C:\Sysmon\ folder. The -Force flag means it won't error if the folder already exists.
  • Expand-Archive unzips everything into C:\Sysmon\, giving you the Sysmon64.exe binary your project needs.
  • Verify that Sysmon extracted correctly by running:
C:\Sysmon\Sysmon64.exe -?

You should see Sysmon's usage information printed to the terminal, starting with the version number and a list of command-line switches.

Seeing an error about the path not being found?

Double-check that the download completed successfully. Run dir C:\Sysmon\ to see what files are in the folder. You should see Sysmon64.exe listed. If the folder is empty, re-run the download and extract commands.

Help me troubleshoot my Sysmon download and extraction.

Install the BurntToast notification module

Your monitoring script will fire desktop toast notifications when it detects suspicious activity. BurntToast is a PowerShell module that wraps the Windows notification APIs into a single cmdlet call.

  • Install BurntToast by running:
Install-Module -Name BurntToast -Force -Scope CurrentUser

What does this command do?

This installs BurntToast 1.1.0 from the PowerShell Gallery. The -Scope CurrentUser flag installs it for your account only (no system-wide changes). The -Force flag skips confirmation prompts.

  • PowerShell may ask you to trust the PSGallery repository. Type Y and press Enter if prompted.
  • Verify BurntToast works by sending a test notification:
New-BurntToastNotification -Text "Test", "BurntToast is working"

A toast notification should pop up in the bottom-right corner of your screen with the text "BurntToast is working".

No toast notification appeared?

Check that Windows notifications are enabled. Open Settings, go to System, then Notifications. Make sure notifications are turned on. Also check that Focus Assist (Do Not Disturb) is off.

Help me fix BurntToast notifications not appearing.

Create the project folder structure

Your monitoring script, configuration files, and alert logs will all live inside a single C:\SecurityMonitor\ folder. This keeps everything organized and easy to clean up later.

  • Create the project folder and its Logs subfolder by running:
New-Item -ItemType Directory -Path "C:\SecurityMonitor\Logs" -Force

What does this command do?

This creates both C:\SecurityMonitor\ and its Logs subfolder in one command. PowerShell automatically creates parent directories when you specify a nested path.

  • Confirm the folder structure was created by running:
dir C:\SecurityMonitor\

You should see the Logs folder listed in the output.

Seeing an access denied error?

Make sure you're running PowerShell as administrator. Check the title bar of your PowerShell window for the word Administrator. If it's missing, close this window and reopen PowerShell using Run as administrator as described at the start of this step.

Help me fix a permissions error when creating folders on C:\.

✔️ Awesome, I've got everything!

Great. You have Sysmon extracted, BurntToast installed, and your project folder ready. Make sure you keep your elevated PowerShell window open for the next step.

ⓧ I'd like to double check the full setup

Run these three checks in your elevated PowerShell to confirm everything is in place:

# Check 1: Sysmon binary exists
C:\Sysmon\Sysmon64.exe -?

# Check 2: BurntToast module is available
Get-Module -ListAvailable -Name BurntToast

# Check 3: Project folder structure exists
dir C:\SecurityMonitor\

Check 1 should print Sysmon's usage info. Check 2 should show BurntToast version 1.1.0. Check 3 should show a Logs subfolder.

Your tools are installed and your project structure is ready. Next up, you'll write a custom Sysmon configuration that filters out system noise and captures only security-relevant events.

Create and Deploy the Sysmon Configuration

Your environment is ready with Sysmon downloaded and your project folder created. Now it's time to tell Sysmon exactly what to watch for.

Sysmon's behavior is controlled entirely by an XML configuration file. This file defines which system events get recorded and which get ignored. A well-tuned config is the difference between a useful security tool and an overwhelming flood of noise.

In this step, get ready to:
  • Create a custom Sysmon XML configuration tuned for low-noise security monitoring.
  • Install Sysmon with your configuration so it starts capturing events.
  • Confirm security events are flowing into the Windows Event Log.
Create the Sysmon XML configuration file

Your configuration uses a two-layer approach. The first layer filters out the noisiest Windows system processes at the kernel driver level so they never even reach the event log. The second layer tells Sysmon which specific event types to capture.

  • In your elevated PowerShell terminal, open a new file in Notepad by running this command:
notepad C:\SecurityMonitor\sysmonconfig.xml
  • Notepad will ask if you want to create a new file. Click Yes.
  • Paste the following XML configuration into the empty file:
<Sysmon schemaversion="4.90">
    <HashAlgorithms>SHA256</HashAlgorithms>
    <EventFiltering>
        <ProcessCreate onmatch="exclude">
            <Image condition="is">C:\Windows\System32\svchost.exe</Image>
            <Image condition="is">C:\Windows\System32\RuntimeBroker.exe</Image>
            <Image condition="is">C:\Windows\System32\taskhostw.exe</Image>
            <Image condition="is">C:\Windows\System32\backgroundTaskHost.exe</Image>
            <Image condition="is">C:\Windows\System32\sihost.exe</Image>
            <Image condition="is">C:\Windows\System32\SearchIndexer.exe</Image>
            <Image condition="is">C:\Windows\System32\SearchProtocolHost.exe</Image>
            <Image condition="is">C:\Windows\System32\SearchFilterHost.exe</Image>
            <Image condition="is">C:\Windows\System32\WmiPrvSE.exe</Image>
            <Image condition="is">C:\Windows\System32\conhost.exe</Image>
            <Image condition="is">C:\Windows\System32\dllhost.exe</Image>
            <Image condition="is">C:\Windows\System32\audiodg.exe</Image>
            <Image condition="is">C:\Windows\System32\ctfmon.exe</Image>
            <Image condition="is">C:\Windows\System32\fontdrvhost.exe</Image>
            <Image condition="is">C:\Windows\System32\dwm.exe</Image>
            <Image condition="is">C:\Windows\System32\csrss.exe</Image>
            <Image condition="is">C:\Windows\System32\wininit.exe</Image>
            <Image condition="is">C:\Windows\System32\services.exe</Image>
            <Image condition="is">C:\Windows\System32\lsass.exe</Image>
            <Image condition="is">C:\Windows\System32\smss.exe</Image>
            <Image condition="is">C:\Windows\System32\winlogon.exe</Image>
            <Image condition="is">C:\Windows\System32\spoolsv.exe</Image>
            <Image condition="is">C:\Windows\System32\SecurityHealthSystray.exe</Image>
            <Image condition="is">C:\Windows\System32\SecurityHealthService.exe</Image>
            <Image condition="is">C:\Windows\System32\MoUsoCoreWorker.exe</Image>
            <Image condition="is">C:\Windows\System32\musNotificationUx.exe</Image>
            <Image condition="is">C:\Windows\System32\SettingSyncHost.exe</Image>
            <Image condition="is">C:\Program Files\Windows Defender\MsMpEng.exe</Image>
            <Image condition="is">C:\Program Files\Windows Defender\NisSrv.exe</Image>
            <Image condition="is">C:\Program Files\Windows Defender\MpCmdRun.exe</Image>
        </ProcessCreate>
        <FileCreate onmatch="include">
            <TargetFilename condition="end with">.exe</TargetFilename>
            <TargetFilename condition="end with">.dll</TargetFilename>
            <TargetFilename condition="end with">.bat</TargetFilename>
            <TargetFilename condition="end with">.cmd</TargetFilename>
            <TargetFilename condition="end with">.ps1</TargetFilename>
            <TargetFilename condition="end with">.vbs</TargetFilename>
            <TargetFilename condition="end with">.js</TargetFilename>
            <TargetFilename condition="end with">.scr</TargetFilename>
            <TargetFilename condition="end with">.hta</TargetFilename>
            <TargetFilename condition="end with">.lnk</TargetFilename>
            <TargetFilename condition="contains">\Startup\</TargetFilename>
            <TargetFilename condition="contains">\Start Menu\</TargetFilename>
            <TargetFilename condition="contains">\drivers\</TargetFilename>
        </FileCreate>
        <RegistryEvent onmatch="include">
            <TargetObject condition="contains">\CurrentVersion\Run</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\RunOnce</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\Winlogon</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\Policies\Explorer\Run</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\Explorer\StartupApproved</TargetObject>
            <TargetObject condition="contains">\Services\</TargetObject>
        </RegistryEvent>
        <ProcessTerminate onmatch="include" />
        <FileCreateTime onmatch="include" />
        <NetworkConnect onmatch="include" />
        <DriverLoad onmatch="include" />
        <ImageLoad onmatch="include" />
        <CreateRemoteThread onmatch="include" />
        <RawAccessRead onmatch="include" />
        <ProcessAccess onmatch="include" />
        <FileCreateStreamHash onmatch="include" />
        <PipeEvent onmatch="include" />
        <WmiEvent onmatch="include" />
        <DnsQuery onmatch="include" />
        <FileDelete onmatch="include" />
        <ClipboardChange onmatch="include" />
        <ProcessTampering onmatch="include" />
        <FileDeleteDetected onmatch="include" />
    </EventFiltering>
</Sysmon>

What does this configuration do?

This config has three active monitoring sections and a block of disabled event types:

  • ProcessCreate with onmatch="exclude" logs every new process EXCEPT the 30 noisy system executables listed. This means any unknown program that launches will be recorded.
  • FileCreate with onmatch="include" only logs file creations matching dangerous extensions (.exe, .dll, .bat, etc.) or sensitive locations like Startup folders.
  • RegistryEvent with onmatch="include" only captures changes to persistence-related registry keys (Run, RunOnce, Services). Malware commonly uses these keys to survive reboots.
  • All other event types (NetworkConnect, ImageLoad, DnsQuery, etc.) use empty onmatch="include" rules with no child filters. Since nothing can match an empty include list, these events are effectively disabled.
  • Save the file by pressing Ctrl+S in Notepad, then close Notepad.
  • Confirm the file was saved by running:
Test-Path C:\SecurityMonitor\sysmonconfig.xml
  • You should see True in the output.

Seeing False?

Make sure you saved the file to C:\SecurityMonitor\sysmonconfig.xml and not to a different location. When Notepad's Save dialog opens, navigate to C:\SecurityMonitor\ and name the file sysmonconfig.xml.

Also check that the file isn't accidentally saved as sysmonconfig.xml.txt. In the Save dialog, change Save as type to All Files (*.*) before saving.

help me verify my sysmonconfig.xml file was saved correctly

✔️ Awesome, I've got everything!

Great. Make sure you've saved and closed Notepad before moving on.

ⓧ I'd like to double check the full code

Here is the complete sysmonconfig.xml file for reference:

<Sysmon schemaversion="4.90">
    <HashAlgorithms>SHA256</HashAlgorithms>
    <EventFiltering>
        <ProcessCreate onmatch="exclude">
            <Image condition="is">C:\Windows\System32\svchost.exe</Image>
            <Image condition="is">C:\Windows\System32\RuntimeBroker.exe</Image>
            <Image condition="is">C:\Windows\System32\taskhostw.exe</Image>
            <Image condition="is">C:\Windows\System32\backgroundTaskHost.exe</Image>
            <Image condition="is">C:\Windows\System32\sihost.exe</Image>
            <Image condition="is">C:\Windows\System32\SearchIndexer.exe</Image>
            <Image condition="is">C:\Windows\System32\SearchProtocolHost.exe</Image>
            <Image condition="is">C:\Windows\System32\SearchFilterHost.exe</Image>
            <Image condition="is">C:\Windows\System32\WmiPrvSE.exe</Image>
            <Image condition="is">C:\Windows\System32\conhost.exe</Image>
            <Image condition="is">C:\Windows\System32\dllhost.exe</Image>
            <Image condition="is">C:\Windows\System32\audiodg.exe</Image>
            <Image condition="is">C:\Windows\System32\ctfmon.exe</Image>
            <Image condition="is">C:\Windows\System32\fontdrvhost.exe</Image>
            <Image condition="is">C:\Windows\System32\dwm.exe</Image>
            <Image condition="is">C:\Windows\System32\csrss.exe</Image>
            <Image condition="is">C:\Windows\System32\wininit.exe</Image>
            <Image condition="is">C:\Windows\System32\services.exe</Image>
            <Image condition="is">C:\Windows\System32\lsass.exe</Image>
            <Image condition="is">C:\Windows\System32\smss.exe</Image>
            <Image condition="is">C:\Windows\System32\winlogon.exe</Image>
            <Image condition="is">C:\Windows\System32\spoolsv.exe</Image>
            <Image condition="is">C:\Windows\System32\SecurityHealthSystray.exe</Image>
            <Image condition="is">C:\Windows\System32\SecurityHealthService.exe</Image>
            <Image condition="is">C:\Windows\System32\MoUsoCoreWorker.exe</Image>
            <Image condition="is">C:\Windows\System32\musNotificationUx.exe</Image>
            <Image condition="is">C:\Windows\System32\SettingSyncHost.exe</Image>
            <Image condition="is">C:\Program Files\Windows Defender\MsMpEng.exe</Image>
            <Image condition="is">C:\Program Files\Windows Defender\NisSrv.exe</Image>
            <Image condition="is">C:\Program Files\Windows Defender\MpCmdRun.exe</Image>
        </ProcessCreate>
        <FileCreate onmatch="include">
            <TargetFilename condition="end with">.exe</TargetFilename>
            <TargetFilename condition="end with">.dll</TargetFilename>
            <TargetFilename condition="end with">.bat</TargetFilename>
            <TargetFilename condition="end with">.cmd</TargetFilename>
            <TargetFilename condition="end with">.ps1</TargetFilename>
            <TargetFilename condition="end with">.vbs</TargetFilename>
            <TargetFilename condition="end with">.js</TargetFilename>
            <TargetFilename condition="end with">.scr</TargetFilename>
            <TargetFilename condition="end with">.hta</TargetFilename>
            <TargetFilename condition="end with">.lnk</TargetFilename>
            <TargetFilename condition="contains">\Startup\</TargetFilename>
            <TargetFilename condition="contains">\Start Menu\</TargetFilename>
            <TargetFilename condition="contains">\drivers\</TargetFilename>
        </FileCreate>
        <RegistryEvent onmatch="include">
            <TargetObject condition="contains">\CurrentVersion\Run</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\RunOnce</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\Winlogon</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\Policies\Explorer\Run</TargetObject>
            <TargetObject condition="contains">\CurrentVersion\Explorer\StartupApproved</TargetObject>
            <TargetObject condition="contains">\Services\</TargetObject>
        </RegistryEvent>
        <ProcessTerminate onmatch="include" />
        <FileCreateTime onmatch="include" />
        <NetworkConnect onmatch="include" />
        <DriverLoad onmatch="include" />
        <ImageLoad onmatch="include" />
        <CreateRemoteThread onmatch="include" />
        <RawAccessRead onmatch="include" />
        <ProcessAccess onmatch="include" />
        <FileCreateStreamHash onmatch="include" />
        <PipeEvent onmatch="include" />
        <WmiEvent onmatch="include" />
        <DnsQuery onmatch="include" />
        <FileDelete onmatch="include" />
        <ClipboardChange onmatch="include" />
        <ProcessTampering onmatch="include" />
        <FileDeleteDetected onmatch="include" />
    </EventFiltering>
</Sysmon>
Install Sysmon with your configuration

With the configuration file saved, you can now install the Sysmon kernel driver and point it at your config. The driver starts immediately after installation with no reboot required.

  • Install Sysmon using your config file by running this command in your elevated PowerShell:
C:\Sysmon\Sysmon64.exe -accepteula -i C:\SecurityMonitor\sysmonconfig.xml

What does this command do?

  • -accepteula accepts the Sysinternals license agreement so the installer doesn't pause for confirmation.
  • -i tells Sysmon to install as a service and load the specified configuration file.
  • Once installed, Sysmon runs as a Windows service called Sysmon64 that persists across reboots.
  • Verify Sysmon is running by checking the service status:
Get-Service Sysmon64
  • You should see a table with Status showing Running and Name showing Sysmon64.

Service not showing as Running?

Make sure you are running PowerShell as Administrator. Sysmon requires elevated permissions to install its kernel driver.

If you see an error about the configuration file, double-check that sysmonconfig.xml is valid XML with no extra characters or encoding issues. Try opening it in Notepad again and verifying the first line reads <Sysmon schemaversion="4.90">.

help me troubleshoot my Sysmon installation

Confirm events are flowing

Sysmon is installed, but you need to confirm it's actually writing events to the log. A quick query against the Sysmon event log proves the whole pipeline is working.

  • Query the Sysmon operational log for recent events by running:
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 5
  • You should see a table of recent Sysmon events with timestamps, IDs, and messages.

No events yet?

If the log is empty, wait a moment and open any program (like Notepad) to generate a ProcessCreate event. Then run the Get-WinEvent command again.

Seeing an error about the log not being found?

The log name must be exactly Microsoft-Windows-Sysmon/Operational (case-sensitive, with the forward slash). Copy-paste it directly from the code block above.

If the log truly doesn't exist, Sysmon may not have installed correctly. Go back and re-run the install command, checking for any error messages in the output.

help me figure out why the Sysmon event log isn't available

Your Sysmon sensor is live and capturing security-relevant events across your entire system. Next up, you'll build the PowerShell script that reacts to these events in real-time and fires desktop notifications.

Build the Real-Time Monitoring Script

Your Sysmon sensor is now live, silently writing security events to the Windows Event Log. The problem is that nobody is watching those events.

In this step, you will build a PowerShell script that reacts to Sysmon events the moment they appear. Instead of polling the log on a timer, the script uses the System.Diagnostics.Eventing.Reader.EventLogWatcher .NET class to subscribe to new entries with zero CPU overhead between events.

In this step, get ready to:
  • Create a trusted applications whitelist that tells the script which programs to ignore.
  • Build an event-driven monitoring script that fires toast notifications and logs alerts to CSV.
  • Test the full pipeline end-to-end by triggering a real alert.
Create the trusted applications whitelist

Your Sysmon config already excludes the noisiest system processes at the kernel level. This second whitelist layer lives in a plain text file that you can edit any time without reinstalling Sysmon.

Each line is a wildcard pattern. Any process whose path matches a pattern gets silently ignored by the monitoring script.

  • Create the whitelist file by running this command in your elevated PowerShell:
@"
C:\Windows\System32\*
C:\Windows\SysWOW64\*
C:\Windows\explorer.exe
C:\Windows\ImmersiveControlPanel\*
C:\Windows\SystemApps\*
C:\Program Files\Google\Chrome\*
C:\Program Files\Mozilla Firefox\*
C:\Program Files (x86)\Microsoft\Edge\*
C:\Program Files\Microsoft Office\*
C:\Program Files (x86)\Microsoft Office\*
C:\Program Files\Windows Defender\*
C:\Program Files\WindowsApps\*
C:\Program Files\Microsoft VS Code\*
C:\Program Files\Notepad++\*
C:\Program Files\7-Zip\*
C:\Program Files (x86)\Steam\*
C:\Program Files\PowerShell\*
C:\Windows\Microsoft.NET\*
"@ | Set-Content -Path "C:\SecurityMonitor\trusted-apps.txt" -Encoding UTF8

What does this whitelist cover?

The wildcard * at the end of a path matches any executable inside that folder and its subfolders.

  • Windows system directories (System32, SysWOW64) cover OS services and utilities.
  • Program Files entries cover browsers, Office, Defender, and common dev tools.
  • Anything NOT on this list triggers an alert when it runs, creates executables, or modifies persistence registry keys.
  • Verify the file was created by running:
Get-Content C:\SecurityMonitor\trusted-apps.txt

You should see all 18 wildcard patterns printed to the console.

File is empty or missing?

Make sure you copied the entire here-string block including the opening @" and closing "@ markers. The "@ must appear at the very start of a new line with no leading spaces.

help me fix my trusted-apps.txt here-string in PowerShell

Build the monitoring script

This is the core of your threat detection system. The script subscribes to the Sysmon event log and waits for new entries without consuming CPU. When an event arrives, it checks the process against your whitelist, applies a 30-second cooldown to prevent notification spam, then fires a BurntToast desktop notification and appends a row to a CSV log.

Why EventLogWatcher instead of polling?

A common approach is to loop Get-WinEvent every few seconds to check for new entries. This wastes CPU cycles even when nothing is happening.

The EventLogWatcher .NET class maintains an active subscription to the log and fires a callback only when a new record is written. Between events, your script sleeps at zero CPU cost. For an always-on background monitor, this is the right pattern.

You will create this file in three chunks: configuration and initialization, helper functions, then the main event loop.

  • Open Notepad (or your preferred text editor) and create a new file.
  • Save it immediately as C:\SecurityMonitor\Start-ThreatMonitor.ps1.

Start by pasting the script header, configuration variables, and initialization logic:

#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Lightweight real-time threat monitor using Sysmon event log.
.DESCRIPTION
    Subscribes to the Sysmon operational log using EventLogWatcher (event-driven, no polling).
    Filters events against a trusted applications whitelist.
    Fires toast notifications and logs alerts to CSV for untrusted activity.
#>

$ErrorActionPreference = "Stop"

# --- Configuration ---
$scriptRoot = "C:\SecurityMonitor"
$trustedAppsFile = Join-Path $scriptRoot "trusted-apps.txt"
$logDir = Join-Path $scriptRoot "Logs"
$logFile = Join-Path $logDir "alerts.csv"
$sysmonLogName = "Microsoft-Windows-Sysmon/Operational"
$cooldownSeconds = 30

# --- Initialize ---
if (-not (Test-Path $logDir)) {
    New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}

Import-Module BurntToast -ErrorAction Stop

# Load trusted apps whitelist
if (-not (Test-Path $trustedAppsFile)) {
    Write-Error "Trusted apps file not found: $trustedAppsFile"
    exit 1
}
$trustedPatterns = Get-Content $trustedAppsFile | Where-Object { $_ -ne "" -and $_ -notmatch "^\s*#" }

# Cooldown tracker: stores last alert time per process path
$cooldownTracker = @{}

What does this section do?

  • #Requires -RunAsAdministrator ensures the script only runs in an elevated session (Sysmon logs require admin access).
  • The configuration block sets paths for the whitelist, log directory, CSV output, and the Sysmon log name.
  • Initialization loads the BurntToast module, reads the whitelist patterns, and creates an empty hashtable to track cooldowns per process.

Next, paste the three helper functions directly below the initialization section:

# --- Helper Functions ---
function Test-TrustedApp {
    param([string]$ImagePath)
    if ([string]::IsNullOrWhiteSpace($ImagePath)) { return $false }
    foreach ($pattern in $trustedPatterns) {
        if ($ImagePath -like $pattern) {
            return $true
        }
    }
    return $false
}

function Test-Cooldown {
    param([string]$Key)
    $now = Get-Date
    if ($cooldownTracker.ContainsKey($Key)) {
        $lastAlert = $cooldownTracker[$Key]
        if (($now - $lastAlert).TotalSeconds -lt $cooldownSeconds) {
            return $true
        }
    }
    $cooldownTracker[$Key] = $now
    return $false
}

function Get-EventTypeName {
    param([int]$EventId)
    switch ($EventId) {
        1  { "ProcessCreate" }
        11 { "FileCreate" }
        12 { "RegistryAddOrDelete" }
        13 { "RegistryValueSet" }
        14 { "RegistryRename" }
        default { "EventId-$EventId" }
    }
}

What do these functions do?

  • Test-TrustedApp checks a process path against every wildcard pattern in your whitelist using the -like operator.
  • Test-Cooldown prevents notification spam by returning $true if the same process already triggered an alert within the last 30 seconds.
  • Get-EventTypeName translates Sysmon event IDs (1, 11, 12, 13, 14) into readable names for the notification and log.

Finally, paste the main monitoring loop that ties everything together:

# --- Main Monitoring Loop ---
Write-Host "Security Monitor starting..." -ForegroundColor Green
Write-Host "Monitoring: $sysmonLogName" -ForegroundColor Cyan
Write-Host "Trusted apps loaded: $($trustedPatterns.Count) patterns" -ForegroundColor Cyan
Write-Host "Log file: $logFile" -ForegroundColor Cyan
Write-Host "Press Ctrl+C to stop." -ForegroundColor Yellow
Write-Host ""

$sourceId = "SysmonThreatMonitor"

try {
    $query = [System.Diagnostics.Eventing.Reader.EventLogQuery]::new(
        $sysmonLogName,
        [System.Diagnostics.Eventing.Reader.PathType]::LogName
    )
    $watcher = [System.Diagnostics.Eventing.Reader.EventLogWatcher]::new($query)
    $watcher.Enabled = $true

    Register-ObjectEvent -InputObject $watcher -SourceIdentifier $sourceId -EventName EventRecordWritten | Out-Null

    while ($true) {
        $evt = Wait-Event -SourceIdentifier $sourceId
        $evt | Remove-Event

        $record = $evt.SourceEventArgs.EventRecord
        if ($null -eq $record) { continue }

        # Only process event types we care about
        $eventId = $record.Id
        if ($eventId -notin @(1, 11, 12, 13, 14)) { continue }

        # Parse event XML
        [xml]$eventXml = $record.ToXml()
        $dataNodes = $eventXml.Event.EventData.Data

        # Extract Image (process path)
        $imagePath = ($dataNodes | Where-Object { $_.Name -eq "Image" }).'#text'

        # Check trusted whitelist
        if (Test-TrustedApp -ImagePath $imagePath) { continue }

        # Check cooldown
        $cooldownKey = "$eventId-$imagePath"
        if (Test-Cooldown -Key $cooldownKey) { continue }

        # Extract additional details based on event type
        $details = switch ($eventId) {
            1  { ($dataNodes | Where-Object { $_.Name -eq "CommandLine" }).'#text' }
            11 { ($dataNodes | Where-Object { $_.Name -eq "TargetFilename" }).'#text' }
            12 { ($dataNodes | Where-Object { $_.Name -eq "TargetObject" }).'#text' }
            13 { ($dataNodes | Where-Object { $_.Name -eq "TargetObject" }).'#text' }
            14 { ($dataNodes | Where-Object { $_.Name -eq "TargetObject" }).'#text' }
            default { "" }
        }

        $user = ($dataNodes | Where-Object { $_.Name -eq "User" }).'#text'
        $eventTypeName = Get-EventTypeName -EventId $eventId
        $processName = if ($imagePath) { Split-Path $imagePath -Leaf } else { "Unknown" }

        # Log to CSV
        $alertEntry = [PSCustomObject]@{
            Timestamp   = $record.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss")
            EventId     = $eventId
            EventType   = $eventTypeName
            ProcessName = $processName
            ProcessPath = $imagePath
            Details     = $details
            User        = $user
        }
        $alertEntry | Export-Csv -Path $logFile -Append -NoTypeInformation

        # Toast notification
        $toastTitle = "Security Alert: $eventTypeName"
        $toastBody = "Process: $processName`nPath: $imagePath"
        New-BurntToastNotification -Text $toastTitle, $toastBody

        # Console output
        Write-Host "[$($record.TimeCreated)] ALERT - $eventTypeName : $processName" -ForegroundColor Red
        Write-Host "  Path: $imagePath" -ForegroundColor DarkYellow
        Write-Host "  Details: $details" -ForegroundColor DarkYellow
        Write-Host ""
    }
}
finally {
    Write-Host "`nShutting down monitor..." -ForegroundColor Yellow
    Unregister-Event -SourceIdentifier $sourceId -ErrorAction SilentlyContinue
    if ($null -ne $watcher) {
        $watcher.Enabled = $false
        $watcher.Dispose()
    }
    Write-Host "Monitor stopped." -ForegroundColor Green
}

How does the main loop work?

  • An EventLogQuery targets the Microsoft-Windows-Sysmon/Operational log. The EventLogWatcher subscribes to that query and fires EventRecordWritten each time a new event lands.
  • Wait-Event blocks until a new record arrives. Between events, the script is asleep and uses no CPU.
  • For each event, the script parses the XML, extracts the process path, checks the whitelist, checks the cooldown, then either skips silently or fires an alert.
  • The finally block ensures clean shutdown when you press Ctrl+C. It unregisters the event subscription and disposes the watcher.
  • Save Start-ThreatMonitor.ps1.

✔️ Awesome, I've got everything!

Great. Make sure you have saved the file to C:\SecurityMonitor\Start-ThreatMonitor.ps1 before moving on.

ⓧ I'd like to double check the full code

Here is the complete Start-ThreatMonitor.ps1 file for comparison:

#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Lightweight real-time threat monitor using Sysmon event log.
.DESCRIPTION
    Subscribes to the Sysmon operational log using EventLogWatcher (event-driven, no polling).
    Filters events against a trusted applications whitelist.
    Fires toast notifications and logs alerts to CSV for untrusted activity.
#>

$ErrorActionPreference = "Stop"

# --- Configuration ---
$scriptRoot = "C:\SecurityMonitor"
$trustedAppsFile = Join-Path $scriptRoot "trusted-apps.txt"
$logDir = Join-Path $scriptRoot "Logs"
$logFile = Join-Path $logDir "alerts.csv"
$sysmonLogName = "Microsoft-Windows-Sysmon/Operational"
$cooldownSeconds = 30

# --- Initialize ---
if (-not (Test-Path $logDir)) {
    New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}

Import-Module BurntToast -ErrorAction Stop

# Load trusted apps whitelist
if (-not (Test-Path $trustedAppsFile)) {
    Write-Error "Trusted apps file not found: $trustedAppsFile"
    exit 1
}
$trustedPatterns = Get-Content $trustedAppsFile | Where-Object { $_ -ne "" -and $_ -notmatch "^\s*#" }

# Cooldown tracker: stores last alert time per process path
$cooldownTracker = @{}

# --- Helper Functions ---
function Test-TrustedApp {
    param([string]$ImagePath)
    if ([string]::IsNullOrWhiteSpace($ImagePath)) { return $false }
    foreach ($pattern in $trustedPatterns) {
        if ($ImagePath -like $pattern) {
            return $true
        }
    }
    return $false
}

function Test-Cooldown {
    param([string]$Key)
    $now = Get-Date
    if ($cooldownTracker.ContainsKey($Key)) {
        $lastAlert = $cooldownTracker[$Key]
        if (($now - $lastAlert).TotalSeconds -lt $cooldownSeconds) {
            return $true
        }
    }
    $cooldownTracker[$Key] = $now
    return $false
}

function Get-EventTypeName {
    param([int]$EventId)
    switch ($EventId) {
        1  { "ProcessCreate" }
        11 { "FileCreate" }
        12 { "RegistryAddOrDelete" }
        13 { "RegistryValueSet" }
        14 { "RegistryRename" }
        default { "EventId-$EventId" }
    }
}

# --- Main Monitoring Loop ---
Write-Host "Security Monitor starting..." -ForegroundColor Green
Write-Host "Monitoring: $sysmonLogName" -ForegroundColor Cyan
Write-Host "Trusted apps loaded: $($trustedPatterns.Count) patterns" -ForegroundColor Cyan
Write-Host "Log file: $logFile" -ForegroundColor Cyan
Write-Host "Press Ctrl+C to stop." -ForegroundColor Yellow
Write-Host ""

$sourceId = "SysmonThreatMonitor"

try {
    $query = [System.Diagnostics.Eventing.Reader.EventLogQuery]::new(
        $sysmonLogName,
        [System.Diagnostics.Eventing.Reader.PathType]::LogName
    )
    $watcher = [System.Diagnostics.Eventing.Reader.EventLogWatcher]::new($query)
    $watcher.Enabled = $true

    Register-ObjectEvent -InputObject $watcher -SourceIdentifier $sourceId -EventName EventRecordWritten | Out-Null

    while ($true) {
        $evt = Wait-Event -SourceIdentifier $sourceId
        $evt | Remove-Event

        $record = $evt.SourceEventArgs.EventRecord
        if ($null -eq $record) { continue }

        # Only process event types we care about
        $eventId = $record.Id
        if ($eventId -notin @(1, 11, 12, 13, 14)) { continue }

        # Parse event XML
        [xml]$eventXml = $record.ToXml()
        $dataNodes = $eventXml.Event.EventData.Data

        # Extract Image (process path)
        $imagePath = ($dataNodes | Where-Object { $_.Name -eq "Image" }).'#text'

        # Check trusted whitelist
        if (Test-TrustedApp -ImagePath $imagePath) { continue }

        # Check cooldown
        $cooldownKey = "$eventId-$imagePath"
        if (Test-Cooldown -Key $cooldownKey) { continue }

        # Extract additional details based on event type
        $details = switch ($eventId) {
            1  { ($dataNodes | Where-Object { $_.Name -eq "CommandLine" }).'#text' }
            11 { ($dataNodes | Where-Object { $_.Name -eq "TargetFilename" }).'#text' }
            12 { ($dataNodes | Where-Object { $_.Name -eq "TargetObject" }).'#text' }
            13 { ($dataNodes | Where-Object { $_.Name -eq "TargetObject" }).'#text' }
            14 { ($dataNodes | Where-Object { $_.Name -eq "TargetObject" }).'#text' }
            default { "" }
        }

        $user = ($dataNodes | Where-Object { $_.Name -eq "User" }).'#text'
        $eventTypeName = Get-EventTypeName -EventId $eventId
        $processName = if ($imagePath) { Split-Path $imagePath -Leaf } else { "Unknown" }

        # Log to CSV
        $alertEntry = [PSCustomObject]@{
            Timestamp   = $record.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss")
            EventId     = $eventId
            EventType   = $eventTypeName
            ProcessName = $processName
            ProcessPath = $imagePath
            Details     = $details
            User        = $user
        }
        $alertEntry | Export-Csv -Path $logFile -Append -NoTypeInformation

        # Toast notification
        $toastTitle = "Security Alert: $eventTypeName"
        $toastBody = "Process: $processName`nPath: $imagePath"
        New-BurntToastNotification -Text $toastTitle, $toastBody

        # Console output
        Write-Host "[$($record.TimeCreated)] ALERT - $eventTypeName : $processName" -ForegroundColor Red
        Write-Host "  Path: $imagePath" -ForegroundColor DarkYellow
        Write-Host "  Details: $details" -ForegroundColor DarkYellow
        Write-Host ""
    }
}
finally {
    Write-Host "`nShutting down monitor..." -ForegroundColor Yellow
    Unregister-Event -SourceIdentifier $sourceId -ErrorAction SilentlyContinue
    if ($null -ne $watcher) {
        $watcher.Enabled = $false
        $watcher.Dispose()
    }
    Write-Host "Monitor stopped." -ForegroundColor Green
}
Test the monitoring script end-to-end

Time to see your threat detector in action. You will start the script, trigger a suspicious event on purpose, and confirm that both the toast notification and CSV log fire correctly.

  • Launch the monitor by running this command in your elevated PowerShell:
powershell -ExecutionPolicy Bypass -File C:\SecurityMonitor\Start-ThreatMonitor.ps1

You should see green and cyan startup text confirming the monitor is active and the number of trusted patterns loaded.

What should I see?

The console prints the Sysmon log name, the number of loaded whitelist patterns (18), and the path to the CSV log file. It then says "Press Ctrl+C to stop." and waits silently for events.

Now trigger a test alert. Open a second PowerShell window (does not need to be elevated) and create a batch file on your Desktop:

  • Create and run a test batch file by running these commands in the second PowerShell window:
Set-Content -Path "$env:USERPROFILE\Desktop\test-alert.bat" -Value 'echo test > C:\Users\%USERNAME%\test.txt'
& "$env:USERPROFILE\Desktop\test-alert.bat"

Why does this trigger an alert?

Creating a .bat file fires a Sysmon FileCreate event (Event ID 11) because your Sysmon config includes .bat in the FileCreate rules. Running it also fires a ProcessCreate event (Event ID 1). Neither cmd.exe from your Desktop path nor the batch file itself is on the trusted whitelist, so the monitor picks them up.

  • Switch back to your first PowerShell window (the one running the monitor).
  • Confirm you see red ALERT lines in the console output showing the process name and path of the batch file.
  • Confirm a Windows toast notification appeared in the bottom-right corner of your screen with the title "Security Alert" and the process details.
  • Stop the monitor by pressing Ctrl+C in the first PowerShell window.
  • Verify the CSV log was created by running:
Get-Content C:\SecurityMonitor\Logs\alerts.csv

You should see a header row followed by at least one alert entry with columns for Timestamp, EventId, EventType, ProcessName, ProcessPath, Details, and User.

No toast notification or empty CSV?

  • Make sure the first PowerShell window is elevated (Run as Administrator). The #Requires -RunAsAdministrator directive prevents the script from running without admin rights.
  • If you see an error about BurntToast, confirm the module installed correctly by running Get-Module -ListAvailable BurntToast.
  • If the CSV exists but is empty, your test program may have matched a whitelist pattern. Try running a program from a non-whitelisted path like your Downloads folder.

help me debug why my threat monitor script isn't generating alerts

  • Clean up the test file by running:
Remove-Item "$env:USERPROFILE\Desktop\test-alert.bat" -ErrorAction SilentlyContinue
Remove-Item "$env:USERPROFILE\test.txt" -ErrorAction SilentlyContinue

Your threat detection engine is fully operational. Next up, you will wire it into Task Scheduler so it starts automatically every time you log in.

Auto-Start and End-to-End Verification

Your monitoring script works when you run it manually. But a security tool is only useful if it runs automatically every time you log in, without you needing to remember to start it.

In this step, you will register a Task Scheduler entry that launches your monitor silently at logon. Then you will reboot and run a full end-to-end test to prove the entire system works without any manual intervention.

In this step, get ready to:
  • Register a scheduled task that auto-starts the monitor at logon.
  • Reboot and confirm the task is running in the background.
  • Perform a full end-to-end test with a suspicious file to verify toast notifications and CSV logging.
Register a scheduled task for auto-start at logon

Windows Task Scheduler can launch programs automatically based on triggers like logon, time of day, or system events. You will create a task that starts your monitoring script every time you sign in, running in a hidden window so it stays out of your way.

  • Register the scheduled task by running this command in your elevated PowerShell terminal:
# Define the action: launch PowerShell hidden, running the monitor script
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -File C:\SecurityMonitor\Start-ThreatMonitor.ps1"

# Define the trigger: run at user logon
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME

# Define settings: keep running on battery, no time limit
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit ([TimeSpan]::Zero)

# Register the task with highest privileges
Register-ScheduledTask -TaskName "SecurityMonitor" -Action $action -Trigger $trigger -Settings $settings -RunLevel Highest -Description "Lightweight threat detection monitor"

What does this command do?

  • `New-ScheduledTaskAction` defines what runs. The `-WindowStyle Hidden` flag keeps the PowerShell window invisible so the monitor runs silently in the background.
  • `New-ScheduledTaskTrigger -AtLogOn` fires the task every time your user account signs in.
  • `-ExecutionTimeLimit ([TimeSpan]::Zero)` means the task runs indefinitely without being killed by the scheduler after a timeout.
  • `-RunLevel Highest` ensures the script runs with administrator privileges, which it needs to read the Sysmon event log.
  • You should see a table confirming the task was registered, showing the task name SecurityMonitor and its Ready state.

Seeing an access denied error?

  • Make sure you are running PowerShell as Administrator. Right-click the PowerShell icon and select **Run as administrator**.
  • If a task named `SecurityMonitor` already exists from a previous attempt, remove it first by running `Unregister-ScheduledTask -TaskName "SecurityMonitor" -Confirm:$false` and then re-run the registration command.

Help me troubleshoot registering a scheduled task in PowerShell

Reboot and verify the task is running

The scheduled task is set to trigger at logon. To prove it works, you need to sign out and back in (or reboot).

  • Reboot your machine by running:
Restart-Computer
  • After your machine restarts and you sign back in, open an elevated PowerShell terminal.
  • Press the **Windows** key to open your search bar.
  • Type PowerShell, right-click **Windows PowerShell**, and select **Run as administrator**.
  • Verify the task is running by executing:
Get-ScheduledTask -TaskName "SecurityMonitor" | Get-ScheduledTaskInfo
  • You should see the task info with a LastRunTime showing the current logon time.
  • Also confirm a hidden `powershell.exe` process is running your script by running:
Get-Process powershell | Select-Object Id, ProcessName, StartTime
  • You should see at least one powershell process with a start time matching your login.

Task not running after reboot?

  • Check the task state by running `Get-ScheduledTask -TaskName "SecurityMonitor"`. If the state shows `Disabled`, re-enable it with `Enable-ScheduledTask -TaskName "SecurityMonitor"`.
  • Make sure the `-User` value in the trigger matches your current username. Run `$env:USERNAME` to check your account name.
  • If the task ran but stopped immediately, the script may have hit an error. Run `Start-ScheduledTask -TaskName "SecurityMonitor"` manually and check for errors in the console.

Help me debug why my scheduled task isn't starting at logon

Run a full end-to-end test

The monitor is now running silently in the background. Time to prove the full detection pipeline works: Sysmon captures an event, the script picks it up, a toast notification fires, and the alert is logged to CSV.

  • Create a test `.bat` file on your Desktop by running:
Set-Content -Path "$env:USERPROFILE\Desktop\test-alert.bat" -Value 'echo test > C:\Users\%USERNAME%\test.txt'

Why a .bat file?

Creating a `.bat` file on your Desktop triggers a Sysmon FileCreate event (because `.bat` is in your config's include list). Double-clicking it then triggers a ProcessCreate event from an untrusted location. Both events should be caught by your monitor.

  • Open **File Explorer** and navigate to your Desktop.
  • Double-click test-alert.bat to execute it.

Within a few seconds, you should see a Windows toast notification in the bottom-right corner of your screen with the title starting with "Security Alert".

  • Verify the alert was logged to CSV by running:
Import-Csv "C:\SecurityMonitor\Logs\alerts.csv" | Select-Object -Last 3 | Format-Table Timestamp, EventId, ProcessPath, Details
  • You should see recent rows with populated Timestamp, EventId, ProcessPath, and Details columns showing the test batch file activity.

No toast notification appeared?

  • Check that Windows notifications are enabled. Go to **Settings**, then **System**, then **Notifications** and make sure notifications are turned on.
  • The 30-second cooldown may be blocking a repeat alert. Wait 30 seconds and try double-clicking the `.bat` file again.
  • If `alerts.csv` has no new rows either, the background task may have stopped. Run `Start-ScheduledTask -TaskName "SecurityMonitor"` to restart it, then try the test again.

Help me debug why my threat monitor is not generating alerts

  • Clean up the test file by running:
Remove-Item "$env:USERPROFILE\Desktop\test-alert.bat" -ErrorAction SilentlyContinue
Remove-Item "$env:USERPROFILE\test.txt" -ErrorAction SilentlyContinue

Your threat detection system is now fully operational. It starts automatically, runs silently, catches suspicious activity, and logs everything. Next up, you can extend it with a daily summary report in the Secret Mission.

Secret mission

Build a Daily HTML Summary Report

Your monitor is logging alerts around the clock. But who wants to scroll through raw CSV data every morning? In this mission, you'll build an automated daily report that aggregates your threat data into a polished HTML dashboard with stats, top suspicious processes, and a complete event table.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally on your machine, so there are no ongoing cloud costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Sysmon64 service running as a Windows service (uses minimal CPU and memory with the filtered config).
  • Scheduled task SecurityMonitor running the PowerShell monitoring script at logon.
  • Scheduled task SecurityMonitorDailyReport running the daily report script at 8:00 AM.
  • C:\SecurityMonitor\ folder containing scripts, config, trusted-apps list, and log files.
  • C:\Sysmon\ folder containing the extracted Sysmon binary.
  • BurntToast PowerShell module installed for the current user.

Keep everything running

No action needed. Choose this if you want ongoing protection on your machine. The monitor will continue running silently at logon and alerting you to suspicious activity.

  • Sysmon uses minimal CPU and memory with the filtered configuration.
  • Log files in C:\SecurityMonitor\Logs\ will grow over time. Consider periodically deleting old alerts.csv entries or adding log rotation.
  • The BurntToast module stays installed for your user profile.

Pause - I'll come back to this later

Shut down the monitoring tasks to free up resources while keeping all files in place. Sysmon continues writing to the Windows Event Log, but no toast notifications fire and no CSV logging occurs.

  • Stop the monitoring scheduled task by running this in an elevated PowerShell:
Stop-ScheduledTask -TaskName "SecurityMonitor"
  • Stop the daily report scheduled task by running:
Stop-ScheduledTask -TaskName "SecurityMonitorDailyReport"
  • When you want to resume, run Start-ScheduledTask -TaskName "SecurityMonitor" and Start-ScheduledTask -TaskName "SecurityMonitorDailyReport" in an elevated PowerShell.

Delete - I don't want to use this again

Remove all project resources completely. Run each of the following commands in an elevated PowerShell.

  • Uninstall Sysmon by running:
C:\Sysmon\Sysmon64.exe -u
  • Remove the monitoring scheduled task by running:
Unregister-ScheduledTask -TaskName "SecurityMonitor" -Confirm:$false
  • Remove the daily report scheduled task by running:
Unregister-ScheduledTask -TaskName "SecurityMonitorDailyReport" -Confirm:$false
  • Delete the project folder by running:
Remove-Item -Path "C:\SecurityMonitor" -Recurse -Force
  • Delete the Sysmon binary folder by running:
Remove-Item -Path "C:\Sysmon" -Recurse -Force
  • Uninstall the BurntToast module by running:
Uninstall-Module -Name BurntToast -Force
  • Verify everything is gone by running:
Get-Service Sysmon64 -ErrorAction SilentlyContinue
Test-Path "C:\SecurityMonitor"
Test-Path "C:\Sysmon"

All three checks should return errors or False. Your machine is back to its original state.

Nice Work!

Nice Work!

You just built a production-ready, silent security monitor that watches your entire Windows 11 machine for suspicious activity in real-time. From kernel-level telemetry to desktop toast notifications, this system runs entirely on native Microsoft tools with zero ongoing cost.

You've learned how to:

  • Install and configure Sysmon with a custom XML configuration that filters out noisy system processes while capturing security-relevant events like suspicious file creation, process execution, and registry persistence changes.
  • Build an event-driven monitoring script using the EventLogWatcher .NET class that reacts to suspicious activity in real-time with zero polling overhead, firing toast notifications and logging structured CSV alerts.
  • Implement a two-layer whitelisting system (Sysmon config level plus script-level trusted-apps list) and auto-start the monitor silently at login via Task Scheduler.
  • Secret Mission: Created an automated daily HTML summary report that generates each morning with stats on suspicious events from the past 24 hours.

Ready to quiz yourself?