Monitoring file system changes with PowerShell

I recently returned from facilitating Lenny Zeltser‘s excellent Reverse Engineering Malware course at SANS Security West.  One of the utilities covered in the course is called CaptureBAT, which is a useful utility for monitoring a system for changes while performing malware analysis.  Of course, given my ongoing interest in PowerShell, I decided to see if I could emulate some of CaptureBAT’s file system monitoring functionality natively in a PS script.

There are several strategies that you can use to monitor the file system in PowerShell and I decided to use the .Net framework to help accomplish this task.  Here’s how I did it:

  1. Create a new System.IO.FileSystemWatcher object, and set appropriate settings:
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $searchPath
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true

    .Path is the path that will be monitored, .IncludeSubdirectories tells the FileSystemWatcher to monitor all subdirectories of .Path

  2. Now we need to define some events that will fire when $watcher detects a filesystem change, I’m going to define an event for Changed, Created, Deleted, and Renamed:
    $changed = Register-ObjectEvent $watcher "Changed" -Action {
       write-host "Changed: $($eventArgs.FullPath)"
    $created = Register-ObjectEvent $watcher "Created" -Action {
       write-host "Created: $($eventArgs.FullPath)"
    $deleted = Register-ObjectEvent $watcher "Deleted" -Action {
       write-host "Deleted: $($eventArgs.FullPath)"
    $renamed = Register-ObjectEvent $watcher "Renamed" -Action {
       write-host "Renamed: $($eventArgs.FullPath)"

    Within each event you can define code for what you want to happen when the event fires.  In this example I’m just directly outputting the type of action and the full path of the changed object on the filesystem.

  3. That’s pretty much it.  These events will hang around until you close your current PowerShell session or manually unregister the events.  You can unregister the events using the Unregister-Event command:
    Unregister-Event $changed.Id
    Unregister-Event $created.Id
    Unregister-Event $deleted.Id
    Unregister-Event $renamed.Id

As you can see – monitoring file system changes is actually quite easy with PowerShell.  You could easily take the output of this script and write it to a file (either using output redirection, or specifying output directly in the script) and with a little more code you could read in a CSV/structured file containing a list of exclusions (i.e. what CaptureBat does) and within each event filter out unwanted “noise.”

The great thing about this method is that it just *works* in Windows systems that have PowerShell.  No need to install any applications or run any third-party utilities – which the malware that you’re looking at may be looking for (i.e. anti-analysis measures built into some malware).  My goal is to build a complete replacement for CaptureBat within PowerShell in my spare time over the next few weeks.  Once done, I’ll post the complete script with explanations on my blog…

But for now, time to spend some quality time with the family.  Have a great weekend.


  1. says

    This could become very interesting when combined with something like netcat to stream the results off to a central collection/monitoring system.

    Nice work.

  2. says

    PowerShell and event management are certainly well intertwined — this is a great example of that.

    You can also monitor system information using Windows Management Instrumentation (WMI) — WMI is a fundamental background service of the Windows operating system since XP / 2003 — it was installable in Windows 2000. I’ve written some code and documentation that makes working with WMI events much easier. Check out this module:

  3. Brian says

    I’d like to use this to be alerted when certain people log into my network.

    As part of our logon scripts, mapped drives and environment variables for the user are logged (appended) to a file \\fs01\admin\logon\user\username.csv.

    Using this fact and the script you’ve supplied above, I’ve come up with this:

    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = “D:\admin\Logon\User”
    $watcher.IncludeSubdirectories = $false
    $watcher.EnableRaisingEvents = $true

    $changed = Register-ObjectEvent $watcher “Changed” -Action {
    write-host “Changed: $($eventArgs.FullPath)”

    if ($($eventArgs.FullPath).contains(“User1″)) {
    Send-MailMessage –From “[email protected]” –To “[email protected]” –Subject “User1 logged in” –Body “Changed: $($eventArgs.FullPath)” –SmtpServer mailserver -BodyAsHtml
    elseif ($($eventArgs.FullPath).contains(“User2″)) {
    Send-MailMessage –From “[email protected]” –To “[email protected]” –Subject “User2 logged in” –Body “Changed: $($eventArgs.FullPath)” –SmtpServer mailserver -BodyAsHtml

    The problem is that when a file changes, I receive multiple email alerts – when I only want one. How can I filter it so I only receive one email?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>