Analyze Windows Event Logs

Hello,
I want to know which file or folder was deleted by whom.

The problem is that there is no file or folder name in ID 4660 and I need to extract the file or folder name from ID 4663, but how do I link these together? How do I know which ID 4660 is related to which ID 4663? What field is common between these IDs?

For example, A user named Jason creates a file or folder named Windows and either he or another user named James deletes this file or folder. I want to generate an output that tells me that the file or folder named Windows was deleted by the user named James.

For my purpose, I found the following script. What’s the problem?

# Get Event ID 4663 (Object Access)
$events4663 = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4663} | ForEach-Object {
    [PSCustomObject]@{
        TimeCreated = $_.TimeCreated
        ObjectHandleID = $_.Properties[7].Value
        ObjectName = $_.Properties[6].Value
        User = $_.Properties[1].Value
    }
}

# Get Event ID 4660 (Object Deletion)
$events4660 = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4660} | ForEach-Object {
    [PSCustomObject]@{
        TimeCreated = $_.TimeCreated
        ObjectHandleID = $_.Properties[7].Value
        User = $_.Properties[1].Value
    }
}

# Correlate events
foreach ($event4660 in $events4660) {
    $matching4663 = $events4663 | Where-Object { $_.ObjectHandleID -eq $event4660.ObjectHandleID }
    if ($matching4663) {
        Write-Output "File/Folder '$($matching4663.ObjectName)' was deleted by '$($event4660.User)' at $($event4660.TimeCreated)."
    }
}

I expect output like the following:

File/Folder 'C:\New Folder' was deleted by 'Jason' at 2/24/2025 10:15:00 AM.

Thank you.

According to this link, you will need to correlate to event ID 4656 to get the object name.

https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=4660

I just setup logging for these events. You can correlate them using handle id.

Hello,
Thank you so much for your reply.
The required information is also available in Event ID 4663.

Hello,
Thank you so much for your reply.
I need an output like the following:

File/Folder 'C:\New Folder' was deleted by 'Jason' at 2/24/2025 10:15:00 AM.

Gotcha. Well you are getting all the event’s that you need. It’s not correlating the events by object handle id correctly when running the events through the where-object pipeline.

Using the -match comparison you can obtain all matches and since there should only be a single match for the handle id in each of the event log streams this will also work.

# Correlate events
foreach ($event4660 in $events4660) {
     $matching4663 = $events4663 -match $event4660.ObjectHandleID
     if ($null -ne $matching4663[0]) {
         Write-Host "File/Folder '$($matching4663[0].ObjectName)' was deleted by '$($event4660.User)' at $($event4660.TimeCreated)."
         Write-host ""
     }
}

Hi,
Thanks again.
Can you tell me the problem with the following code:

# Define the output JSON file path
$outputFile = "C:\DeletedFilesLog.json"

# Get the security event logs for Event ID 4663 (File Deletion) and 4660 (File Handle Creation)
$events4663 = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4663} -MaxEvents 1000
$events4660 = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4660} -MaxEvents 1000

# Create an array to store the results
$results = @()

# Loop through each Event ID 4663 (File Deletion)
foreach ($event4663 in $events4663) {
    # Extract the Handle ID from Event ID 4663
    $handleId4663 = ($event4663.Properties | Where-Object { $_.Id -eq "0x80" }).Value

    # Find the corresponding Event ID 4660 (File Handle Creation) with the same Handle ID
    $matchingEvent4660 = $events4660 | Where-Object {
        ($_.Properties | Where-Object { $_.Id -eq "0x80" }).Value -eq $handleId4663
    } | Select-Object -First 1

    if ($matchingEvent4660) {
        # Extract relevant information
        $hostname = $env:COMPUTERNAME
        $username = ($matchingEvent4660.Properties | Where-Object { $_.Id -eq "0x84" }).Value
        $fileOrFolderName = ($event4663.Properties | Where-Object { $_.Id -eq "0x86" }).Value
        $dateTime = $event4663.TimeCreated

        # Create a custom object to store the information
        $result = [PSCustomObject]@{
            Hostname = $hostname
            Username = $username
            FileOrFolderName = $fileOrFolderName
            DateTime = $dateTime
        }

        # Add the result to the array
        $results += $result
    }
}

# Convert the results to JSON and save to the output file
$results | ConvertTo-Json | Out-File -FilePath $outputFile -Encoding UTF8

Write-Host "Deleted files log saved to $outputFile"

What’s wrong with your original code? It looks like you may have copy pasted this code from ChatGPT. Which is fine with me, but you should learn how to step through your code and debug it before asking what’s wrong with it…

You are looking for an ID property in the properties property which doesn’t exist. You are comparing with a static value of 0x84, 0x86, 0x80.

What was the purpose for rewriting it all?

2 Likes

Hi,
You right. I found a solution.

1 Like