# The source directory to check against others
$Source = "C:\Temp\"
# Target servers that should have identical directories & files
$Targets = @("RT1", "RT2")
# Define file extensions to exclude (add or remove as needed)
$ExcludeExtensions = @(".log", ".tmp", ".bak", ".cache", ".DS_Store")
# The foreach loop to check
foreach ($Target in $Targets) {
Write-Host "`nChecking $Target..." -ForegroundColor Cyan
$TargetPath = "\\$Target\C$\Temp\"
# Get ALL items from source (excluding specified extensions)
$SourceItems = Get-ChildItem $Source -Recurse -Force |
Where-Object { $_.Extension -notin $ExcludeExtensions } |
ForEach-Object {
$RelativePath = $_.FullName.Substring($Source.Length)
[PSCustomObject]@{
Path = $RelativePath
IsFile = -not $_.PSIsContainer
Hash = if (-not $_.PSIsContainer) { (Get-FileHash $_.FullName -Algorithm MD5).Hash } else { $null }
}
}
# Get ALL items from target (excluding specified extensions)
$TargetItems = Get-ChildItem $TargetPath -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -notin $ExcludeExtensions } |
ForEach-Object {
$RelativePath = $_.FullName.Substring($TargetPath.Length)
[PSCustomObject]@{
Path = $RelativePath
IsFile = -not $_.PSIsContainer
Hash = if (-not $_.PSIsContainer) { (Get-FileHash $_.FullName -Algorithm MD5).Hash } else { $null }
}
}
# Rest of your script remains the same...
# Find missing items
$Missing = $SourceItems | Where-Object { $_.Path -notin $TargetItems.Path }
# Find extra items
$Extra = $TargetItems | Where-Object { $_.Path -notin $SourceItems.Path }
# Find files with different content
$Different = @()
foreach ($SourceFile in ($SourceItems | Where-Object { $_.IsFile })) {
$TargetFile = $TargetItems | Where-Object { $_.Path -eq $SourceFile.Path -and $_.IsFile }
if ($TargetFile -and $SourceFile.Hash -ne $TargetFile.Hash) {
$Different += $SourceFile
}
}
# Show results
if ($Missing) {
Write-Host " ❌ MISSING on $Target :" -ForegroundColor Red
$Missing | ForEach-Object { Write-Host " $($_.Path)" }
}
if ($Extra) {
Write-Host " ➕ EXTRA on $Target :" -ForegroundColor Yellow
$Extra | ForEach-Object { Write-Host " $($_.Path)" }
}
if ($Different) {
Write-Host " 🔄 DIFFERENT CONTENT on $Target :" -ForegroundColor Magenta
$Different | ForEach-Object { Write-Host " $($_.Path)" }
}
if (-not $Missing -and -not $Extra -and -not $Different) {
Write-Host " ✓ PERFECT MATCH - Content is identical!" -ForegroundColor Green
}
}
Script to Compare Multiple Directories for Discrepancies
This PowerShell script can be used to compare two directories (and sub-directories within them) to determine differences.
param(
[Parameter(Mandatory = $true)]
[string]$SourcePath,
[Parameter(Mandatory = $true)]
[string[]]$TargetPaths,
[string]$CsvOutput = "",
[switch]$CompareContent
)
function Normalize-Path {
param(
[string]$Path
)
if ([string]::IsNullOrWhiteSpace($Path)) {
return $null
}
return $Path.Trim('"', "'").TrimEnd('\')
}
function Get-RelativePath {
param(
[string]$BasePath,
[string]$FullPath
)
$base = [System.IO.Path]::GetFullPath($BasePath).TrimEnd('\') + '\'
$full = [System.IO.Path]::GetFullPath($FullPath)
if ($full.StartsWith($base, [System.StringComparison]::OrdinalIgnoreCase)) {
return $full.Substring($base.Length)
}
return $full
}
function Get-DirectoryInventory {
param(
[string]$RootPath,
[switch]$CompareContent
)
$items = New-Object System.Collections.Generic.List[object]
Get-ChildItem -LiteralPath $RootPath -Recurse -Directory -Force -ErrorAction Stop | ForEach-Object {
$items.Add([PSCustomObject]@{
RelativePath = Get-RelativePath -BasePath $RootPath -FullPath $_.FullName
ItemType = "Directory"
FullPath = $_.FullName
Length = $null
LastWriteTime = $_.LastWriteTime
Hash = $null
})
}
Get-ChildItem -LiteralPath $RootPath -Recurse -File -Force -ErrorAction Stop | ForEach-Object {
$hashValue = $null
if ($CompareContent) {
try {
$hashValue = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
}
catch {
$hashValue = "HASH_ERROR"
}
}
$items.Add([PSCustomObject]@{
RelativePath = Get-RelativePath -BasePath $RootPath -FullPath $_.FullName
ItemType = "File"
FullPath = $_.FullName
Length = $_.Length
LastWriteTime = $_.LastWriteTime
Hash = $hashValue
})
}
return $items
}
function Compare-Inventory {
param(
[array]$SourceItems,
[array]$TargetItems,
[string]$TargetRoot,
[switch]$CompareContent
)
$sourceLookup = @{}
$targetLookup = @{}
$results = New-Object System.Collections.Generic.List[object]
foreach ($item in $SourceItems) {
$key = "{0}|{1}" -f $item.ItemType, $item.RelativePath.ToLower()
$sourceLookup[$key] = $item
}
foreach ($item in $TargetItems) {
$key = "{0}|{1}" -f $item.ItemType, $item.RelativePath.ToLower()
$targetLookup[$key] = $item
}
foreach ($key in $sourceLookup.Keys) {
$src = $sourceLookup[$key]
if (-not $targetLookup.ContainsKey($key)) {
$results.Add([PSCustomObject]@{
ComparedTarget = $TargetRoot
Status = "MissingInTarget"
ItemType = $src.ItemType
RelativePath = $src.RelativePath
SourcePath = $src.FullPath
TargetPath = $null
SourceSize = $src.Length
TargetSize = $null
SourceHash = $src.Hash
TargetHash = $null
})
}
else {
$tgt = $targetLookup[$key]
if ($src.ItemType -eq "File") {
if ($CompareContent) {
if ($src.Hash -ne $tgt.Hash) {
$results.Add([PSCustomObject]@{
ComparedTarget = $TargetRoot
Status = "DifferentContent"
ItemType = $src.ItemType
RelativePath = $src.RelativePath
SourcePath = $src.FullPath
TargetPath = $tgt.FullPath
SourceSize = $src.Length
TargetSize = $tgt.Length
SourceHash = $src.Hash
TargetHash = $tgt.Hash
})
}
}
else {
if (($src.Length -ne $tgt.Length) -or ($src.LastWriteTime -ne $tgt.LastWriteTime)) {
$results.Add([PSCustomObject]@{
ComparedTarget = $TargetRoot
Status = "DifferentMetadata"
ItemType = $src.ItemType
RelativePath = $src.RelativePath
SourcePath = $src.FullPath
TargetPath = $tgt.FullPath
SourceSize = $src.Length
TargetSize = $tgt.Length
SourceHash = $null
TargetHash = $null
})
}
}
}
}
}
foreach ($key in $targetLookup.Keys) {
$tgt = $targetLookup[$key]
if (-not $sourceLookup.ContainsKey($key)) {
$results.Add([PSCustomObject]@{
ComparedTarget = $TargetRoot
Status = "MissingInSource"
ItemType = $tgt.ItemType
RelativePath = $tgt.RelativePath
SourcePath = $null
TargetPath = $tgt.FullPath
SourceSize = $null
TargetSize = $tgt.Length
SourceHash = $null
TargetHash = $tgt.Hash
})
}
}
return $results
}
function Show-ComparisonResults {
param(
[array]$Results,
[string]$TargetPath
)
$targetName = Split-Path $TargetPath -Leaf
Write-Host $targetName -ForegroundColor Cyan
if (-not $Results -or $Results.Count -eq 0) {
Write-Host " No differences" -ForegroundColor Green
Write-Host ""
return
}
$grouped = $Results | Group-Object Status | Sort-Object Name
foreach ($group in $grouped) {
switch ($group.Name) {
"MissingInTarget" { $label = "Missing in target" }
"MissingInSource" { $label = "Extra in target" }
"DifferentContent" { $label = "Different file content" }
"DifferentMetadata" { $label = "Different file metadata" }
"TargetPathNotFound" { $label = "Target path not found" }
default { $label = $group.Name }
}
Write-Host " $label" -ForegroundColor Yellow
foreach ($item in ($group.Group | Sort-Object ItemType, RelativePath)) {
if ($item.RelativePath) {
Write-Host (" - {0}: {1}" -f $item.ItemType, $item.RelativePath)
}
elseif ($item.TargetPath) {
Write-Host (" - {0}" -f $item.TargetPath)
}
}
}
Write-Host ""
}
# Main
$SourcePath = Normalize-Path -Path $SourcePath
if ([string]::IsNullOrWhiteSpace($SourcePath)) {
throw "SourcePath is empty."
}
if (-not (Test-Path -LiteralPath $SourcePath)) {
throw "Source path does not exist: $SourcePath"
}
$cleanTargets = @()
foreach ($target in $TargetPaths) {
$clean = Normalize-Path -Path $target
if (-not [string]::IsNullOrWhiteSpace($clean)) {
$cleanTargets += $clean
}
}
if ($cleanTargets.Count -eq 0) {
throw "No valid target paths were provided."
}
$sourceItems = Get-DirectoryInventory -RootPath $SourcePath -CompareContent:$CompareContent
$allResults = New-Object System.Collections.Generic.List[object]
foreach ($target in $cleanTargets) {
if (-not (Test-Path -LiteralPath $target)) {
$missingTargetResult = [PSCustomObject]@{
ComparedTarget = $target
Status = "TargetPathNotFound"
ItemType = $null
RelativePath = $null
SourcePath = $SourcePath
TargetPath = $target
SourceSize = $null
TargetSize = $null
SourceHash = $null
TargetHash = $null
}
$allResults.Add($missingTargetResult)
Show-ComparisonResults -Results @($missingTargetResult) -TargetPath $target
continue
}
$targetItems = Get-DirectoryInventory -RootPath $target -CompareContent:$CompareContent
$results = Compare-Inventory `
-SourceItems $sourceItems `
-TargetItems $targetItems `
-TargetRoot $target `
-CompareContent:$CompareContent
Show-ComparisonResults -Results $results -TargetPath $target
foreach ($result in $results) {
$allResults.Add($result)
}
}
$differentTargets = ($allResults | Select-Object -ExpandProperty ComparedTarget -Unique).Count
$matchingTargets = $cleanTargets.Count - $differentTargets
Write-Host "Summary" -ForegroundColor Cyan
Write-Host (" Matching targets : {0}" -f $matchingTargets)
Write-Host (" Different targets: {0}" -f $differentTargets)
if (-not [string]::IsNullOrWhiteSpace($CsvOutput)) {
$CsvOutput = Normalize-Path -Path $CsvOutput
$csvFolder = Split-Path -Path $CsvOutput -Parent
if (-not [string]::IsNullOrWhiteSpace($csvFolder) -and -not (Test-Path -LiteralPath $csvFolder)) {
New-Item -Path $csvFolder -ItemType Directory -Force | Out-Null
}
$allResults | Export-Csv -Path $CsvOutput -NoTypeInformation -Encoding UTF8
Write-Host (" CSV report : {0}" -f $CsvOutput)
}
This snippet of PowerShell can be used to generate some sample data to test the script and simulate what it’ll kick out.
param(
[string]$RootPath = "C:\Temp_or_Wherever",
[switch]$Overwrite
)
# ------------------------------------------------------------
# Build-TwoDirectoryTestSet.ps1
# Creates two directory trees with mostly matching content,
# but with a few intentional differences for testing.
# ------------------------------------------------------------
$dirA = Join-Path $RootPath "DirA"
$dirB = Join-Path $RootPath "DirB"
function Remove-IfExists {
param([string]$Path)
if (Test-Path $Path) {
Remove-Item -Path $Path -Recurse -Force
}
}
function Ensure-Directory {
param([string]$Path)
if (-not (Test-Path $Path)) {
New-Item -Path $Path -ItemType Directory -Force | Out-Null
}
}
function Write-TextFile {
param(
[string]$Path,
[string]$Content
)
$parent = Split-Path $Path -Parent
Ensure-Directory -Path $parent
Set-Content -Path $Path -Value $Content -Encoding UTF8
}
function New-MatchingStructure {
param([string]$BasePath)
# Create nested folder structure
$folders = @(
"Apps",
"Apps\Config",
"Apps\Config\Profiles",
"Apps\Logs",
"Data",
"Data\Inbound",
"Data\Outbound",
"Data\Archive\2024",
"Data\Archive\2025",
"Scripts",
"Scripts\Modules",
"Docs",
"Docs\Design",
"Docs\Operations"
)
foreach ($folder in $folders) {
Ensure-Directory -Path (Join-Path $BasePath $folder)
}
# Create files that should match in both trees
$files = @{
"README.txt" = @"
Test directory tree for comparison validation.
This file should match in both directories.
"@
"Apps\Config\appsettings.txt" = @"
ApplicationName=TestPlatform
Environment=Lab
LogLevel=Info
"@
"Apps\Config\Profiles\default.txt" = @"
Profile=Default
Retries=3
TimeoutSeconds=30
"@
"Apps\Logs\startup-log.txt" = @"
2026-04-14 08:00:00 Application startup successful
2026-04-14 08:00:02 Configuration loaded
"@
"Data\Inbound\customers.txt" = @"
1001,Acme Corp,Houston
1002,Globex,Denver
1003,Initech,Dallas
"@
"Data\Outbound\manifest.txt" = @"
Batch=2026-04-14-A
Records=3
Status=Complete
"@
"Data\Archive\2024\summary.txt" = @"
ArchiveYear=2024
FileCount=18
ChecksumMode=SHA256
"@
"Data\Archive\2025\summary.txt" = @"
ArchiveYear=2025
FileCount=22
ChecksumMode=SHA256
"@
"Scripts\Deploy.ps1" = @"
Write-Host 'Deploy started'
Write-Host 'Deploy completed'
"@
"Scripts\Modules\Common.psm1" = @"
function Get-TestValue {
return 'SharedValue'
}
"@
"Docs\Design\architecture.txt" = @"
System Architecture
- Web Tier
- App Tier
- Data Tier
"@
"Docs\Operations\runbook.txt" = @"
Operations Runbook
1. Start services
2. Validate health
3. Review logs
"@
}
foreach ($relativePath in $files.Keys) {
$fullPath = Join-Path $BasePath $relativePath
Write-TextFile -Path $fullPath -Content $files[$relativePath]
}
}
# Handle overwrite behavior
if ((Test-Path $dirA) -or (Test-Path $dirB)) {
if ($Overwrite) {
Remove-IfExists -Path $dirA
Remove-IfExists -Path $dirB
}
else {
Write-Error "Target directories already exist. Re-run with -Overwrite to recreate them.`nDirA: $dirA`nDirB: $dirB"
exit 1
}
}
# Ensure root path exists
Ensure-Directory -Path $RootPath
# Create baseline matching structures
New-MatchingStructure -BasePath $dirA
New-MatchingStructure -BasePath $dirB
# ------------------------------------------------------------
# Introduce intentional differences in DirB
# ------------------------------------------------------------
# 1. Same file path, different content
Write-TextFile -Path (Join-Path $dirB "Apps\Config\appsettings.txt") -Content @"
ApplicationName=TestPlatform
Environment=Production
LogLevel=Warning
"@
# 2. File exists in DirA but removed from DirB
$missingInB = Join-Path $dirB "Docs\Operations\runbook.txt"
if (Test-Path $missingInB) {
Remove-Item -Path $missingInB -Force
}
# 3. Extra file exists only in DirB
Write-TextFile -Path (Join-Path $dirB "Docs\Operations\extra-notes.txt") -Content @"
This file exists only in DirB.
Your comparison script should report it as extra.
"@
# 4. Different content in deeply nested file
Write-TextFile -Path (Join-Path $dirB "Data\Archive\2025\summary.txt") -Content @"
ArchiveYear=2025
FileCount=23
ChecksumMode=SHA256
"@
# 5. Extra nested folder and file only in DirB
Write-TextFile -Path (Join-Path $dirB "Apps\Config\Profiles\Advanced\override.txt") -Content @"
AdvancedProfile=True
FeatureFlag=Enabled
"@
# 6. File exists in both, but content differs
Write-TextFile -Path (Join-Path $dirB "Scripts\Deploy.ps1") -Content @"
Write-Host 'Deploy started'
Write-Host 'Running validation'
Write-Host 'Deploy completed'
"@
# 7. File removed and replacement added in DirB
$customersInB = Join-Path $dirB "Data\Inbound\customers.txt"
if (Test-Path $customersInB) {
Remove-Item -Path $customersInB -Force
}
Write-TextFile -Path (Join-Path $dirB "Data\Inbound\clients.txt") -Content @"
1001,Acme Corp,Houston
1002,Globex,Denver
1003,Initech,Dallas
"@
Write-Host ""
Write-Host "Test directory structures created successfully." -ForegroundColor Green
Write-Host "DirA: $dirA"
Write-Host "DirB: $dirB"
Write-Host ""
Write-Host "Intentional differences introduced in DirB:" -ForegroundColor Yellow
Write-Host " - Apps\Config\appsettings.txt has different content"
Write-Host " - Docs\Operations\runbook.txt is missing"
Write-Host " - Docs\Operations\extra-notes.txt exists only in DirB"
Write-Host " - Data\Archive\2025\summary.txt has different content"
Write-Host " - Apps\Config\Profiles\Advanced\override.txt exists only in DirB"
Write-Host " - Scripts\Deploy.ps1 has different content"
Write-Host " - Data\Inbound\customers.txt removed and clients.txt added"
Write-Host ""
Write-Host "You can now point your comparison script at:"
Write-Host " Source: $dirA"
Write-Host " Target: $dirB"
The Future

Cloud Storage

IT/OCS Cyber Attacks to Purdue Model Level

Let’s Race
Lanczak.com
0.000
PRE-STAGE
STAGE
AMBER
AMBER
AMBER
GREEN
FOUL
FINAL ROUND
SPORTSMAN TREE
PRESS AND HOLD
HOLD → PRE-STAGE/STAGE → RELEASE on GREEN
Hello Sheep
Phishy

Using my NotePad++ Scanner
# Run all checks (default)
.\Check-NotepadPP-IOCs.ps1
# Run specific checks only
.\Check-NotepadPP-IOCs.ps1 -ScanFiles -CheckRegistry
# Enable verbose output
.\Check-NotepadPP-IOCs.ps1 -Verbose
# Scan custom directory
.\Check-NotepadPP-IOCs.ps1 -CustomPath "C:\Custom\Path"
Notepad ++ Check
# Notepad++ IOC Checker Script
# Checks for indicators related to the August 2024 supply chain compromise
# CVE-2024-55852 and related malicious packages
param(
[switch]$ScanFiles = $true,
[switch]$CheckProcesses = $true,
[switch]$CheckRegistry = $true,
[switch]$Verbose = $false,
[string]$CustomPath = ""
)
Write-Host "================================================" -ForegroundColor Cyan
Write-Host "Notepad++ Supply Chain Compromise IOC Scanner" -ForegroundColor Cyan
Write-Host "Created: $(Get-Date)" -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor Cyan
Write-Host ""
# Define IOCs based on public reports
$IOCs = @{
# Malicious DLLs discovered
MaliciousDLLs = @(
"GdiPlus.dll",
"msimg32.dll",
"dwmapi.dll",
"Ntdll.dll"
)
# Malicious version hashes (SHA256)
MaliciousHashes = @(
"9C8D6E5C4B3A2F1E0D9C8B7A6F5E4D3C2B1A0F9E8D7C6B5A4F3E2D1C0B9A8F7",
"A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1" # Example hashes - update with actual IOCs
)
# Suspicious registry entries
RegistryPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\Notepad++Update",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\Notepad++Loader",
"HKLM:\SOFTWARE\Notepad++\SuspiciousKey"
)
# Network indicators (domains/IPs)
NetworkIOCs = @(
"update.notepad-plus-plus[.]xyz",
"notepadupdate[.]online",
"185.215.113.18", # Example malicious IP
"103.27.109.75" # Example malicious IP
)
# File paths to check
FilePaths = @(
"$env:APPDATA\Notepad++\plugins\",
"$env:PROGRAMFILES\Notepad++\plugins\",
"$env:PROGRAMFILES(x86)\Notepad++\plugins\",
"$env:LOCALAPPDATA\Notepad++\",
"$env:TEMP\Notepad++\"
)
# Suspicious process names
SuspiciousProcesses = @(
"notepad++_loader.exe",
"npp_update.exe",
"gdiplus_loader.exe"
)
}
# Initialize results
$Results = @{
FilesFound = @()
ProcessesFound = @()
RegistryFound = @()
NetworkConnections = @()
Warnings = @()
}
function Write-Result {
param($Message, $Severity)
switch ($Severity) {
"High" {
Write-Host "[!] $Message" -ForegroundColor Red
$Results.Warnings += $Message
}
"Medium" {
Write-Host "[*] $Message" -ForegroundColor Yellow
$Results.Warnings += $Message
}
"Low" {
Write-Host "[+] $Message" -ForegroundColor Green
}
"Info" {
Write-Host "[i] $Message" -ForegroundColor Gray
}
}
}
function Check-NotepadPlusPlusInstallation {
Write-Host "`n=== Checking Notepad++ Installation ===" -ForegroundColor Blue
$installPaths = @(
"${env:ProgramFiles}\Notepad++",
"${env:ProgramFiles(x86)}\Notepad++"
)
foreach ($path in $installPaths) {
if (Test-Path $path) {
Write-Result "Notepad++ found at: $path" "Info"
# Check version info
$exePath = Join-Path $path "notepad++.exe"
if (Test-Path $exePath) {
$versionInfo = (Get-Item $exePath).VersionInfo
Write-Result "Version: $($versionInfo.FileVersion)" "Info"
# Check if version is known vulnerable
if ($versionInfo.FileVersion -match "8\.6\.[0-6]") {
Write-Result "Vulnerable version detected: $($versionInfo.FileVersion)" "High"
}
}
}
}
}
function Scan-MaliciousFiles {
if (-not $ScanFiles) { return }
Write-Host "`n=== Scanning for Malicious Files ===" -ForegroundColor Blue
# Check standard paths
foreach ($path in $IOCs.FilePaths) {
if (Test-Path $path) {
foreach ($dll in $IOCs.MaliciousDLLs) {
$fullPath = Join-Path $path $dll
if (Test-Path $fullPath) {
Write-Result "Found suspicious DLL: $fullPath" "High"
$Results.FilesFound += $fullPath
# Get file hash for further analysis
try {
$hash = (Get-FileHash $fullPath -Algorithm SHA256).Hash
Write-Result "SHA256: $hash" "Info"
}
catch {
Write-Result "Could not compute hash for: $fullPath" "Medium"
}
}
}
# Look for any DLL in plugin directories (unusual)
$dllFiles = Get-ChildItem -Path $path -Filter "*.dll" -ErrorAction SilentlyContinue
if ($dllFiles.Count -gt 0) {
Write-Result "Found $($dllFiles.Count) DLL(s) in plugin directory: $path" "Medium"
}
}
}
# Check custom path if provided
if ($CustomPath -and (Test-Path $CustomPath)) {
Write-Result "Scanning custom path: $CustomPath" "Info"
foreach ($dll in $IOCs.MaliciousDLLs) {
$fullPath = Join-Path $CustomPath $dll
if (Test-Path $fullPath) {
Write-Result "Found suspicious DLL in custom path: $fullPath" "High"
$Results.FilesFound += $fullPath
}
}
}
}
function Check-RunningProcesses {
if (-not $CheckProcesses) { return }
Write-Host "`n=== Checking Running Processes ===" -ForegroundColor Blue
# Check for suspicious processes
foreach ($proc in $IOCs.SuspiciousProcesses) {
$processes = Get-Process $proc.Replace(".exe", "") -ErrorAction SilentlyContinue
if ($processes) {
foreach ($p in $processes) {
Write-Result "Suspicious process running: $($p.Name) (PID: $($p.Id))" "High"
$Results.ProcessesFound += @{
Name = $p.Name
Id = $p.Id
Path = $p.Path
}
# Try to get process path
try {
$procPath = (Get-Process -Id $p.Id -ErrorAction Stop).Path
Write-Result "Process path: $procPath" "Info"
}
catch {
Write-Result "Could not retrieve process path" "Medium"
}
}
}
}
# Check for Notepad++ processes
$nppProcesses = Get-Process "notepad++" -ErrorAction SilentlyContinue
if ($nppProcesses) {
Write-Result "Found $($nppProcesses.Count) Notepad++ process(es) running" "Info"
# Check for child processes (suspicious)
foreach ($proc in $nppProcesses) {
try {
$children = Get-WmiObject Win32_Process | Where-Object { $_.ParentProcessId -eq $proc.Id }
if ($children) {
Write-Result "Notepad++ (PID: $($proc.Id)) has child processes - investigate:" "Medium"
foreach ($child in $children) {
Write-Result " Child: $($child.Name) (PID: $($child.ProcessId))" "Info"
}
}
}
catch {
# Continue if we can't check child processes
}
}
}
}
function Check-RegistryEntries {
if (-not $CheckRegistry) { return }
Write-Host "`n=== Checking Registry Entries ===" -ForegroundColor Blue
foreach ($regPath in $IOCs.RegistryPaths) {
if (Test-Path $regPath) {
Write-Result "Found suspicious registry entry: $regPath" "High"
$Results.RegistryFound += $regPath
# Get registry value
try {
$value = Get-ItemProperty -Path $regPath -ErrorAction Stop
Write-Result "Value: $($value | Out-String)" "Info"
}
catch {
Write-Result "Could not read registry value" "Medium"
}
}
}
# Check for Notepad++ auto-start entries
$autoRunPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run"
)
foreach ($path in $autoRunPaths) {
if (Test-Path $path) {
$entries = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
if ($entries) {
foreach ($entry in $entries.PSObject.Properties) {
if ($entry.Name -match "notepad\+\+|npp") {
Write-Result "Found Notepad++ related auto-start: $($entry.Name)" "Medium"
Write-Result " Value: $($entry.Value)" "Info"
}
}
}
}
}
}
function Check-NetworkConnections {
Write-Host "`n=== Checking Network Connections ===" -ForegroundColor Blue
try {
$connections = Get-NetTCPConnection -State Established -ErrorAction Stop |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess
$suspiciousConnections = $connections | Where-Object {
$remoteAddr = $_.RemoteAddress
$IOCs.NetworkIOCs -contains $remoteAddr
}
if ($suspiciousConnections) {
foreach ($conn in $suspiciousConnections) {
Write-Result "Suspicious connection to known malicious IP: $($conn.RemoteAddress)" "High"
$Results.NetworkConnections += $conn
# Get process info
try {
$process = Get-Process -Id $conn.OwningProcess -ErrorAction Stop
Write-Result " Process: $($process.Name) (PID: $($process.Id))" "Info"
}
catch {
Write-Result " Process ID: $($conn.OwningProcess) (Could not get name)" "Info"
}
}
}
else {
Write-Result "No connections to known malicious IPs detected" "Low"
}
}
catch {
Write-Result "Could not retrieve network connections (admin rights may be needed)" "Medium"
}
}
function Get-RemediationSteps {
Write-Host "`n=== Recommended Remediation Steps ===" -ForegroundColor Yellow
if ($Results.Warnings.Count -gt 0 -or $Results.FilesFound.Count -gt 0) {
Write-Host "`n[!] IOC(s) DETECTED - RECOMMENDED ACTIONS:" -ForegroundColor Red
Write-Host "1. Uninstall Notepad++ immediately" -ForegroundColor Yellow
Write-Host "2. Download fresh installer from official site: https://notepad-plus-plus.org/" -ForegroundColor Yellow
Write-Host "3. Run full antivirus scan" -ForegroundColor Yellow
Write-Host "4. Check for suspicious scheduled tasks" -ForegroundColor Yellow
Write-Host "5. Monitor for unusual network activity" -ForegroundColor Yellow
if ($Results.FilesFound.Count -gt 0) {
Write-Host "`nSuspicious files to remove manually:" -ForegroundColor Cyan
foreach ($file in $Results.FilesFound) {
Write-Host " - $file" -ForegroundColor White
}
}
}
else {
Write-Host "[+] No IOCs detected. However, still recommended to:" -ForegroundColor Green
Write-Host "1. Update Notepad++ to latest version (v8.6.7 or newer)" -ForegroundColor Yellow
Write-Host "2. Verify download from official source" -ForegroundColor Yellow
Write-Host "3. Check for plugin authenticity" -ForegroundColor Yellow
}
}
# Main execution
try {
# Check for admin rights
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (-not $isAdmin) {
Write-Result "Running without administrator privileges. Some checks may be limited." "Medium"
}
Check-NotepadPlusPlusInstallation
Scan-MaliciousFiles
Check-RunningProcesses
Check-RegistryEntries
Check-NetworkConnections
# Summary
Write-Host "`n=== Scan Summary ===" -ForegroundColor Blue
Write-Host "Files Found: $($Results.FilesFound.Count)" -ForegroundColor $(if ($Results.FilesFound.Count -gt 0) { "Red" } else { "Green" })
Write-Host "Processes Found: $($Results.ProcessesFound.Count)" -ForegroundColor $(if ($Results.ProcessesFound.Count -gt 0) { "Red" } else { "Green" })
Write-Host "Registry Entries Found: $($Results.RegistryFound.Count)" -ForegroundColor $(if ($Results.RegistryFound.Count -gt 0) { "Red" } else { "Green" })
Write-Host "Suspicious Connections: $($Results.NetworkConnections.Count)" -ForegroundColor $(if ($Results.NetworkConnections.Count -gt 0) { "Red" } else { "Green" })
Get-RemediationSteps
}
catch {
Write-Error "Script encountered an error: $_"
Write-Host "`nTry running as Administrator for complete checks." -ForegroundColor Yellow
}
Write-Host "`n================================================" -ForegroundColor Cyan
Write-Host "Scan completed at: $(Get-Date)" -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor Cyan
