How I Built a Windows Security Monitor That Records Intruders Automatically Using PowerShell
DEV Community

How I Built a Windows Security Monitor That Records Intruders Automatically Using PowerShell

How I Built a Windows Security Monitor That Records Intruders Automatically Using PowerShell

The Story That Started It All

I watched a video that stuck with me. An engineer had spent hours building an entire software from scratch. The moment he stepped away from his PC, someone who had been observing his pattern walked over, plugged in a flash drive, and copied the entire work. The painful part was not just the theft. It was how it happened. The spy had been watching. He knew the pattern. He tried the security details a few times before he got in. That video made me ask a question I could not stop thinking about: How do you secure a PC in an environment where it cannot be stolen but can still be accessed without your permission? That question sent me down a rabbit hole of research. A few weeks ago, curiosity turned into code. What I Built is a Windows security monitoring tool built entirely in PowerShell with no third-party security software. Here is what it does: After 3 failed login attempts on your Windows PC, two things happen automatically: an email alert fires instantly to two configured addresses, and the webcam starts recording silently. The person trying to get in cannot stop the recording unless they have access to the code configured for manual stop or the keyboard shortcut built into the system. A few days ago I confirmed it worked. I watched recorded footage of an actual attempt to enter my system.

What I Built

How It Works

Step 1 - Monitoring Failed Logins

Windows logs every failed login attempt as Event ID 4625 in the Security Event Log. PowerShell can read this in real time.

$events = Get-WinEvent -FilterHashtable @{
    LogName = "Security"
    Id     = 4625
    StartTime = (Get-Date).AddSeconds(-30)
} -ErrorAction SilentlyContinue
$count = @($events).Count

The key fix here was using a sliding 30-second window instead of counting from script start. Counting from script start causes the number to grow forever and trigger duplicate recordings. The sliding window resets naturally every poll cycle.

Step 2 - Email Alert via Gmail SMTP

The moment the threshold is hit, an email fires to two addresses using Gmail SMTP through PowerShell's built-in System.Net.Mail.

$smtp = New-Object System.Net.Mail.SmtpClient("smtp.gmail.com", 587)
$smtp.EnableSsl = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($gmailFrom, $gmailPass)

No external libraries. No API keys to pay for. Just a Gmail App Password and two lines of configuration.

Step 3 - Webcam Recording via FFmpeg

FFmpeg captures the webcam using DirectShow, Windows' built-in camera interface.

$ffmpegArgs = "-rtbufsize 100M -f dshow -i video=`"Integrated Webcam`" -vcodec libx264 -preset ultrafast "$outputFile""

The biggest bug I hit here was that combining -NoNewWindow and -WindowStyle Hidden when launching FFmpeg from a script caused DirectShow to fail silently. The webcam light never turned on and no error was written. The fix was switching to ProcessStartInfo with the window set to Minimized instead of Hidden, which gives DirectShow the process context it needs.

Step 4 - Hotkey Stop via Win32 API

A separate script uses embedded C# inside PowerShell to register a global hotkey through the Win32 RegisterHotKey function.

protected override void OnHandleCreated(EventArgs e) {
    base.OnHandleCreated(e);
    RegisterHotKey(this.Handle, HOTKEY_ID, MOD_CONTROL | MOD_ALT, VK_S);
}

The original version called RegisterHotKey inside the constructor, which meant the window handle did not exist yet and the hotkey silently never registered. Moving it to OnHandleCreated fixed it.

Problems I Hit Along the Way

  1. Event count kept inflating - Original code used StartTime set to script start time, so events accumulated forever. Fixed with a rolling 30-second window.
  2. FFmpeg crashed silently with no error log - The combination of -NoNewWindow and -WindowStyle Hidden on Start-Process blocked DirectShow from accessing the webcam. No error. No log. Just a silent crash. Fixed by using ProcessStartInfo directly.
  3. Two scripts running at the same time - During testing, an old version of the script was still running in the background while the new one started. Both tried to grab the webcam simultaneously and both failed. Always kill old PowerShell instances before starting fresh.
  4. Parser errors from special characters - Em dashes and curly quotes copied into the script from documentation caused PowerShell parser errors that looked completely unrelated to the actual problem. Always write PowerShell in plain ASCII.

The Honest Limitation

Windows architecture locks webcam access to active user sessions. This means recording can only start after someone logs in, not before. On Linux, PAM (Pluggable Authentication Modules) and V4L2 (Video4Linux2) make it possible to hook into the login process itself and trigger recording before anyone gets past the login screen. A Linux port is something to explore in the future.

What I Learned

  • Windows Event Log is more accessible than most people realize. PowerShell can query it natively with no extra tools.
  • DirectShow is sensitive to process context. How you launch a process matters as much as what you put in the command line.
  • Silent failures are the hardest bugs. FFmpeg crashing with no output taught me to always check HasExited after launch and always redirect stderr somewhere readable.
  • OS architecture decisions have real consequences. The Windows session model is a security feature. It is also the reason this tool has a fundamental ceiling.

The Full Code

The repository is documented and available on GitHub: https://github.com/alfredoeinsteino2024/SecurityMonitor-PowerShell. The repo includes monitor.ps1, hotkey.ps1, stop.ps1, and a full README with setup instructions. There are still bugs to fix and more to learn. But curiosity got something working and that is enough to keep going.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.