v2.2.0 - Complete Security Hardening Framework (632 Settings)

This commit is contained in:
NexusOne23 2025-12-08 10:32:49 +01:00
commit ba364813ed
195 changed files with 43788 additions and 0 deletions

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# Support NoID Privacy development
buy_me_a_coffee: noidprivacy

79
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,79 @@
---
name: 🐛 Bug Report
about: Report a bug or unexpected behavior
title: '[BUG] '
labels: 'bug'
assignees: ''
---
## 🐛 Bug Description
A clear and concise description of what the bug is.
## 📋 Steps to Reproduce
1. Run command: `...`
2. Configure module: `...`
3. Execute script: `...`
4. See error
## ✅ Expected Behavior
A clear description of what you expected to happen.
## ❌ Actual Behavior
A clear description of what actually happened.
## 💻 System Information
- **OS**: Windows 11 [e.g., 25H2 Build 26200]
- **PowerShell Version**: [e.g., 5.1.26100.2161]
- **CPU**: [e.g., AMD Ryzen 7 9800X3D]
- **TPM**: [e.g., 2.0 Present]
- **Third-Party AV**: [e.g., None, Windows Defender only]
- **Script Version**: [e.g., v2.2.0]
- **Execution Mode**: [Interactive / Direct / DryRun]
**Get System Info:**
```powershell
# Run this to get system info
$PSVersionTable
Get-ComputerInfo | Select-Object OsName, OsVersion, OsBuildNumber
Get-Tpm | Select-Object TpmPresent, TpmReady
```
## 📝 Log Files
Please attach or paste the relevant portion of the log file:
**Location**: `Logs\NoIDPrivacy_YYYYMMDD_HHMMSS.log`
```
[Paste relevant log excerpt here]
```
## 📸 Screenshots
If applicable, add screenshots to help explain your problem.
## 🔍 Additional Context
Add any other context about the problem here:
- Was this a fresh installation or re-run?
- Did the script work previously?
- Any recent system changes?
- Running in VM or physical machine?
## ✔️ Checklist
- [ ] I have searched for similar issues
- [ ] I have verified this is reproducible
- [ ] I have included the log file
- [ ] I have provided complete system information
- [ ] I have tested on a clean Windows 11 25H2 installation (if possible)
## 🔒 Security Note
If this is a **security vulnerability**, please **DO NOT** create a public issue!
Instead, report it privately via: https://github.com/NexusOne23/noid-privacy/security/advisories

View file

@ -0,0 +1,103 @@
---
name: ✨ Feature Request
about: Suggest a new feature or enhancement
title: '[FEATURE] '
labels: 'enhancement'
assignees: ''
---
## 🚀 Feature Request
**Note:** For questions or discussions, please use [GitHub Discussions](https://github.com/NexusOne23/noid-privacy/discussions) instead!
## 🔍 Problem Statement
**Is your feature request related to a problem?**
Describe the problem this feature would solve. Example: "I'm frustrated when [...]"
## 💡 Proposed Solution
Describe the solution you'd like to see implemented.
## 🔄 Alternatives Considered
Describe any alternative solutions or features you've considered.
## 📊 Impact Assessment
Please assess the potential impact of this feature:
### Security Impact
- [ ] Enhances security
- [ ] No security impact
- [ ] Potential security concerns (explain below)
**Details:**
### Privacy Impact
- [ ] Enhances privacy
- [ ] No privacy impact
- [ ] Potential privacy concerns (explain below)
**Details:**
### Compatibility Impact
- [ ] No breaking changes
- [ ] Minor breaking changes
- [ ] Major breaking changes (explain below)
**Details:**
### Usability Impact
- [ ] Improves usability
- [ ] No usability impact
- [ ] May affect usability (explain below)
**Details:**
## 📝 Additional Context
Add any other context, examples, or mockups about the feature request here.
## 🎯 Use Cases
Describe specific use cases where this feature would be beneficial:
1. **Use case 1**: [Description]
2. **Use case 2**: [Description]
3. **Use case 3**: [Description]
## 📚 References
Link to any relevant documentation, standards, or similar implementations:
- [Example: Microsoft documentation]
- [Example: CIS Benchmark requirement]
- [Example: Similar project implementation]
## 🏷️ Module Target
Which module would this feature belong to?
- [ ] SecurityBaseline (MS Baseline settings)
- [ ] ASR (Attack Surface Reduction)
- [ ] DNS (Secure DNS)
- [ ] Privacy (Telemetry, Bloatware)
- [ ] AntiAI (AI Features Lockdown)
- [ ] EdgeHardening (Microsoft Edge)
- [ ] AdvancedSecurity (Beyond MS Baseline)
- [ ] Core (Framework/Architecture)
- [ ] New Module (describe below)
## ✔️ Checklist
- [ ] I have searched for similar feature requests
- [ ] I have considered the impact on security and privacy
- [ ] I have described the problem and proposed solution clearly
- [ ] I have provided use cases and examples
- [ ] This is NOT a security vulnerability (use Security Advisory instead)
## 💼 Commercial Licensing
If this feature is critical for your organization and you need it prioritized, consider our [commercial licensing options](https://github.com/NexusOne23/noid-privacy/discussions) with dedicated support and custom development.

94
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,94 @@
# Pull Request
## 📝 Description
Please include a summary of the changes and the related issue. Explain the motivation and context.
Fixes #(issue number)
## 🎯 Type of Change
Please delete options that are not relevant:
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
- [ ] ✨ New feature (non-breaking change which adds functionality)
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] 📚 Documentation update
- [ ] 🔧 Code refactoring (no functional changes)
- [ ] ⚡ Performance improvement
- [ ] ✅ Test coverage improvement
## 🧪 Testing
Please describe the tests you ran to verify your changes:
- [ ] Tested on Windows 11 25H2
- [ ] Tested on Windows 11 24H2
- [ ] Tested in VM environment
- [ ] Tested on physical hardware
- [ ] Unit tests pass (`.\Tests\Run-Tests.ps1`)
- [ ] Integration tests pass
- [ ] Verification script passes (`.\Tools\Verify-Complete-Hardening.ps1`)
**Test Configuration:**
- **OS Version**: Windows 11 25H2 Build 26200
- **PowerShell Version**: 5.1.26100.xxxx
- **Test Environment**: VM / Physical
## 📋 Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published
- [ ] I have updated the CHANGELOG.md
- [ ] I have read and agree to the [Code of Conduct](../CODE_OF_CONDUCT.md)
## 🔒 Security Considerations
- [ ] This change does not introduce security vulnerabilities
- [ ] This change has been reviewed for security implications
- [ ] Sensitive data is handled properly (if applicable)
- [ ] No hardcoded credentials or secrets
**Security Impact Details:**
## 📚 Documentation
- [ ] README.md updated (if needed)
- [ ] CHANGELOG.md updated
- [ ] Docs/ folder updated (if needed)
- [ ] Code comments added/updated
## 🔄 Backwards Compatibility
- [ ] This change is backwards compatible
- [ ] This change includes migration path for existing users
- [ ] Breaking changes are documented
**Compatibility Details:**
## 🎨 Screenshots (if applicable)
Add screenshots to help explain your changes (e.g., UI changes, verification report).
## 📝 Additional Notes
Add any additional notes for reviewers here.
---
## For Maintainers
**Review Checklist:**
- [ ] Code quality meets project standards
- [ ] Tests are comprehensive
- [ ] Documentation is complete
- [ ] Security implications reviewed
- [ ] Backwards compatibility considered
- [ ] CHANGELOG updated
- [ ] Ready to merge

226
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,226 @@
name: CI - PowerShell Quality Checks
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
permissions:
contents: read
checks: write
pull-requests: write
jobs:
psscriptanalyzer:
name: PSScriptAnalyzer
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run PSScriptAnalyzer
shell: pwsh
run: |
Write-Host "Installing PSScriptAnalyzer..."
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser -SkipPublisherCheck -ErrorAction Stop
Write-Host ""
Write-Host "Running PSScriptAnalyzer (Errors only)..."
$results = Invoke-ScriptAnalyzer -Path . -Recurse -Severity Error
if ($results) {
Write-Host ""
Write-Host "=== PSScriptAnalyzer Errors Found ===" -ForegroundColor Red
$results | Format-Table -AutoSize
Write-Host ""
Write-Host "Error Count: $($results.Count)" -ForegroundColor Red
Write-Host "Failing CI due to errors" -ForegroundColor Red
exit 1
} else {
Write-Host ""
Write-Host "No errors found! (Warnings are ignored)" -ForegroundColor Green
}
test-powershell-51:
name: Test on PowerShell 5.1
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test PowerShell Scripts (5.1)
shell: powershell
run: |
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)" -ForegroundColor Cyan
Write-Host "Testing script syntax..."
$ErrorActionPreference = 'Stop'
$failed = $false
Get-ChildItem -Path . -Filter "*.ps1" -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host "Checking: $($_.Name)"
try {
$errors = $null
$tokens = [System.Management.Automation.PSParser]::Tokenize((Get-Content $_.FullName -Raw), [ref]$errors)
if ($errors.Count -gt 0) {
Write-Host " [ERROR] $($errors[0].Message)" -ForegroundColor Red
$failed = $true
} else {
Write-Host " [OK]" -ForegroundColor Green
}
} catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
$failed = $true
}
}
if ($failed) {
Write-Host ""
Write-Host "Syntax check FAILED" -ForegroundColor Red
exit 1
} else {
Write-Host ""
Write-Host "All scripts have valid syntax!" -ForegroundColor Green
}
test-powershell-7:
name: Test on PowerShell 7.4
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test PowerShell Scripts (7.4)
shell: pwsh
run: |
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)" -ForegroundColor Cyan
Write-Host "Testing script syntax..."
$ErrorActionPreference = 'Stop'
$failed = $false
Get-ChildItem -Path . -Filter "*.ps1" -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host "Checking: $($_.Name)"
try {
$errors = $null
$tokens = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$tokens, [ref]$errors)
if ($errors.Count -gt 0) {
Write-Host " [ERROR] $($errors[0].Message)" -ForegroundColor Red
$failed = $true
} else {
Write-Host " [OK]" -ForegroundColor Green
}
} catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
$failed = $true
}
}
if ($failed) {
Write-Host ""
Write-Host "Syntax check FAILED" -ForegroundColor Red
exit 1
} else {
Write-Host ""
Write-Host "All scripts have valid syntax!" -ForegroundColor Green
}
validate-structure:
name: Validate Project Structure
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check Required Files
shell: pwsh
run: |
Write-Host "Checking project structure..." -ForegroundColor Cyan
$required = @(
"README.md",
"LICENSE",
"CHANGELOG.md",
"NoIDPrivacy-Interactive.ps1",
"Core/Framework.ps1",
"Modules",
"Tools"
)
$missing = @()
foreach ($item in $required) {
if (Test-Path $item) {
Write-Host "[OK] $item" -ForegroundColor Green
} else {
Write-Host "[MISSING] $item" -ForegroundColor Red
$missing += $item
}
}
if ($missing.Count -gt 0) {
Write-Host ""
Write-Host "Missing required files/folders!" -ForegroundColor Red
exit 1
} else {
Write-Host ""
Write-Host "All required files present!" -ForegroundColor Green
}
- name: Check Module Structure
shell: pwsh
run: |
Write-Host "`nValidating module structure..." -ForegroundColor Cyan
$modules = @(
"SecurityBaseline",
"ASR",
"DNS",
"Privacy",
"AntiAI",
"EdgeHardening",
"AdvancedSecurity"
)
$failed = $false
foreach ($module in $modules) {
$modulePath = "Modules/$module"
if (Test-Path $modulePath) {
Write-Host "[OK] Module: $module" -ForegroundColor Green
# Check for required module files
$moduleFile = "$modulePath/$module.psm1"
$manifestFile = "$modulePath/$module.psd1"
if (Test-Path $moduleFile) {
Write-Host " [OK] $module.psm1" -ForegroundColor Green
} else {
Write-Host " [MISSING] $module.psm1" -ForegroundColor Red
$failed = $true
}
if (Test-Path $manifestFile) {
Write-Host " [OK] $module.psd1" -ForegroundColor Green
} else {
Write-Host " [MISSING] $module.psd1" -ForegroundColor Red
$failed = $true
}
} else {
Write-Host "[MISSING] Module: $module" -ForegroundColor Red
$failed = $true
}
}
if ($failed) {
Write-Host ""
Write-Host "Module structure validation FAILED!" -ForegroundColor Red
exit 1
} else {
Write-Host ""
Write-Host "All modules are correctly structured!" -ForegroundColor Green
}

71
.github/workflows/pester-tests.yml vendored Normal file
View file

@ -0,0 +1,71 @@
name: Pester Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
test:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Pester
shell: pwsh
run: |
Install-Module -Name Pester -Force -SkipPublisherCheck -Scope CurrentUser
Import-Module Pester
- name: Run Unit Tests
shell: pwsh
run: |
$config = New-PesterConfiguration
$config.Run.Path = "Tests/Unit"
$config.Run.PassThru = $true
$config.Output.Verbosity = 'Detailed'
$config.TestResult.Enabled = $true
$config.TestResult.OutputPath = "TestResults-Unit.xml"
$config.TestResult.OutputFormat = 'NUnitXml'
$results = Invoke-Pester -Configuration $config
if ($results.FailedCount -gt 0) {
Write-Error "Unit tests failed: $($results.FailedCount) failures"
exit 1
}
- name: Run Integration Tests (DryRun only)
shell: pwsh
run: |
$config = New-PesterConfiguration
$config.Run.Path = "Tests/Integration"
$config.Run.PassThru = $true
$config.Output.Verbosity = 'Detailed'
$config.TestResult.Enabled = $true
$config.TestResult.OutputPath = "TestResults-Integration.xml"
$config.TestResult.OutputFormat = 'NUnitXml'
$results = Invoke-Pester -Configuration $config
if ($results.FailedCount -gt 0) {
Write-Error "Integration tests failed: $($results.FailedCount) failures"
exit 1
}
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: TestResults-*.xml
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action/windows@v2
if: always()
with:
files: TestResults-*.xml

58
.gitignore vendored Normal file
View file

@ -0,0 +1,58 @@
# Logs
Logs/
*.log
# Backups
Backups/
*.reg
*.bak
# Reports
Reports/
*.html
*.csv
# Temporary files
*.tmp
*.temp
# PowerShell module cache
*.psm1.tmp
*.ps1.tmp
# Windows
Thumbs.db
Desktop.ini
$RECYCLE.BIN/
# IDE
.vscode/
.idea/
*.code-workspace
# Build artifacts
Build/Output/
Build/Release/
*.msi
*.exe
# Test results
Tests/Results/
*.trx
*.coverage
# User configuration (don't commit personal settings)
config.local.json
# Sensitive data
*.key
*.pem
*.pfx
secrets/
# Common build artifacts and IDE metadata
[Bb]in/
[Oo]bj/
TestResults/
.vs/

324
CHANGELOG.md Normal file
View file

@ -0,0 +1,324 @@
# Changelog
All notable changes to NoID Privacy will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
---
## [2.2.0] - 2025-12-08
### 🚀 Enhanced Framework - 630+ Settings
**Major update with expanded AI lockdown, improved privacy coverage, and ASR quick-toggle fix.**
---
## 🌟 Release Highlights
**630+ Settings** - Expanded from 580+ (Privacy, AntiAI, EdgeHardening, AdvSec Wireless Display)
**NonInteractive Mode** - Full GUI integration via config.json
**Third-Party AV Support** - Automatic detection, graceful ASR skip
**AntiAI Enhanced** - 32 policies (was 24), Recall Export Block, Edge Copilot disabled
**Pre-Framework ASR Snapshot** - Preserves rule state before multi-module runs
**Smart Registry Backup** - JSON fallback for protected keys
**Critical Bugfixes** - ASR Quick-Toggle, NonInteractive strict-mode, DNS offline
### ✅ Added
**NonInteractive Mode (GUI Integration)**
- Complete `config.json` support for automated execution
- All 7 modules fully configurable without prompts when values are provided in `config.json`
- Enables GUI-driven hardening in non-interactive mode (no Read-Host prompts)
**Pre-Framework ASR Snapshot**
- Captures all 19 ASR rules before multi-module runs
- Ensures original system state is preserved
- Prevents ASR rule loss during complex operations
**AntiAI Module Enhancements (24 → 32 policies)**
- Recall Export Block (prevents snapshot export)
- Advanced Copilot Blocks (URI handlers, Edge sidebar)
- Improved Edge Copilot sidebar disable (5 additional policies)
- Hardware Copilot key remapped to Notepad
- CapabilityAccessManager AI blocking
**AdvancedSecurity: Wireless Display / Miracast Hardening**
- New Wireless Display security available in all AdvancedSecurity profiles (Balanced/Enterprise/Maximum)
- Default: Block receiving projections and require PIN for incoming connections
- Optional: Complete disable (blocks sending projections, mDNS discovery, ports 7236/7250, and Wi-Fi Direct adapters)
**AdvancedSecurity: Discovery Protocols Security (Maximum profile)**
- Optional WS-Discovery + mDNS complete disable
- Blocks automatic device discovery (printers, TVs, scanners)
- Firewall rules for UDP 3702 (WS-Discovery) and UDP 5353 (mDNS)
- Prevents network mapping and mDNS spoofing attacks
**AdvancedSecurity: IPv6 Disable (Maximum profile - mitm6 mitigation)**
- Optional complete IPv6 disable (DisabledComponents = 0xFF)
- Prevents mitm6 attacks (DHCPv6 spoofing → DNS takeover → NTLM relay)
- Defense-in-depth (WPAD already disabled by framework)
- Recommended for air-gapped/standalone systems
**Privacy Module Expansion (55+ → 77 settings)**
- Cloud Clipboard toggle (user-configurable)
- Enhanced compliance verification
- Improved bloatware detection
- Better OneDrive sync compatibility
**Third-Party Antivirus Detection**
- Automatic detection of Kaspersky, Norton, Bitdefender, etc.
- ASR module gracefully skipped when 3rd-party AV active
- Clear user notification explaining why
- All other modules continue normally (613 settings)
**Smart Registry Backup System**
- JSON fallback for protected system keys
- Handles access-denied scenarios gracefully
- Empty marker files for non-existent keys
- Improved restore reliability
**Documentation**
- AV Compatibility section: "Designed for Microsoft Defender Works with Any Antivirus"
- Clear 632 vs 613 explanation for Defender vs. 3rd-party AV setups
- Improved troubleshooting guides
### 🔨 Fixed
**ASR Quick-Toggle Bug (Critical)**
- Fixed: Quick-toggling ASR rules caused 3 advanced rules to disappear
- Affected rules: Safe Mode Reboot, Copied System Tools, Webshell Creation
- Root cause: `Set-MpPreference` was called with single rule instead of full rule set
- Fix: Now reads existing rules, updates target, writes complete set back
**NonInteractive Strict-Mode Error**
- Fixed fatal error when dot-sourcing `NonInteractive.ps1` in GUI context
- Safe check for `$global:NonInteractiveMode` variable
**Registry Backup Protected Keys**
- Enhanced JSON fallback for protected system keys
- Prevents backup failures on restricted registry paths
- Creates marker files for rollback tracking
**DNS Offline Handling**
- Graceful handling when system temporarily offline during DNS test
- Configuration proceeds and activates when connection restored
**Module Progress Feedback**
- Improved status messages during long operations
- No more "stuck at 95%" feeling
### 📊 What Changed
| Component | v2.1.0 | v2.2.0 |
|-----------|--------|--------|
| Total Settings | 580+ | **632** |
| AntiAI Policies | 24 | **32** |
| Privacy Settings | 55+ | **77** |
| NonInteractive Mode | ❌ | ✅ |
| 3rd-Party AV Detection | ❌ | ✅ |
| Pre-Framework ASR Snapshot | ❌ | ✅ |
| Smart Registry Backup | Basic | **JSON Fallback** |
---
## [2.1.0] - 2025-11-23
### 🎉 Production Release - Complete Windows 11 Security Framework
**The first complete, production-ready release of NoID Privacy v2.x - 580+ settings, 7 modules, full BAVR pattern implementation.**
---
## 🌟 Release Highlights
**All 7 Modules Production-Ready** - Complete framework with 580+ security settings
**Zero-Day Protection** - CVE-2025-9491 mitigation (SRP .lnk protection)
**100% BAVR Coverage** - Every setting can be backed up, applied, verified, and restored
**Professional Code Quality** - All lint warnings resolved, comprehensive error handling
**Zero Tracking** - No cookies, no analytics, no telemetry (we practice what we preach)
### ✅ Added - Complete Framework
#### All 7 Security Modules
**SecurityBaseline** (425 settings) - Microsoft Security Baseline for Windows 11 25H2
- 335 Registry policies (Computer + User Configuration)
- 67 Security Template settings (Password Policy, Account Lockout, User Rights, Security Options)
- 23 Advanced Audit policies (Complete security event logging)
- Credential Guard, BitLocker policies, VBS & HVCI
- No LGPO.exe dependency (100% native PowerShell)
**ASR** (19 rules) - Attack Surface Reduction
- 18 rules in Block mode, 1 configurable (PSExec/WMI)
- Blocks ransomware, macros, exploits, credential theft
- Office/Adobe/Email protection
- ConfigMgr detection for compatibility
**DNS** (5 checks) - Secure DNS with DoH encryption
- 3 providers: Cloudflare (default), Quad9, AdGuard
- REQUIRE mode (no unencrypted fallback) or ALLOW mode (VPN-friendly)
- IPv4 + IPv6 dual-stack support
- DNSSEC validation
**Privacy** (55+ settings) - Telemetry & Privacy Hardening
- 3 operating modes: MSRecommended (default), Strict, Paranoid
- Telemetry minimized to Security-Essential level
- Bloatware removal with auto-restore via winget (policy-based on 25H2+ Ent/Edu)
- OneDrive telemetry off (sync functional)
- App permissions default-deny
**AntiAI** (24 policies) - AI Lockdown
- Generative AI Master Switch (blocks ALL AI models system-wide)
- Windows Recall (complete deactivation + component protection)
- Windows Copilot (system-wide disabled + hardware key remapped)
- Click to Do, Paint AI, Notepad AI, Settings Agent - all disabled
**EdgeHardening** (20 policies) - Microsoft Edge Security Baseline
- SmartScreen enforced, Tracking Prevention strict
- SSL/TLS hardening, Extension security
- IE Mode restrictions
- Native PowerShell implementation (no LGPO.exe)
**AdvancedSecurity** (44 settings) - Beyond Microsoft Baseline
- **SRP .lnk Protection (CVE-2025-9491)** - Zero-day mitigation for ClickFix malware
- **RDP Hardening** - Disabled by default, TLS + NLA enforced
- **Legacy Protocol Blocking** - SMBv1, NetBIOS, LLMNR, WPAD, PowerShell v2
- **TLS Hardening** - 1.0/1.1 OFF, 1.2/1.3 ON
- **Windows Update** - 3 GUI-equivalent settings (interactive configuration)
- **Finger Protocol** - Blocked (ClickFix malware protection)
#### Core Features
**Complete BAVR Pattern (Backup-Apply-Verify-Restore)**
- All 580+ settings now fully verified in `Verify-Complete-Hardening.ps1`
- EdgeHardening: 20 verification checks added
- AdvancedSecurity: 42 verification checks added
- 100% coverage achieved (was 89.4%)
**Bloatware Removal & Restore**
- `REMOVED_APPS_LIST.txt` created in backup folder with reinstall instructions
- `REMOVED_APPS_WINGET.json` metadata enables automatic reinstallation via `winget`
- Session restore attempts auto-restore first, falls back to manual Microsoft Store reinstall
- Policy-based removal for Windows 11 25H2+ Ent/Edu editions
**Documentation & Repository**
- **FEATURES.md** - Complete settings reference
- **SECURITY-ANALYSIS.md** - Home user impact analysis
- **README.md** - Professional restructure with improved visual hierarchy
- **CHANGELOG.md** - Comprehensive release history
- **.gitignore** - Clean repository (ignores Logs/, Backups/, Reports/)
---
### 🔨 Fixed - Critical Bugfixes
**DNS Module Crash (CRITICAL)**
- Fixed `System.Object[]` to `System.Int32` type conversion error in `Get-PhysicalAdapters`
- Removed unary comma operator causing DNS configuration failure
- Prevents complete DNS module failure on certain network configurations
**Bloatware Count Accuracy**
- Corrected misleading console output showing "2 apps removed" instead of actual count
- Fixed pipeline contamination from `Register-Backup` output in `Remove-Bloatware.ps1`
- Now shows accurate count (e.g., "14 apps removed")
**Restore Logging System**
- Implemented dedicated `RESTORE_Session_XXXXXX_timestamp.log` file
- Captures all restore activities from A-Z with detailed logging
- Fixed empty `Message` parameter validation errors in `Write-RestoreLog`
**User Selection Logs**
- Moved user selection messages from INFO to DEBUG (cleaner console output)
- Affects: Privacy mode selection, DNS provider selection, ASR mode selection
- Console now shows only critical information, detailed logs in log file
**Code Quality & Linting**
- Removed all unused variables (`$isAdmin` in `Invoke-AdvancedSecurity.ps1`)
- Fixed PSScriptAnalyzer warnings across entire project
- Resolved double backslash escaping in documentation paths
**Terminal Services GPO Cleanup**
- Enhanced GPO cleanup with explicit value removal
- Improved restore consistency for Terminal Services registry keys
- Cosmetic variance only (no functional impact)
**Temporary File Leaks**
- SecurityBaseline: Added `finally` blocks to prevent temp file pollution
- Ensures cleanup of `secedit.exe` temp files even on errors
- Prevents TEMP folder accumulation
---
### 📊 What Changed
**Framework Completion**
- Status: **7/7 modules (100%)** - All production-ready
- Total Settings: **580+** (was 521)
- BAVR Coverage: **100%** (was 89.4%)
- Verification: **EdgeHardening** (20 checks) + **AdvancedSecurity** (44 checks) added
**Module Structure**
- All 7 modules now use consistent `/Config/` folder structure
- ASR: `Data/``Config/`
- EdgeHardening: `ParsedSettings/``Config/`
**Documentation Improvements**
- README: Professional restructure, improved navigation
- Added "Why NoID Privacy?" section (Security ↔ Privacy connection)
- Added "Our Privacy Promise" section (Zero tracking)
- Fixed all inconsistent list formatting (trailing spaces → proper bullets)
**Restore System**
- Production tested with full apply-restore cycle verification
- Restores to clean baseline state
- AdvancedSecurity: 100% perfect restoration
---
### ⚠️ Breaking Changes
**License Change**
- **MIT (v1.x) → GPL v3.0 (v2.x+)**
- Reason: Complete rewrite from scratch (100% new codebase)
- Impact: Derivatives must comply with GPL v3.0 copyleft requirements
- Note: v1.8.x releases remain under MIT license (unchanged)
- **Dual-Licensing:** Commercial licenses available for closed-source use
---
### 📈 Before/After Comparison
**Before v2.1.0:**
```
Modules: 5/7 (71%)
Settings: 521
BAVR Coverage: 89.4%
Restore Accuracy: Unknown
Code Quality: Lint warnings present
Temp File Cleanup: Partial
```
**After v2.1.0:**
```
Modules: 7/7 (100%)
Settings: 580+
BAVR Coverage: 100%
Restore: Verified (full cycle)
Code Quality: PSScriptAnalyzer clean
Temp File Cleanup: Complete
```
---
## 📚 Additional Resources
- **Full Documentation:** See [README.md](README.md) and [FEATURES.md](Docs/FEATURES.md)
- **Security Analysis:** See [SECURITY-ANALYSIS.md](Docs/SECURITY-ANALYSIS.md)
- **Bug Reports:** [GitHub Issues](https://github.com/NexusOne23/noid-privacy/issues)
- **Discussions:** [GitHub Discussions](https://github.com/NexusOne23/noid-privacy/discussions)
---
**Made with 🛡️ for the Windows Security Community**

135
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,135 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement via:
- **GitHub Discussions**: https://github.com/NexusOne23/noid-privacy/discussions
- **GitHub Issues**: For code-related conduct issues only
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

783
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,783 @@
# 🛠️ Contributor Guide - Building New Modules
**How to build production-quality hardening modules for NoID Privacy**
This guide shows you how to create a new module using the **AdvancedSecurity** module as the reference implementation. Every principle, pattern, and structure you see here is based on real, production-tested code.
---
## 📋 Table of Contents
1. [Module Architecture](#module-architecture)
2. [File Structure](#file-structure)
3. [Core Integration Points](#core-integration-points)
4. [Implementation Checklist](#implementation-checklist)
5. [Best Practices](#best-practices)
6. [Testing & Verification](#testing--verification)
---
## 🏗️ Module Architecture
### Design Principles
All modules in NoID Privacy follow these principles:
1. **Separation of Concerns** - Public vs Private functions
2. **Backup Before Modify** - Always backup before changes
3. **Comprehensive Testing** - Test functions for compliance
4. **Structured Logging** - Use Write-Log for everything
5. **Error Handling** - Try/Catch everywhere
6. **PowerShell Best Practices** - Modern CIM, explicit returns, validated parameters
### Module Types
| Type | Example | Auto-Enabled | Use Case |
|------|---------|--------------|----------|
| **Core Hardening** | SecurityBaseline, ASR | ✅ Yes | Standard security settings, always safe |
| **Service-Based** | DNS, Privacy | ⚠️ Optional | User choice (provider, mode) |
| **Advanced/Aggressive** | AdvancedSecurity | ❌ Opt-in only | Breaking changes, requires testing |
---
## 📁 File Structure
### Required Structure
```
Modules/
└── YourModule/
├── YourModule.psd1 # Module Manifest (REQUIRED)
├── YourModule.psm1 # Module Loader (REQUIRED)
├── Config/ # Configuration files
│ ├── Feature1.json # Feature-specific config
│ └── Feature2.json
├── Private/ # Internal functions (not exported)
│ ├── Set-Feature1.ps1 # Implementation functions
│ ├── Set-Feature2.ps1
│ ├── Test-Feature1.ps1 # Compliance test functions
│ ├── Test-Feature2.ps1
│ ├── Backup-YourModuleSettings.ps1 # Comprehensive backup
│ └── Restore-YourModuleSettings.ps1 # Comprehensive restore
└── Public/ # Exported functions (user-facing)
├── Invoke-YourModule.ps1 # Main entry point
└── Test-YourModule.ps1 # Compliance test entry point
```
### Example: AdvancedSecurity Module Structure
```
Modules/AdvancedSecurity/
├── AdvancedSecurity.psd1 # Manifest with version 2.2.0
├── AdvancedSecurity.psm1 # Loads Private/*.ps1 and Public/*.ps1
├── Config/
│ ├── RDP.json # RDP hardening config
│ ├── Credentials.json # WDigest config
│ └── AdminShares.json # Admin shares config
├── Private/
│ ├── Enable-RdpNLA.ps1 # RDP hardening implementation
│ ├── Set-WDigestProtection.ps1 # WDigest implementation
│ ├── Disable-AdminShares.ps1 # Admin shares implementation
│ ├── Disable-RiskyPorts.ps1 # Firewall ports
│ ├── Stop-RiskyServices.ps1 # Services management
│ ├── Disable-WPAD.ps1 # WPAD disable
│ ├── Disable-LegacyTLS.ps1 # TLS 1.0/1.1 disable
│ ├── Remove-PowerShellV2.ps1 # PSv2 removal
│ ├── Test-RdpSecurity.ps1 # RDP compliance test
│ ├── Test-WDigest.ps1 # WDigest compliance test
│ ├── Test-AdminShares.ps1 # Admin shares test
│ ├── Test-RiskyPorts.ps1 # Ports compliance test
│ ├── Test-RiskyServices.ps1 # Services compliance test
│ ├── Backup-AdvancedSecuritySettings.ps1 # Full backup
│ └── Restore-AdvancedSecuritySettings.ps1 # Full restore
└── Public/
├── Invoke-AdvancedSecurity.ps1 # Main function with profiles
└── Test-AdvancedSecurity.ps1 # Compliance aggregator
```
---
## 🔧 Core Integration Points
### 1. Module Manifest (.psd1)
**Template:**
```powershell
@{
RootModule = 'YourModule.psm1'
ModuleVersion = '2.2.0'
GUID = 'YOUR-GUID-HERE' # Generate with [guid]::NewGuid()
Author = 'Your Name'
CompanyName = 'NoID Privacy'
Copyright = '(c) 2025. All rights reserved.'
Description = 'Brief description of what your module does'
PowerShellVersion = '5.1'
FunctionsToExport = @(
'Invoke-YourModule',
'Test-YourModule'
)
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
PrivateData = @{
PSData = @{
Tags = @('Security', 'Hardening', 'Windows11')
ProjectUri = 'https://github.com/yourusername/noid-privacy'
ReleaseNotes = @"
v2.2.0 - Initial Release
- Feature 1
- Feature 2
"@
}
}
}
```
**Real Example (AdvancedSecurity.psd1):**
```powershell
@{
RootModule = 'AdvancedSecurity.psm1'
ModuleVersion = '2.2.0'
GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
Author = 'NexusOne23'
Description = 'Advanced Security hardening beyond Microsoft Security Baseline'
FunctionsToExport = @(
'Invoke-AdvancedSecurity',
'Test-AdvancedSecurity'
)
PrivateData = @{
PSData = @{
Tags = @('Security', 'Hardening', 'RDP', 'TLS', 'Windows11')
ReleaseNotes = @"
v2.2.0 - Production Release
- RDP NLA enforcement + optional complete disable
- WDigest credential protection
- Administrative shares disable (domain-aware)
- Risky firewall ports closure (LLMNR, NetBIOS, UPnP/SSDP)
- Risky network services stop
- Legacy TLS 1.0/1.1 disable
- WPAD auto-discovery disable
- PowerShell v2 removal
- Profile system (Balanced/Enterprise/Maximum)
- Comprehensive backup/restore
"@
}
}
}
```
---
### 2. Module Loader (.psm1)
**Template:**
```powershell
<#
.SYNOPSIS
Module loader for YourModule
#>
# Get module root path
$ModuleRoot = $PSScriptRoot
# Import Private functions (not exported)
$PrivateFunctions = Get-ChildItem -Path "$ModuleRoot\Private\*.ps1" -ErrorAction SilentlyContinue
foreach ($function in $PrivateFunctions) {
try {
. $function.FullName
Write-Verbose "Imported private function: $($function.BaseName)"
}
catch {
Write-Error "Failed to import private function $($function.FullName): $_"
}
}
# Import Public functions (will be exported)
$PublicFunctions = Get-ChildItem -Path "$ModuleRoot\Public\*.ps1" -ErrorAction SilentlyContinue
foreach ($function in $PublicFunctions) {
try {
. $function.FullName
Write-Verbose "Imported public function: $($function.BaseName)"
}
catch {
Write-Error "Failed to import public function $($function.FullName): $_"
}
}
# Export only Public functions
$PublicFunctionNames = $PublicFunctions | ForEach-Object { $_.BaseName }
Export-ModuleMember -Function $PublicFunctionNames
Write-Verbose "YourModule loaded successfully. Exported functions: $($PublicFunctionNames -join ', ')"
```
---
### 3. Logging Integration
**Always use Write-Log from Core/Logger.ps1:**
```powershell
# Import at module level if not using framework
. "$PSScriptRoot\..\..\Core\Logger.ps1"
# In your functions
Write-Log -Level INFO -Message "Starting feature configuration..." -Module "YourModule"
Write-Log -Level SUCCESS -Message "Feature configured successfully" -Module "YourModule"
Write-Log -Level WARNING -Message "Non-critical issue detected" -Module "YourModule"
Write-Log -Level ERROR -Message "Failed to apply setting" -Module "YourModule" -Exception $_.Exception
Write-Log -Level DEBUG -Message "Registry key set: $regPath" -Module "YourModule"
```
**Log Levels:**
- `INFO` - General progress
- `SUCCESS` - Operation completed
- `WARNING` - Non-critical issues
- `ERROR` - Failures
- `DEBUG` - Detailed diagnostic info
---
### 4. Backup Integration
**Use Core/Rollback.ps1 functions:**
```powershell
# Backup registry key
Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\..." -BackupName "YourFeature"
# Backup service
Backup-ServiceConfiguration -ServiceName "YourService"
# Register custom backup data
$backupData = @{
YourData = $someValue
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
} | ConvertTo-Json
Register-Backup -Type "YourFeature_Settings" -Data $backupData -Name "YourFeatureName"
```
**Real Example (from AdvancedSecurity):**
```powershell
function Backup-AdvancedSecuritySettings {
try {
# Start backup session
$backupSession = Start-ModuleBackup -ModuleName "AdvancedSecurity"
$backupCount = 0
# 1. RDP Settings
$rdpBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -BackupName "RDP_Settings"
if ($rdpBackup) { $backupCount++ }
# 2. Services
$services = @("SSDPSRV", "upnphost", "lmhosts")
foreach ($svc in $services) {
$svcBackup = Backup-ServiceConfiguration -ServiceName $svc
if ($svcBackup) { $backupCount++ }
}
# 3. Custom data (firewall rules snapshot)
$firewallRules = Get-NetFirewallRule | Where-Object { ... }
$firewallData = @{
Rules = $firewallRules
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
} | ConvertTo-Json -Depth 10
$firewallBackup = Register-Backup -Type "Firewall_Rules" -Data $firewallData -Name "RiskyPorts_Firewall"
if ($firewallBackup) { $backupCount++ }
return $backupCount
}
catch {
Write-Log -Level ERROR -Message "Backup failed: $_" -Module "YourModule"
return $false
}
}
```
---
## ✅ Implementation Checklist
### Phase 1: Planning & Structure
- [ ] Define module name and purpose
- [ ] Choose module type (Core/Service/Advanced)
- [ ] Plan features and settings
- [ ] Create folder structure
- [ ] Generate GUID for manifest
- [ ] Write module manifest (.psd1)
- [ ] Write module loader (.psm1)
### Phase 2: Configuration
- [ ] Create Config/*.json files for each feature
- [ ] Document settings with rationale
- [ ] Include impact assessment
- [ ] Add Microsoft documentation links
- [ ] Define default values
### Phase 3: Implementation Functions (Private/)
For each feature:
- [ ] Create Set-FeatureName.ps1
- [ ] Add comprehensive help block
- [ ] Implement backup integration
- [ ] Add registry/service/file modifications
- [ ] Include error handling (try/catch)
- [ ] Add logging at every step
- [ ] Return $true on success, $false on failure
- [ ] Create Test-FeatureName.ps1
- [ ] Check compliance
- [ ] Return PSCustomObject with status
- [ ] Include Details array for human-readable output
### Phase 4: Aggregation Functions (Private/)
- [ ] Create Backup-YourModuleSettings.ps1
- [ ] Backup all features
- [ ] Use Start-ModuleBackup for session tracking
- [ ] Return backup count
- [ ] Create Restore-YourModuleSettings.ps1
- [ ] Restore from backup directory
- [ ] Handle missing backups gracefully
- [ ] Log all restore operations
### Phase 5: Public Interface (Public/)
- [ ] Create Invoke-YourModule.ps1
- [ ] Add [CmdletBinding(SupportsShouldProcess=$true)]
- [ ] Define parameters (profiles, modes, switches)
- [ ] Check for admin rights
- [ ] Initialize backup system
- [ ] Call Backup-YourModuleSettings (unless -SkipBackup)
- [ ] Call all Set-Feature functions
- [ ] Track applied/failed features
- [ ] Return structured PSCustomObject
- [ ] Provide user-friendly console output
- [ ] Create Test-YourModule.ps1
- [ ] Call all Test-Feature functions
- [ ] Aggregate results
- [ ] Calculate compliance percentage
- [ ] Return array of compliance objects
### Phase 6: Integration
- [ ] Update config.json with module entry
- [ ] Set appropriate status (IMPLEMENTED/PLANNED)
- [ ] Set enabled (true for auto, false for opt-in)
- [ ] Add priority number
- [ ] Include description
- [ ] Update README.md
- [ ] Add module to appropriate table
- [ ] Document features
- [ ] Provide usage examples
- [ ] Explain opt-in if applicable
- [ ] Update Verify-Complete-Hardening.ps1 (if auto-enabled)
### Phase 7: Testing
- [ ] Test on clean Windows 11 VM
- [ ] Verify backup creation
- [ ] Verify settings application
- [ ] Test compliance checks
- [ ] Verify restore functionality
- [ ] Test error scenarios
- [ ] Test -WhatIf mode
- [ ] Document any issues
---
## 📚 Best Practices
### 1. Use Modern PowerShell
**✅ DO:**
```powershell
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
$adapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE"
```
**❌ DON'T:**
```powershell
$computerSystem = Get-WmiObject Win32_ComputerSystem # Deprecated
```
---
### 2. Explicit Error Handling
**✅ DO:**
```powershell
try {
Set-ItemProperty -Path $regPath -Name $valueName -Value $value -Type DWord -Force -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Registry value set successfully" -Module "YourModule"
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to set registry value: $_" -Module "YourModule" -Exception $_.Exception
return $false
}
```
**❌ DON'T:**
```powershell
Set-ItemProperty -Path $regPath -Name $valueName -Value $value # No error handling!
```
---
### 3. Structured Returns
**✅ DO (for action functions):**
```powershell
function Set-YourFeature {
try {
# ... implementation ...
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed: $_" -Module "YourModule"
return $false
}
}
```
**✅ DO (for test functions):**
```powershell
function Test-YourFeature {
try {
$result = [PSCustomObject]@{
Feature = "Your Feature Name"
Status = "Secure" # or "Insecure" or "Partially Secure"
Details = @()
Compliant = $true # or $false
}
# ... check settings ...
if ($settingCorrect) {
$result.Details += "Setting is correct"
} else {
$result.Status = "Insecure"
$result.Compliant = $false
$result.Details += "Setting is incorrect!"
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Test failed: $_" -Module "YourModule"
return [PSCustomObject]@{
Feature = "Your Feature Name"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}
```
**✅ DO (for main public function with scripting support):**
```powershell
function Invoke-YourModule {
try {
# ... apply all features ...
# Return structured object
return [PSCustomObject]@{
Success = $true
FeaturesApplied = $appliedFeatures
FeaturesFailed = $failedFeatures
TotalFeatures = $appliedFeatures.Count + $failedFeatures.Count
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
}
catch {
return [PSCustomObject]@{
Success = $false
ErrorMessage = $_.Exception.Message
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
}
}
```
---
### 4. Validated Parameters
**✅ DO:**
```powershell
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateSet('Mode1', 'Mode2', 'Mode3')]
[string]$Mode = 'Mode1',
[Parameter(Mandatory = $false)]
[switch]$Force
)
```
---
### 5. Comprehensive Help
**✅ DO:**
```powershell
function Set-YourFeature {
<#
.SYNOPSIS
Brief one-line description
.DESCRIPTION
Detailed description of what the function does.
Why this feature is important:
- Security benefit 1
- Security benefit 2
Potential impact:
- Impact consideration 1
- Impact consideration 2
.PARAMETER ParameterName
Description of parameter
.EXAMPLE
Set-YourFeature
Basic usage
.EXAMPLE
Set-YourFeature -Force
Force mode example
.NOTES
Additional important information
References: Microsoft KB article, CVE, etc.
#>
[CmdletBinding()]
param(...)
# Implementation
}
```
---
### 6. WhatIf Support for Destructive Operations
**✅ DO:**
```powershell
function Invoke-YourModule {
[CmdletBinding(SupportsShouldProcess=$true)]
param(...)
if ($PSCmdlet.ShouldProcess("YourModule", "Apply hardening")) {
# Apply changes
}
else {
Write-Host "WhatIf mode - no changes applied" -ForegroundColor Yellow
return $true
}
}
```
**Usage:**
```powershell
Invoke-YourModule -WhatIf # Dry run
```
---
## 🧪 Testing & Verification
### Pester Test Runner (Framework-Wide)
```powershell
# 1) Test-Umgebung vorbereiten (einmalig)
.\Tests\Setup-TestEnvironment.ps1
# 2) Alle Tests (Unit + Integration + Validation) ausführen
.\Tests\Run-Tests.ps1
# 3) Nur bestimmte Testtypen ausführen
.\Tests\Run-Tests.ps1 -TestType Unit
.\Tests\Run-Tests.ps1 -TestType Integration
.\Tests\Run-Tests.ps1 -TestType Validation
# 4) Mit Code Coverage
.\Tests\Run-Tests.ps1 -TestType All -CodeCoverage
# Alternative: Einfacher Runner für alle Tests
.\Tests\Run-AllTests.ps1
### Manual Testing Checklist
1. **Clean Windows 11 VM**
```powershell
# Check Windows version
winver # Should be 24H2 or 25H2
# Check PowerShell version
$PSVersionTable.PSVersion # Should be 5.1+
```
2. **Import Module**
```powershell
Import-Module .\Modules\YourModule\YourModule.psd1 -Force -Verbose
```
3. **Test Compliance (Before)**
```powershell
$before = Test-YourModule
$before | Format-Table
```
4. **Apply Hardening**
```powershell
$result = Invoke-YourModule
$result
```
5. **Test Compliance (After)**
```powershell
$after = Test-YourModule
$after | Format-Table
# Check improvement
$beforeCompliant = ($before | Where-Object { $_.Compliant }).Count
$afterCompliant = ($after | Where-Object { $_.Compliant }).Count
Write-Host "Before: $beforeCompliant compliant"
Write-Host "After: $afterCompliant compliant"
```
6. **Test Restore**
```powershell
Restore-YourModuleSettings
$restored = Test-YourModule
$restored | Format-Table
```
7. **Test WhatIf**
```powershell
Invoke-YourModule -WhatIf
```
### Automated Testing Template
```powershell
# Test-YourModule-Integration.ps1
Describe "YourModule Integration Tests" {
BeforeAll {
Import-Module ".\Modules\YourModule\YourModule.psd1" -Force
}
Context "Module Loading" {
It "Should export public functions" {
$commands = Get-Command -Module YourModule
$commands.Count | Should -Be 2
$commands.Name | Should -Contain 'Invoke-YourModule'
$commands.Name | Should -Contain 'Test-YourModule'
}
}
Context "Compliance Testing" {
It "Should return compliance results" {
$results = Test-YourModule
$results | Should -Not -BeNullOrEmpty
$results[0].PSObject.Properties.Name | Should -Contain 'Feature'
$results[0].PSObject.Properties.Name | Should -Contain 'Status'
$results[0].PSObject.Properties.Name | Should -Contain 'Compliant'
}
}
Context "Application" {
It "Should apply hardening successfully" {
$result = Invoke-YourModule
$result.Success | Should -Be $true
$result.FeaturesApplied.Count | Should -BeGreaterThan 0
}
}
}
```
---
## 🎯 Real-World Example: AdvancedSecurity Module
The **AdvancedSecurity** module is the gold standard reference implementation. Study these files:
### Key Files to Study
1. **Manifest & Loader**
- `AdvancedSecurity.psd1` - Version, exports, metadata
- `AdvancedSecurity.psm1` - Function loading pattern
2. **Feature Implementation**
- `Private/Enable-RdpNLA.ps1` - Registry modification with backup
- `Private/Disable-AdminShares.ps1` - Domain-aware safety checks
- `Private/Stop-RiskyServices.ps1` - Service management with dependencies
3. **Testing**
- `Private/Test-RdpSecurity.ps1` - Compliance check pattern
- `Public/Test-AdvancedSecurity.ps1` - Test aggregation
4. **Public Interface**
- `Public/Invoke-AdvancedSecurity.ps1` - Profile system, backup, structured returns
5. **Backup/Restore**
- `Private/Backup-AdvancedSecuritySettings.ps1` - Comprehensive backup
- `Private/Restore-AdvancedSecuritySettings.ps1` - Full restore logic
---
## 📝 Summary
### Key Takeaways
1. **Structure Matters** - Follow the Private/Public separation
2. **Always Backup** - Before ANY modification
3. **Log Everything** - Use Write-Log consistently
4. **Error Handling** - Try/Catch everywhere
5. **Explicit Returns** - $true/$false for actions, PSCustomObject for tests
6. **Modern PowerShell** - CIM instead of WMI
7. **User-Friendly** - Clear console output + structured data for scripts
8. **Test Thoroughly** - Clean VM, before/after, restore
### Quick Start for New Module
```powershell
# 1. Create structure
mkdir "Modules\YourModule\Private"
mkdir "Modules\YourModule\Public"
mkdir "Modules\YourModule\Config"
# 2. Generate GUID
[guid]::NewGuid()
# 3. Create manifest (use template above)
# 4. Create loader (use template above)
# 5. Implement Private functions (Set-*, Test-*, Backup-*, Restore-*)
# 6. Implement Public functions (Invoke-*, Test-*)
# 7. Update config.json
# 8. Update README.md
# 9. Test on clean VM
```
---
**Questions? Study AdvancedSecurity v2.2.0 - it's the reference implementation!** 🎯

466
Core/Config.ps1 Normal file
View file

@ -0,0 +1,466 @@
<#
.SYNOPSIS
Configuration management for NoID Privacy Framework
.DESCRIPTION
Handles loading, saving, and validating configuration settings
from JSON configuration files.
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: PowerShell 5.1+
#>
# Global configuration object
$script:Config = $null
function Initialize-Config {
<#
.SYNOPSIS
Initialize configuration system
.PARAMETER ConfigPath
Path to configuration file (JSON)
.PARAMETER CreateDefault
Create default configuration if file doesn't exist
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$ConfigPath = (Join-Path $PSScriptRoot "..\config.json"),
[Parameter(Mandatory = $false)]
[bool]$CreateDefault = $true
)
# Check if config file exists
if (-not (Test-Path -Path $ConfigPath)) {
if ($CreateDefault) {
Write-Log -Level INFO -Message "Configuration file not found, creating default" -Module "Config"
New-DefaultConfig -Path $ConfigPath
}
else {
throw "Configuration file not found: $ConfigPath"
}
}
# Load configuration
try {
$configContent = Get-Content -Path $ConfigPath -Raw -Encoding UTF8
$script:Config = $configContent | ConvertFrom-Json
Write-Log -Level INFO -Message "Configuration loaded successfully" -Module "Config"
}
catch {
Write-Log -Level ERROR -Message "Failed to load configuration" -Module "Config" -Exception $_.Exception
throw
}
# Validate configuration
if (-not (Test-ConfigValid)) {
throw "Configuration validation failed"
}
}
function New-DefaultConfig {
<#
.SYNOPSIS
Create default configuration file
.PARAMETER Path
Path where configuration file should be created
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
$defaultConfig = @{
version = "2.2.0"
modules = @{
SecurityBaseline = @{
enabled = $true
priority = 1
status = "IMPLEMENTED"
}
ASR = @{
enabled = $true
priority = 2
status = "IMPLEMENTED"
}
DNS = @{
enabled = $true
priority = 3
provider = ""
status = "IMPLEMENTED"
}
Privacy = @{
enabled = $true
priority = 4
mode = ""
status = "IMPLEMENTED"
}
AntiAI = @{
enabled = $true
priority = 5
status = "IMPLEMENTED"
description = "Disable all Windows 11 AI features (Recall, Copilot, Paint AI, etc.)"
}
EdgeHardening = @{
enabled = $true
priority = 6
status = "IMPLEMENTED"
description = "Microsoft Edge v139 Security Baseline: 20 security policies including SmartScreen enforcement, site isolation, SSL/TLS hardening, extension blocklist, IE Mode restrictions, and Spectre mitigations. No LGPO.exe dependency."
version = "2.2.0"
baseline = "Edge v139"
policies = 20
features = @{
smartscreen_enforcement = $true
site_isolation = $true
ssl_error_blocking = $true
extension_blocklist = $true
ie_mode_restrictions = $true
spectre_mitigations = $true
application_encryption = $true
auth_scheme_restrictions = $true
}
}
AdvancedSecurity = @{
enabled = $true
priority = 7
status = "IMPLEMENTED"
description = "Advanced Security hardening beyond MS Baseline: RDP NLA/Disable, WDigest protection, Admin Shares disable, Risky ports/services, Legacy TLS disable, WPAD disable, PowerShell v2 removal, SRP .lnk protection, Windows Update (3 GUI settings), Finger Protocol block. Opt-in by design (use -SecurityProfile Balanced/Enterprise/Maximum)"
version = "2.2.0"
policies = 36
features = @{
rdp_hardening = $true
wdigest_protection = $true
admin_shares_disable = $true
risky_ports_closure = $true
risky_services_stop = $true
legacy_tls_disable = $true
wpad_disable = $true
powershell_v2_removal = $true
srp_lnk_protection = $true
windows_update_config = $true
finger_protocol_block = $true
}
profiles = @("Balanced", "Enterprise", "Maximum")
}
}
options = @{
dryRun = $false
createBackup = $true
verboseLogging = $true
autoReboot = $false
}
}
try {
$defaultConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $Path -Encoding UTF8 | Out-Null
Write-Log -Level SUCCESS -Message "Default configuration created: $Path" -Module "Config"
}
catch {
Write-Log -Level ERROR -Message "Failed to create default configuration" -Module "Config" -Exception $_.Exception
throw
}
}
function Test-ConfigValid {
<#
.SYNOPSIS
Validate configuration structure and values
.OUTPUTS
Boolean indicating if configuration is valid
#>
[CmdletBinding()]
[OutputType([bool])]
param()
if ($null -eq $script:Config) {
Write-Log -Level ERROR -Message "Configuration is null" -Module "Config"
return $false
}
# Check required properties
$requiredProps = @('version', 'modules', 'options')
foreach ($prop in $requiredProps) {
if (-not (Get-Member -InputObject $script:Config -Name $prop -MemberType NoteProperty)) {
Write-Log -Level ERROR -Message "Missing required property: $prop" -Module "Config"
return $false
}
}
# Validate modules
if ($null -eq $script:Config.modules) {
Write-Log -Level ERROR -Message "Modules configuration is missing" -Module "Config"
return $false
}
# Validate each module configuration
foreach ($prop in $script:Config.modules.PSObject.Properties) {
$moduleName = $prop.Name
$moduleConfig = $prop.Value
# Check required module properties
$requiredModuleProps = @('enabled', 'priority')
foreach ($moduleProp in $requiredModuleProps) {
if (-not (Get-Member -InputObject $moduleConfig -Name $moduleProp -MemberType NoteProperty)) {
Write-Log -Level ERROR -Message "Module '$moduleName' missing required property: $moduleProp" -Module "Config"
return $false
}
}
# Validate property types
if ($moduleConfig.enabled -isnot [bool]) {
Write-Log -Level ERROR -Message "Module '$moduleName' property 'enabled' must be boolean" -Module "Config"
return $false
}
if ($moduleConfig.priority -isnot [int] -and $moduleConfig.priority -isnot [long]) {
Write-Log -Level ERROR -Message "Module '$moduleName' property 'priority' must be integer" -Module "Config"
return $false
}
# Module-specific validation
if ($moduleName -eq "DNS") {
# Validate DNS provider if specified
if (Get-Member -InputObject $moduleConfig -Name 'provider' -MemberType NoteProperty) {
$validProviders = @('Cloudflare', 'Quad9', 'AdGuard', '', 'KEEP')
# Empty string means interactive selection
if ($moduleConfig.provider -eq '') {
Write-Log -Level DEBUG -Message "DNS provider not specified - will prompt user for selection" -Module "Config"
}
elseif ($moduleConfig.provider -eq 'KEEP') {
Write-Log -Level DEBUG -Message "DNS provider set to KEEP - will detect and preserve current provider" -Module "Config"
}
elseif ($validProviders -notcontains $moduleConfig.provider) {
Write-Log -Level ERROR -Message "DNS module has invalid provider: '$($moduleConfig.provider)'. Valid providers: $($validProviders -join ', ')" -Module "Config"
return $false
}
else {
Write-Log -Level DEBUG -Message "DNS provider validated: $($moduleConfig.provider)" -Module "Config"
}
}
else {
# Provider property missing - will prompt for selection
Write-Log -Level DEBUG -Message "DNS provider not specified - will prompt user for selection" -Module "Config"
}
}
if ($moduleName -eq "Privacy") {
# Validate Privacy mode if specified
if (Get-Member -InputObject $moduleConfig -Name 'mode' -MemberType NoteProperty) {
$validModes = @('MSRecommended', 'Strict', 'Paranoid', '')
# Empty string means interactive selection
if ($moduleConfig.mode -eq '') {
Write-Log -Level DEBUG -Message "Privacy mode not specified - will prompt user for selection" -Module "Config"
}
elseif ($validModes -notcontains $moduleConfig.mode) {
Write-Log -Level ERROR -Message "Privacy module has invalid mode: '$($moduleConfig.mode)'. Valid modes: $($validModes -join ', ')" -Module "Config"
return $false
}
else {
Write-Log -Level DEBUG -Message "Privacy mode validated: $($moduleConfig.mode)" -Module "Config"
}
}
else {
# Mode property missing - will prompt for selection
Write-Log -Level DEBUG -Message "Privacy mode not specified - will prompt user for selection" -Module "Config"
}
}
}
# Validate options
if ($null -eq $script:Config.options) {
Write-Log -Level ERROR -Message "Options configuration is missing" -Module "Config"
return $false
}
# Check required option properties
$requiredOptions = @('dryRun', 'createBackup', 'verboseLogging', 'autoReboot')
foreach ($option in $requiredOptions) {
if (-not (Get-Member -InputObject $script:Config.options -Name $option -MemberType NoteProperty)) {
Write-Log -Level ERROR -Message "Missing required option: $option" -Module "Config"
return $false
}
}
Write-Log -Level INFO -Message "Configuration validation passed" -Module "Config"
return $true
}
function Get-Config {
<#
.SYNOPSIS
Get current configuration object
.OUTPUTS
Configuration object
#>
return $script:Config
}
function Get-ModuleConfig {
<#
.SYNOPSIS
Get configuration for specific module
.PARAMETER ModuleName
Name of the module
.OUTPUTS
Module configuration object or $null if not found
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ModuleName
)
if ($null -eq $script:Config -or $null -eq $script:Config.modules) {
Write-Log -Level WARNING -Message "Configuration not initialized" -Module "Config"
return $null
}
$moduleConfig = $script:Config.modules | Get-Member -Name $ModuleName -MemberType NoteProperty
if ($null -eq $moduleConfig) {
Write-Log -Level WARNING -Message "Module configuration not found: $ModuleName" -Module "Config"
return $null
}
return $script:Config.modules.$ModuleName
}
function Test-ModuleAvailability {
<#
.SYNOPSIS
Check if a module is actually implemented and available
.DESCRIPTION
Checks if module directory exists and contains the required .psd1 manifest file
.PARAMETER ModuleName
Name of the module to check
.OUTPUTS
Boolean - True if module is implemented, False otherwise
.EXAMPLE
Test-ModuleAvailability -ModuleName "SecurityBaseline"
#>
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory = $true)]
[string]$ModuleName
)
# Get framework root (3 levels up from Config.ps1)
$frameworkRoot = Split-Path $PSScriptRoot -Parent
$modulePath = Join-Path $frameworkRoot "Modules\$ModuleName"
$manifestPath = Join-Path $modulePath "$ModuleName.psd1"
# Check if module directory exists
if (-not (Test-Path $modulePath)) {
Write-Log -Level DEBUG -Message "Module directory not found: $modulePath" -Module "Config"
return $false
}
# Check if module manifest exists
if (-not (Test-Path $manifestPath)) {
Write-Log -Level DEBUG -Message "Module manifest not found: $manifestPath" -Module "Config"
return $false
}
Write-Log -Level DEBUG -Message "Module $ModuleName is available" -Module "Config"
return $true
}
function Get-EnabledModules {
<#
.SYNOPSIS
Get list of enabled modules sorted by priority
.DESCRIPTION
Returns only modules that are both enabled in config AND actually implemented
.OUTPUTS
Array of enabled module names sorted by priority
#>
[CmdletBinding()]
[OutputType([string[]])]
param()
if ($null -eq $script:Config -or $null -eq $script:Config.modules) {
Write-Log -Level WARNING -Message "Configuration not initialized" -Module "Config"
return @()
}
$enabledModules = @()
foreach ($prop in $script:Config.modules.PSObject.Properties) {
$moduleName = $prop.Name
$moduleConfig = $prop.Value
if ($moduleConfig.enabled -eq $true) {
# Check if module is actually implemented
if (Test-ModuleAvailability -ModuleName $moduleName) {
$enabledModules += [PSCustomObject]@{
Name = $moduleName
Priority = $moduleConfig.priority
}
}
else {
$status = if ($moduleConfig.PSObject.Properties.Name -contains 'status') { $moduleConfig.status } else { 'UNKNOWN' }
Write-Log -Level WARNING -Message "Module '$moduleName' is enabled in config but not implemented (Status: $status)" -Module "Config"
}
}
}
# Sort by priority
$sorted = $enabledModules | Sort-Object -Property Priority
return $sorted | ForEach-Object { $_.Name }
}
function Set-ModuleEnabled {
<#
.SYNOPSIS
Enable or disable a module
.PARAMETER ModuleName
Name of the module
.PARAMETER Enabled
Enable (true) or disable (false) the module
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ModuleName,
[Parameter(Mandatory = $true)]
[bool]$Enabled
)
if ($null -eq $script:Config -or $null -eq $script:Config.modules) {
throw "Configuration not initialized"
}
$moduleConfig = $script:Config.modules | Get-Member -Name $ModuleName -MemberType NoteProperty
if ($null -eq $moduleConfig) {
throw "Module not found: $ModuleName"
}
$script:Config.modules.$ModuleName.enabled = $Enabled
Write-Log -Level INFO -Message "Module '$ModuleName' set to: $Enabled" -Module "Config"
}
# Note: Export-ModuleMember not used - this script is dot-sourced, not imported as module

927
Core/Framework.ps1 Normal file
View file

@ -0,0 +1,927 @@
<#
.SYNOPSIS
Main orchestration engine for NoID Privacy Framework
.DESCRIPTION
Core framework that orchestrates module execution, manages configuration,
logging, validation, and rollback functionality.
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: PowerShell 5.1+
.EXAMPLE
.\Framework.ps1 -DryRun
Run in dry-run mode to preview changes
.EXAMPLE
.\Framework.ps1 -ModulesOnly SecurityBaseline,ASR
Run only specific modules
#>
# Note: This script is dot-sourced as a library, not called directly with parameters.
# All configuration comes from config.json via Initialize-Config.
# Script-level variables
$script:FrameworkVersion = "2.2.0"
$script:FrameworkRoot = Split-Path -Parent $PSScriptRoot
$script:ExecutionStartTime = Get-Date
# Import core and utility modules
$script:ModulesToLoad = @(
[PSCustomObject]@{ Path = "Core\Logger.ps1"; Name = "Logger" },
[PSCustomObject]@{ Path = "Core\Config.ps1"; Name = "Config" },
[PSCustomObject]@{ Path = "Core\Validator.ps1"; Name = "Validator" },
[PSCustomObject]@{ Path = "Core\Rollback.ps1"; Name = "Rollback" },
[PSCustomObject]@{ Path = "Core\NonInteractive.ps1"; Name = "NonInteractive" },
[PSCustomObject]@{ Path = "Utils\Registry.ps1"; Name = "Registry Utils" },
[PSCustomObject]@{ Path = "Utils\Service.ps1"; Name = "Service Utils" },
[PSCustomObject]@{ Path = "Utils\Hardware.ps1"; Name = "Hardware Utils" },
# NOTE: Utils\GPO.ps1 removed - v2.0 SecurityBaseline is self-contained, no LGPO.exe dependency
[PSCustomObject]@{ Path = "Utils\Localization.ps1"; Name = "Localization Utils" },
[PSCustomObject]@{ Path = "Utils\Compatibility.ps1"; Name = "Compatibility Utils" },
[PSCustomObject]@{ Path = "Utils\Dependencies.ps1"; Name = "Dependencies Utils" }
)
Write-Host "NoID Privacy Framework v$script:FrameworkVersion" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
foreach ($moduleInfo in $script:ModulesToLoad) {
$modulePath = Join-Path $script:FrameworkRoot $moduleInfo.Path
if (Test-Path -Path $modulePath) {
try {
. $modulePath
Write-Host "[OK] Loaded: $($moduleInfo.Name)" -ForegroundColor Green
}
catch {
Write-Host "[ERROR] Failed to load: $($moduleInfo.Name)" -ForegroundColor Red
Write-Host "Error: $_" -ForegroundColor Red
exit 1
}
}
else {
Write-Host "[ERROR] Module not found: $($moduleInfo.Name) ($modulePath)" -ForegroundColor Red
exit 1
}
}
Write-Host ""
function Initialize-Framework {
<#
.SYNOPSIS
Initialize the framework and all subsystems
#>
[CmdletBinding()]
param()
Write-Host "Initializing framework..." -ForegroundColor Cyan
# Only initialize logger if not already initialized (NoIDPrivacy.ps1 may have already done it)
if ([string]::IsNullOrEmpty($global:LoggerConfig.LogFilePath)) {
$logLevel = if ($VerboseLogging) { [LogLevel]::DEBUG } else { [LogLevel]::INFO }
$logDirectory = Join-Path $script:FrameworkRoot "Logs"
Initialize-Logger -LogDirectory $logDirectory -MinimumLevel $logLevel
}
Write-Log -Level INFO -Message "NoID Privacy Framework v$script:FrameworkVersion starting" -Module "Framework"
Write-Log -Level INFO -Message "PowerShell version: $($PSVersionTable.PSVersion)" -Module "Framework"
# Load configuration
if ($ConfigPath) {
Initialize-Config -ConfigPath $ConfigPath
}
else {
Initialize-Config
}
# Initialize backup system
if (-not $SkipBackup) {
Initialize-BackupSystem
# Create system restore point
Write-Host "Creating system restore point..." -ForegroundColor Yellow
$restorePointCreated = New-SystemRestorePoint
if (-not $restorePointCreated) {
Write-Log -Level WARNING -Message "System restore point creation failed or unavailable" -Module "Framework"
}
}
else {
Write-Log -Level WARNING -Message "Backup system SKIPPED (not recommended)" -Module "Framework"
}
Write-Host ""
}
function Test-FrameworkPrerequisites {
<#
.SYNOPSIS
Validate all prerequisites before execution
.OUTPUTS
Boolean indicating if prerequisites are met
#>
[CmdletBinding()]
[OutputType([bool])]
param()
Write-Host "Validating system prerequisites..." -ForegroundColor Cyan
Write-Host ""
$prereqsPassed = Test-Prerequisites
if (-not $prereqsPassed.Success) {
Write-Log -Level ERROR -Message "Prerequisite validation failed" -Module "Framework"
Write-Host ""
Write-Host "PREREQUISITE CHECK FAILED" -ForegroundColor Red
Write-Host "Please resolve the issues above before continuing." -ForegroundColor Red
return $false
}
Write-Host ""
Write-Host "All basic prerequisite checks passed" -ForegroundColor Green
Write-Host ""
# Check if system is domain-joined (interactive warning only in CLI mode)
Write-Host "Checking domain status..." -ForegroundColor Cyan
if (Test-NonInteractiveMode) {
# GUI mode - just check, don't prompt
$null = Test-DomainJoined
}
else {
# CLI mode - show interactive warning
$null = Test-DomainJoined -Interactive
}
Write-Host ""
# Confirm system backup exists (interactive prompt only in CLI mode)
Write-Host "Verifying system backup..." -ForegroundColor Cyan
if (Test-NonInteractiveMode) {
# GUI mode - auto-confirm (backup is created by engine)
Write-Host "[GUI] Backup verification: Auto-confirmed" -ForegroundColor Cyan
$backupStatus = [PSCustomObject]@{ UserConfirmed = $true }
}
else {
# CLI mode - interactive prompt
$backupStatus = Confirm-SystemBackup
}
if (-not $backupStatus.UserConfirmed) {
Write-Log -Level ERROR -Message "System backup confirmation failed" -Module "Framework"
return $false
}
Write-Host ""
Write-Host "All prerequisite checks completed successfully" -ForegroundColor Green
Write-Host ""
return $true
}
function Get-ModulesToExecute {
<#
.SYNOPSIS
Determine which modules should be executed
.OUTPUTS
Array of module names to execute
#>
[CmdletBinding()]
[OutputType([string[]])]
param()
if ($ModulesOnly -and $ModulesOnly.Count -gt 0) {
Write-Log -Level INFO -Message "Running specific modules: $($ModulesOnly -join ', ')" -Module "Framework"
return $ModulesOnly
}
else {
$enabledModules = Get-EnabledModules
Write-Log -Level INFO -Message "Running all enabled modules: $($enabledModules -join ', ')" -Module "Framework"
return $enabledModules
}
}
# NOTE: Invoke-HardeningModule has been removed.
# Use Invoke-Hardening instead for module execution.
function Start-HardeningProcess {
<#
.SYNOPSIS
Main execution entry point
#>
[CmdletBinding()]
param()
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "STARTING HARDENING PROCESS" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
if ($DryRun) {
Write-Host "MODE: DRY RUN (Preview only)" -ForegroundColor Yellow
Write-Log -Level WARNING -Message "Running in DRY RUN mode - no changes will be applied" -Module "Framework"
}
else {
Write-Host "MODE: APPLY CHANGES" -ForegroundColor Green
Write-Log -Level INFO -Message "Running in APPLY mode - changes will be applied" -Module "Framework"
}
Write-Host ""
# Get modules to execute
$modulesToRun = Get-ModulesToExecute
if ($modulesToRun.Count -eq 0) {
Write-Log -Level WARNING -Message "No modules enabled for execution" -Module "Framework"
Write-Host "No modules to execute. Check your configuration." -ForegroundColor Yellow
return
}
Write-Host "Modules to execute: $($modulesToRun.Count)" -ForegroundColor Cyan
foreach ($mod in $modulesToRun) {
Write-Host " - $mod" -ForegroundColor White
}
Write-Host ""
# Confirmation prompt (unless in dry run or NonInteractive mode)
if (-not $DryRun -and -not (Test-NonInteractiveMode)) {
Write-Host "WARNING: This will modify your system settings." -ForegroundColor Yellow
Write-Host "A backup and restore point have been created." -ForegroundColor Yellow
Write-Host ""
do {
$confirmation = Read-Host "Do you want to continue? [Y/N] (default: Y)"
if ([string]::IsNullOrWhiteSpace($confirmation)) { $confirmation = "Y" }
$confirmation = $confirmation.ToUpper()
if ($confirmation -notin @('Y', 'N')) {
Write-Host ""
Write-Host "Invalid input. Please enter Y or N." -ForegroundColor Red
Write-Host ""
}
} while ($confirmation -notin @('Y', 'N'))
if ($confirmation -ne "Y") {
Write-Log -Level INFO -Message "Execution cancelled by user" -Module "Framework"
Write-Host ""
Write-Host "Execution cancelled." -ForegroundColor Yellow
Write-Host ""
return
}
Write-Host ""
}
elseif (Test-NonInteractiveMode) {
Write-NonInteractiveDecision -Module "Framework" -Decision "Auto-confirming execution (GUI mode)"
}
# Execute modules using Invoke-Hardening
Write-Log -Level INFO -Message "Starting module execution: $($modulesToRun -join ', ')" -Module "Framework"
# Determine correct module parameter based on what user selected
if ($modulesToRun.Count -eq 1) {
$hardeningResult = Invoke-Hardening -Module $modulesToRun[0] -DryRun:$DryRun
}
else {
$hardeningResult = Invoke-Hardening -Module "All" -DryRun:$DryRun
}
# Summary (correctly use hardeningResult.ModuleResults)
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "EXECUTION SUMMARY" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
$totalDuration = (Get-Date) - $script:ExecutionStartTime
# Correct calculation from ModuleResults
$totalModules = $hardeningResult.ModulesExecuted
$successCount = ($hardeningResult.ModuleResults | Where-Object { $_.Success }).Count
$failureCount = $totalModules - $successCount
Write-Host "Total modules executed: $totalModules" -ForegroundColor White
Write-Host "Successful: $successCount" -ForegroundColor Green
Write-Host "Failed: $failureCount" -ForegroundColor $(if ($failureCount -gt 0) { "Red" } else { "White" })
Write-Host "Total duration: $([math]::Round($totalDuration.TotalMinutes, 2)) minutes" -ForegroundColor White
if ($hardeningResult.Warnings.Count -gt 0) {
Write-Host "Warnings: $($hardeningResult.Warnings.Count)" -ForegroundColor Yellow
}
if ($hardeningResult.Errors.Count -gt 0) {
Write-Host "Errors: $($hardeningResult.Errors.Count)" -ForegroundColor Red
}
Write-Host ""
Write-Log -Level INFO -Message "Hardening process completed" -Module "Framework"
Write-Log -Level INFO -Message "Log file: $(Get-LogFilePath)" -Module "Framework"
Write-Host "Log file: $(Get-LogFilePath)" -ForegroundColor Cyan
Write-Host ""
# Reboot recommendation
if (-not $DryRun -and $successCount -gt 0) {
Write-Host "RECOMMENDATION: Restart your computer for all changes to take effect." -ForegroundColor Yellow
Write-Host ""
}
}
function Invoke-Hardening {
<#
.SYNOPSIS
Execute hardening module(s)
.PARAMETER Module
Module name to execute (SecurityBaseline, ASR, DNS, etc.) or "All"
.PARAMETER DryRun
Preview changes without applying them
.OUTPUTS
PSCustomObject with execution results
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
[Parameter(Mandatory = $true)]
[ValidateSet("SecurityBaseline", "ASR", "DNS", "Privacy", "AntiAI", "EdgeHardening", "AdvancedSecurity", "All")]
[string]$Module,
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
$startTime = Get-Date
$results = [PSCustomObject]@{
Success = $true
ModulesExecuted = 0
Duration = $null
Errors = @()
Warnings = @()
ModuleResults = @()
}
# Get module list to execute
$modulesToExecute = @()
if ($Module -eq "All") {
# Get all enabled modules from config (sorted by priority)
$modulesToExecute = Get-EnabledModules
}
else {
# Single module - check if it's enabled in config
$moduleConfig = $script:Config.modules.$Module
if ($null -eq $moduleConfig) {
Write-Log -Level WARNING -Message "Module '$Module' not found in configuration" -Module "Framework"
$results.Warnings += "Module '$Module' not configured"
return $results
}
if ($moduleConfig.enabled -eq $false) {
Write-Log -Level INFO -Message "Module '$Module' is disabled in configuration - skipping" -Module "Framework"
Write-Host "Module '$Module' is disabled - skipping execution" -ForegroundColor Gray
return $results
}
$modulesToExecute = @($Module)
}
Write-Log -Level INFO -Message "Executing modules: $($modulesToExecute -join ', ')" -Module "Framework"
# Initialize backup system ONCE before all modules
if (-not $DryRun) {
try {
Initialize-BackupSystem
Write-Log -Level INFO -Message "Backup session initialized for all modules" -Module "Framework"
# Set session type from GUI config (for backup identification)
# SessionType is in options block when sent from GUI
if ($script:Config.options -and $script:Config.options.PSObject.Properties.Name -contains 'sessionType' -and $script:Config.options.sessionType) {
Set-SessionType -SessionType $script:Config.options.sessionType
Write-Log -Level DEBUG -Message "Session type from GUI config: $($script:Config.options.sessionType)" -Module "Framework"
}
else {
# CLI mode: Auto-detect session type based on module count
$autoSessionType = if ($modulesToExecute.Count -ge 7) { "wizard" }
elseif ($modulesToExecute.Count -eq 1) { "advanced" }
else { "manual" }
Set-SessionType -SessionType $autoSessionType
Write-Log -Level DEBUG -Message "Session type auto-detected: $autoSessionType (based on $($modulesToExecute.Count) modules)" -Module "Framework"
}
# Create Pre-Framework Snapshot for ASR Rules (shared resource conflict prevention)
# Only when: Multi-module apply AND ASR module is in the list
# Why: SecurityBaseline sets 15 ASR rules, then ASR sets 19 rules
# We need to capture the ORIGINAL state before ANY module touches ASR
if ($modulesToExecute.Count -gt 1 -and $modulesToExecute -contains "ASR") {
Write-Log -Level INFO -Message "Creating Pre-Framework ASR snapshot (multi-module apply with ASR detected)" -Module "Framework"
try {
$sessionPath = $global:BackupBasePath
$mpPref = Get-MpPreference -ErrorAction SilentlyContinue
if (-not $mpPref) {
Write-Log -Level INFO -Message "Pre-Framework ASR snapshot skipped: Get-MpPreference returned no data (Defender/ASR not available)." -Module "Framework"
}
else {
$hasIdsProp = $mpPref.PSObject.Properties.Match('AttackSurfaceReductionRules_Ids').Count -gt 0
$hasActionsProp = $mpPref.PSObject.Properties.Match('AttackSurfaceReductionRules_Actions').Count -gt 0
if (-not $hasIdsProp -and -not $hasActionsProp) {
Write-Log -Level INFO -Message "Pre-Framework ASR snapshot skipped: ASR rule properties not present (third-party AV or Defender ASR disabled)." -Module "Framework"
}
else {
$ruleIds = @()
$ruleActions = @()
if ($hasIdsProp -and $mpPref.AttackSurfaceReductionRules_Ids) {
$ruleIds = @($mpPref.AttackSurfaceReductionRules_Ids)
}
if ($hasActionsProp -and $mpPref.AttackSurfaceReductionRules_Actions) {
$ruleActions = @($mpPref.AttackSurfaceReductionRules_Actions)
}
$ruleCount = $ruleIds.Count
$preFrameworkSnapshot = @{
ASR = @{
RuleIds = $ruleIds
RuleActions = $ruleActions
SnapshotDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
RuleCount = $ruleCount
}
AppliesTo = @("ASR") # Only apply this snapshot if ASR module is being restored
}
$snapshotPath = Join-Path $sessionPath "PreFramework_Snapshot.json"
$preFrameworkSnapshot | ConvertTo-Json -Depth 10 | Out-File $snapshotPath -Encoding UTF8
Write-Log -Level SUCCESS -Message "Pre-Framework snapshot saved: $ruleCount ASR rules captured (original system state)" -Module "Framework"
}
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create Pre-Framework snapshot (non-critical): $_" -Module "Framework"
}
}
}
catch {
Write-ErrorLog -Message "Failed to initialize backup system" -Module "Framework" -ErrorRecord $_
$warnMsg = "Backup system initialization failed - proceeding without automatic backup"
$results.Warnings += $warnMsg
}
}
# Execute each module
foreach ($moduleName in $modulesToExecute) {
try {
Write-Log -Level INFO -Message "========================================" -Module "Framework"
Write-Log -Level INFO -Message "Module: $moduleName" -Module "Framework"
Write-Log -Level INFO -Message "========================================" -Module "Framework"
# Module Confirmation Prompt for interactive CLI runs.
# Skipped in DryRun and in NonInteractive/GUI mode.
if (-not $DryRun -and -not (Test-NonInteractiveMode)) {
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " MODULE: $moduleName" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
# Module-specific description
switch ($moduleName) {
"SecurityBaseline" {
Write-Host "Microsoft Security Baseline for Windows 11 25H2" -ForegroundColor White
Write-Host ""
Write-Host " > Applies 425 hardening settings:" -ForegroundColor Gray
Write-Host " - 335 Registry policies (password, firewall, BitLocker)" -ForegroundColor Gray
Write-Host " - 67 Security template settings (user rights, audit)" -ForegroundColor Gray
Write-Host " - 23 Advanced audit policies" -ForegroundColor Gray
Write-Host " - VBS + Credential Guard + Memory Integrity" -ForegroundColor Gray
Write-Host ""
Write-Host " Impact: Enterprise-grade security, may break legacy software" -ForegroundColor Yellow
}
"ASR" {
Write-Host "Attack Surface Reduction Rules" -ForegroundColor White
Write-Host ""
Write-Host " > Applies 19 Microsoft Defender ASR rules:" -ForegroundColor Gray
Write-Host " - Block ransomware, exploits, malicious scripts" -ForegroundColor Gray
Write-Host " - Block credential theft (lsass.exe protection)" -ForegroundColor Gray
Write-Host " - Block Office macros, email executables" -ForegroundColor Gray
Write-Host " - Block untrusted USB execution, Safe Mode reboot" -ForegroundColor Gray
Write-Host ""
Write-Host " Note: You'll be asked about SCCM/Intune usage" -ForegroundColor Yellow
}
"DNS" {
Write-Host "Secure DNS with DNS-over-HTTPS" -ForegroundColor White
Write-Host ""
Write-Host " > Configures encrypted DNS:" -ForegroundColor Gray
Write-Host " - Choose provider: Cloudflare, Quad9, or AdGuard" -ForegroundColor Gray
Write-Host " - Enable DoH encryption (HTTPS)" -ForegroundColor Gray
Write-Host " - Blocks DNS hijacking and snooping" -ForegroundColor Gray
Write-Host " - IPv4 + IPv6 configuration" -ForegroundColor Gray
Write-Host ""
Write-Host " Note: You'll choose provider and DoH mode interactively" -ForegroundColor Yellow
}
"Privacy" {
Write-Host "Telemetry & Privacy Hardening" -ForegroundColor White
Write-Host ""
Write-Host " > Applies privacy settings based on selected mode:" -ForegroundColor Gray
Write-Host " - Telemetry control (3 modes: MSRecommended/Strict/Paranoid)" -ForegroundColor Gray
Write-Host " - MSRecommended: 37 settings (default, max compatibility)" -ForegroundColor DarkGray
Write-Host " - Strict/Paranoid: 37-40 settings + services disabled" -ForegroundColor DarkGray
Write-Host " - Disable ads, tips, personalization" -ForegroundColor Gray
Write-Host " - Remove bloatware (up to 24 apps, if present)" -ForegroundColor Gray
Write-Host " - OneDrive hardening (keeps sync functional)" -ForegroundColor Gray
Write-Host ""
Write-Host " Note: You'll choose privacy mode interactively" -ForegroundColor Yellow
}
"AntiAI" {
Write-Host "Disable Windows 11 AI Features" -ForegroundColor White
Write-Host ""
Write-Host " > Disables 13 features via 32 policies:" -ForegroundColor Gray
Write-Host " - Windows Recall + Export Block" -ForegroundColor Gray
Write-Host " - Windows Copilot (app + URI handlers + Edge sidebar)" -ForegroundColor Gray
Write-Host " - Click to Do, Explorer AI Actions" -ForegroundColor Gray
Write-Host " - Paint AI (3), Notepad AI, Settings Agent" -ForegroundColor Gray
Write-Host ""
Write-Host " Impact: All AI features disabled, reboot required" -ForegroundColor Yellow
}
"EdgeHardening" {
Write-Host "Microsoft Edge v139 Security Baseline" -ForegroundColor White
Write-Host ""
Write-Host " > Applies Edge security policies:" -ForegroundColor Gray
Write-Host " - Enhanced Security Mode, SmartScreen + PUA" -ForegroundColor Gray
Write-Host " - Site Isolation + SSL/TLS hardening" -ForegroundColor Gray
Write-Host " - Tracking Prevention + Privacy settings" -ForegroundColor Gray
Write-Host " - Extension blocklist (blocks all by default)" -ForegroundColor Gray
Write-Host ""
Write-Host " Impact: Maximum Edge security, extensions blocked by default" -ForegroundColor Yellow
}
"AdvancedSecurity" {
Write-Host "Advanced Security Hardening (Beyond MS Baseline)" -ForegroundColor White
Write-Host ""
Write-Host " > Applies 15 security features (50 individual settings):" -ForegroundColor Gray
Write-Host " - RDP hardening + optional complete disable" -ForegroundColor Gray
Write-Host " - WDigest credential protection" -ForegroundColor Gray
Write-Host " - Admin Shares disable (domain-aware)" -ForegroundColor Gray
Write-Host " - Risky ports/services block (LLMNR, NetBIOS, UPnP)" -ForegroundColor Gray
Write-Host " - Legacy TLS 1.0/1.1 disable, WPAD disable, PSv2 removal" -ForegroundColor Gray
Write-Host " - SRP .lnk protection (CVE-2025-9491)" -ForegroundColor Gray
Write-Host " - Windows Update (3 simple GUI settings)" -ForegroundColor Gray
Write-Host " - Wireless Display (Miracast) security" -ForegroundColor Gray
Write-Host ""
Write-Host " Note: You'll choose profile (Balanced/Enterprise/Maximum)" -ForegroundColor Yellow
}
default {
Write-Host "This module will apply changes to your system." -ForegroundColor White
Write-Host ""
}
}
Write-Host ""
Write-Host "Options:" -ForegroundColor White
Write-Host " [Y] Yes - Apply this module" -ForegroundColor Green
Write-Host " [N] No - Skip this module" -ForegroundColor Yellow
Write-Host " [A] Abort - Stop entire process" -ForegroundColor Red
Write-Host ""
do {
$response = Read-Host "Continue with ${moduleName}? [Y/N/A] (default: Y)"
if ([string]::IsNullOrWhiteSpace($response)) { $response = "Y" }
$response = $response.ToUpper()
} while ($response -notin @('Y', 'N', 'A', 'YES', 'NO', 'ABORT'))
if ($response -in @('A', 'ABORT')) {
Write-Log -Level WARNING -Message "User aborted execution at module: $moduleName" -Module "Framework"
Write-Host ""
Write-Host "Execution aborted by user" -ForegroundColor Red
Write-Host ""
$results.Warnings += "Execution aborted by user at module: $moduleName"
break
}
elseif ($response -in @('N', 'NO')) {
Write-Log -Level INFO -Message "User skipped module: $moduleName" -Module "Framework"
Write-Host ""
Write-Host "Skipping module: $moduleName" -ForegroundColor Yellow
Write-Host ""
continue
}
Write-Host ""
Write-Host "Proceeding with $moduleName..." -ForegroundColor Green
Write-Host ""
}
$moduleResult = $null
$modulePath = Join-Path $script:FrameworkRoot "Modules\$moduleName"
# Check if module exists
if (-not (Test-Path $modulePath)) {
$errMsg = "Module not found: $moduleName (Path: $modulePath)"
Write-Log -Level WARNING -Message $errMsg -Module "Framework"
$results.Warnings += $errMsg
continue
}
# Check module implementation status (FIX #2)
$moduleConfig = $script:Config.modules.$moduleName
if ($moduleConfig.PSObject.Properties.Name -contains 'status') {
if ($moduleConfig.status -ne 'IMPLEMENTED') {
Write-Log -Level WARNING -Message "Skipping module '$moduleName' - Status: $($moduleConfig.status) (not IMPLEMENTED)" -Module "Framework"
Write-Host " [SKIP] $moduleName - Not yet implemented" -ForegroundColor Yellow
continue
}
}
# Load and execute module based on name
switch ($moduleName) {
"SecurityBaseline" {
$manifestPath = Join-Path $modulePath "SecurityBaseline.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
$moduleResult = Invoke-SecurityBaseline -DryRun:$DryRun
}
else {
throw "Module manifest not found: $manifestPath"
}
}
"ASR" {
$manifestPath = Join-Path $modulePath "ASR.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
$moduleResult = Invoke-ASRRules -DryRun:$DryRun
}
else {
throw "Module manifest not found: $manifestPath"
}
}
"DNS" {
$manifestPath = Join-Path $modulePath "DNS.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
# DNS module handles provider selection
# ONLY pass config values in NonInteractive mode (GUI)
# In interactive mode, let the module prompt the user!
if (Test-NonInteractiveMode) {
# GUI mode - use config values
$moduleResult = Invoke-DNSConfiguration -Provider $script:Config.modules.DNS.provider -DryRun:$DryRun
}
else {
# Interactive CLI mode - module will ask for provider and DoH mode
$moduleResult = Invoke-DNSConfiguration -DryRun:$DryRun
}
}
else {
throw "Module manifest not found: $manifestPath"
}
}
"Privacy" {
$manifestPath = Join-Path $modulePath "Privacy.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
# Privacy module handles mode selection
# ONLY pass config values in NonInteractive mode (GUI)
if (Test-NonInteractiveMode) {
# GUI mode - use config values
$privacyArgs = @{ DryRun = $DryRun }
if ($script:Config.modules.Privacy.PSObject.Properties.Name -contains 'mode' -and $script:Config.modules.Privacy.mode) {
Write-Log -Level INFO -Message "Privacy mode: $($script:Config.modules.Privacy.mode)" -Module "Framework"
$privacyArgs["Mode"] = $script:Config.modules.Privacy.mode
}
if ($script:Config.modules.Privacy.PSObject.Properties.Name -contains 'removeBloatware') {
$rb = $script:Config.modules.Privacy.removeBloatware
if ($rb -is [string]) {
$rb = ($rb -eq "Y" -or $rb -eq "yes" -or $rb -eq "true" -or $rb -eq "1")
}
Write-Log -Level INFO -Message "Privacy removeBloatware: $rb" -Module "Framework"
$privacyArgs["RemoveBloatware"] = $rb
}
$moduleResult = Invoke-PrivacyHardening @privacyArgs
}
else {
# Interactive CLI mode - module will ask for mode selection
$moduleResult = Invoke-PrivacyHardening -DryRun:$DryRun
}
}
else {
throw "Module manifest not found: $manifestPath"
}
}
"AntiAI" {
$manifestPath = Join-Path $modulePath "AntiAI.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
# AntiAI module applies maximum AI deactivation (no modes)
Write-Log -Level INFO -Message "Disabling all Windows 11 AI features (13 features, 32 policies)" -Module "Framework"
$moduleResult = Invoke-AntiAI -DryRun:$DryRun
}
else {
throw "Module manifest not found: $manifestPath"
}
}
"EdgeHardening" {
$manifestPath = Join-Path $modulePath "EdgeHardening.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
# EdgeHardening applies Microsoft Edge security baseline
Write-Log -Level INFO -Message "Applying Microsoft Edge v139 Security Baseline (24 policies)" -Module "Framework"
$moduleResult = Invoke-EdgeHardening -DryRun:$DryRun
}
else {
throw "Module manifest not found: $manifestPath"
}
}
"AdvancedSecurity" {
$manifestPath = Join-Path $modulePath "AdvancedSecurity.psd1"
if (Test-Path $manifestPath) {
if (-not (Get-Module -Name $moduleName)) {
Import-Module $manifestPath -ErrorAction Stop
}
# AdvancedSecurity handles profile selection
# ONLY pass config values in NonInteractive mode (GUI)
if (Test-NonInteractiveMode) {
# GUI mode - use config values (securityProfile)
$secProfile = $script:Config.modules.AdvancedSecurity.securityProfile
if ($secProfile) {
Write-Log -Level INFO -Message "AdvancedSecurity profile: $secProfile" -Module "Framework"
$moduleResult = Invoke-AdvancedSecurity -SecurityProfile $secProfile -DryRun:$DryRun
}
else {
$moduleResult = Invoke-AdvancedSecurity -DryRun:$DryRun
}
}
else {
# Interactive CLI mode - module will ask for profile
$moduleResult = Invoke-AdvancedSecurity -DryRun:$DryRun
}
}
else {
throw "Module manifest not found: $manifestPath"
}
}
default {
$warnMsg = "Module '$moduleName' is not yet implemented"
Write-Log -Level WARNING -Message $warnMsg -Module "Framework"
$results.Warnings += $warnMsg
continue
}
}
# Store module result
if ($moduleResult) {
# If module returned an array, use the last element as the actual result
# (handles cases where helper functions inadvertently output to pipeline)
if ($moduleResult -is [array]) {
Write-Log -Level DEBUG -Message "Module '$moduleName' returned array ($($moduleResult.Count) items), using last element as result object" -Module "Framework"
$moduleResult = $moduleResult[-1]
}
$results.ModuleResults += $moduleResult
$results.ModulesExecuted++
# Handle different return types: Boolean or PSCustomObject
$success = $false
if ($moduleResult -is [bool]) {
# Module returned simple boolean (e.g., Privacy module)
$success = $moduleResult
}
elseif ($moduleResult -is [PSCustomObject]) {
# Module returned object with Success property (e.g., ASR, DNS modules)
$hasSuccess = $null -ne ($moduleResult.PSObject.Properties | Where-Object { $_.Name -eq 'Success' })
$success = if ($hasSuccess) { $moduleResult.Success } else { $false }
}
else {
# Unknown type - assume failure
Write-Log -Level WARNING -Message "Module '$moduleName' returned unexpected type: $($moduleResult.GetType().Name)" -Module "Framework"
}
if ($success) {
Write-Log -Level SUCCESS -Message "Module '$moduleName' completed successfully" -Module "Framework"
}
else {
Write-Log -Level WARNING -Message "Module '$moduleName' completed with errors" -Module "Framework"
# Only add errors if moduleResult is an object with Errors property
if ($moduleResult -is [PSCustomObject]) {
$hasErrors = $null -ne ($moduleResult.PSObject.Properties | Where-Object { $_.Name -eq 'Errors' })
if ($hasErrors -and $moduleResult.Errors.Count -gt 0) {
$results.Errors += $moduleResult.Errors
}
}
}
# Always collect warnings from modules (regardless of success)
# Warnings are informational (e.g., "rule set to AUDIT mode") - not errors
if ($moduleResult -is [PSCustomObject]) {
$hasWarnings = $null -ne ($moduleResult.PSObject.Properties | Where-Object { $_.Name -eq 'Warnings' })
if ($hasWarnings -and $moduleResult.Warnings.Count -gt 0) {
$results.Warnings += $moduleResult.Warnings
}
}
}
}
catch {
Write-ErrorLog -Message "Failed to execute module '$moduleName'" -Module "Framework" -ErrorRecord $_
$errMsg = "Module '$moduleName' execution failed: $($_.Exception.Message)"
$results.Errors += $errMsg
$results.Success = $false
}
}
# Calculate duration
$results.Duration = (Get-Date) - $startTime
# Final success status
if ($results.Errors.Count -gt 0) {
$results.Success = $false
}
# Update session display name for backup identification (after all modules complete)
if (-not $DryRun) {
try {
Update-SessionDisplayName
}
catch {
Write-Log -Level WARNING -Message "Failed to update session display name: $_" -Module "Framework"
}
}
Write-Log -Level INFO -Message "Hardening execution completed - Modules: $($results.ModulesExecuted), Errors: $($results.Errors.Count), Warnings: $($results.Warnings.Count)" -Module "Framework"
return $results
}
# Note: Export-ModuleMember not used - this script is dot-sourced, not imported as module
# Main execution (only if script is run directly, not dot-sourced)
# This allows Framework.ps1 to be used standalone OR dot-sourced by NoIDPrivacy.ps1
if ($MyInvocation.InvocationName -ne '.' -and $MyInvocation.Line -notmatch '^\s*\.\s+') {
try {
Initialize-Framework
$prereqsPassed = Test-FrameworkPrerequisites
if ($prereqsPassed) {
Start-HardeningProcess
}
else {
Write-Host "Execution aborted due to failed prerequisites." -ForegroundColor Red
exit 1
}
}
catch {
Write-Host ""
Write-Host "CRITICAL ERROR" -ForegroundColor Red
Write-Host "An unexpected error occurred:" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host ""
Write-Host "Stack trace:" -ForegroundColor Gray
Write-Host $_.ScriptStackTrace -ForegroundColor Gray
if ($null -ne (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
Write-Log -Level ERROR -Message "Critical framework error" -Module "Framework" -Exception $_.Exception
}
exit 1
}
}

366
Core/Logger.ps1 Normal file
View file

@ -0,0 +1,366 @@
<#
.SYNOPSIS
Unified logging system for NoID Privacy Framework
.DESCRIPTION
Provides centralized logging functionality with multiple severity levels,
file output, and optional console output with color coding.
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: PowerShell 5.1+
#>
# Log severity levels
enum LogLevel {
DEBUG = 0
INFO = 1
WARNING = 2
ERROR = 3
SUCCESS = 4
}
# Global logger configuration - MUST be $global: for cross-module session sharing
# Using $script: would create separate log files per Import-Module call!
# NOTE: Must use Get-Variable to check existence (direct access fails in Strict Mode)
if (-not (Get-Variable -Name 'LoggerConfig' -Scope Global -ErrorAction SilentlyContinue)) {
$global:LoggerConfig = @{
LogFilePath = ""
MinimumLevel = [LogLevel]::INFO
EnableConsole = $true
EnableFile = $true
TimestampFormat = "yyyy-MM-dd HH:mm:ss"
}
}
function Initialize-Logger {
<#
.SYNOPSIS
Initialize the logging system
.PARAMETER LogDirectory
Directory path for log files
.PARAMETER MinimumLevel
Minimum log level to record (DEBUG, INFO, WARNING, ERROR, SUCCESS)
.PARAMETER EnableConsole
Enable console output
.PARAMETER EnableFile
Enable file output
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$LogDirectory = (Join-Path $PSScriptRoot "..\Logs"),
[Parameter(Mandatory = $false)]
[LogLevel]$MinimumLevel = [LogLevel]::INFO,
[Parameter(Mandatory = $false)]
[bool]$EnableConsole = $true,
[Parameter(Mandatory = $false)]
[bool]$EnableFile = $true
)
# Reuse existing session if already initialized
if ($global:LoggerConfig.LogFilePath -and (Test-Path -Path $global:LoggerConfig.LogFilePath)) {
Write-Host "[Logger] Reusing existing log session: $($global:LoggerConfig.LogFilePath)" -ForegroundColor DarkGray
return
}
# Create log directory if it doesn't exist
if ($EnableFile) {
if (-not (Test-Path -Path $LogDirectory)) {
try {
New-Item -ItemType Directory -Path $LogDirectory -Force -ErrorAction Stop | Out-Null
}
catch {
Write-Host "[ERROR] Failed to create log directory: $LogDirectory" -ForegroundColor Red
Write-Host "[ERROR] Exception: $_" -ForegroundColor Red
$EnableFile = $false
}
}
}
# Generate log file name with timestamp
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logFileName = "NoIDPrivacy_$timestamp.log"
$global:LoggerConfig.LogFilePath = Join-Path $LogDirectory $logFileName
$global:LoggerConfig.MinimumLevel = $MinimumLevel
$global:LoggerConfig.EnableConsole = $EnableConsole
$global:LoggerConfig.EnableFile = $EnableFile
# Test if we can write to the log file
if ($EnableFile) {
try {
"# NoID Privacy Log File" | Out-File -FilePath $global:LoggerConfig.LogFilePath -Encoding UTF8 -ErrorAction Stop
}
catch {
Write-Host "[ERROR] Failed to create log file: $($global:LoggerConfig.LogFilePath)" -ForegroundColor Red
Write-Host "[ERROR] Exception: $_" -ForegroundColor Red
$global:LoggerConfig.EnableFile = $false
}
}
# Write initial log entry
Write-Log -Level INFO -Message "Logger initialized" -Module "Logger"
Write-Log -Level INFO -Message "Log file: $($global:LoggerConfig.LogFilePath)" -Module "Logger"
}
function Write-Log {
<#
.SYNOPSIS
Write a log entry
.PARAMETER Level
Log severity level
.PARAMETER Message
Log message content
.PARAMETER Module
Module or component name generating the log
.PARAMETER Exception
Optional exception object for error logging
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[LogLevel]$Level,
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[string]$Module = "Framework",
[Parameter(Mandatory = $false)]
[System.Exception]$Exception = $null
)
# Check if level meets minimum threshold
if ($Level -lt $global:LoggerConfig.MinimumLevel) {
return
}
# Format timestamp
$timestamp = Get-Date -Format $global:LoggerConfig.TimestampFormat
# Build log entry
$logEntry = "[$timestamp] [$Level] [$Module] $Message"
# Add exception details if present
if ($null -ne $Exception) {
$logEntry += "`n Exception: $($Exception.Message)"
$logEntry += "`n StackTrace: $($Exception.StackTrace)"
}
# Write to file with robust retry logic
if ($global:LoggerConfig.EnableFile -and $global:LoggerConfig.LogFilePath) {
$maxRetries = 5
$retryDelayMs = 100
$writeSuccess = $false
$lastError = $null
for ($i = 0; $i -lt $maxRetries; $i++) {
try {
Add-Content -Path $global:LoggerConfig.LogFilePath -Value $logEntry -Encoding UTF8 -ErrorAction Stop
$writeSuccess = $true
break
}
catch {
$lastError = $_
Start-Sleep -Milliseconds $retryDelayMs
# Exponential backoff
$retryDelayMs *= 2
}
}
if (-not $writeSuccess) {
# Write error to console with detailed info only after all retries failed
Write-Host "[FILE WRITE ERROR] Failed to write to log file after $maxRetries attempts: $($global:LoggerConfig.LogFilePath)" -ForegroundColor Red
Write-Host "[FILE WRITE ERROR] Last Exception: $lastError" -ForegroundColor Red
# Disable file logging to prevent spam
$global:LoggerConfig.EnableFile = $false
}
}
# Write to console with color coding (suppress DEBUG-level on console)
if ($global:LoggerConfig.EnableConsole -and $Level -ge [LogLevel]::INFO) {
$consoleColor = switch ($Level) {
([LogLevel]::DEBUG) { "Gray" }
([LogLevel]::INFO) { "White" }
([LogLevel]::WARNING) { "Yellow" }
([LogLevel]::ERROR) { "Red" }
([LogLevel]::SUCCESS) { "Green" }
default { "White" }
}
Write-Host $logEntry -ForegroundColor $consoleColor
}
}
function Get-LogFilePath {
<#
.SYNOPSIS
Get the current log file path
.OUTPUTS
String containing the log file path
#>
return $global:LoggerConfig.LogFilePath
}
function Get-ErrorContext {
<#
.SYNOPSIS
Extract detailed error context from PowerShell error record
.DESCRIPTION
Provides comprehensive error information including message, location,
line number, command, and stack trace for better debugging.
.PARAMETER ErrorRecord
The error record to analyze (defaults to $_ in catch block)
.OUTPUTS
Hashtable with error details
.EXAMPLE
catch {
$errorContext = Get-ErrorContext -ErrorRecord $_
Write-Log -Level ERROR -Message $errorContext.Summary -Module "MyModule"
}
#>
[CmdletBinding()]
[OutputType([hashtable])]
param(
[Parameter(Mandatory = $false)]
[System.Management.Automation.ErrorRecord]$ErrorRecord = $_
)
$context = @{
Message = ""
Exception = ""
Category = ""
TargetObject = ""
ScriptName = ""
LineNumber = 0
Command = ""
StackTrace = ""
Summary = ""
}
if ($null -eq $ErrorRecord) {
$context.Summary = "No error record available"
return $context
}
# Extract basic error information
$context.Message = $ErrorRecord.Exception.Message
$context.Exception = $ErrorRecord.Exception.GetType().FullName
$context.Category = $ErrorRecord.CategoryInfo.Category.ToString()
$context.TargetObject = if ($ErrorRecord.TargetObject) { $ErrorRecord.TargetObject.ToString() } else { "N/A" }
# Extract script location information
if ($ErrorRecord.InvocationInfo) {
$context.ScriptName = if ($ErrorRecord.InvocationInfo.ScriptName) {
Split-Path -Leaf $ErrorRecord.InvocationInfo.ScriptName
} else {
"N/A"
}
$context.LineNumber = $ErrorRecord.InvocationInfo.ScriptLineNumber
$context.Command = if ($ErrorRecord.InvocationInfo.MyCommand) {
$ErrorRecord.InvocationInfo.MyCommand.Name
} else {
"N/A"
}
}
# Extract stack trace
if ($ErrorRecord.ScriptStackTrace) {
$context.StackTrace = $ErrorRecord.ScriptStackTrace
}
# Build comprehensive summary
$summary = "$($context.Message)"
if ($context.ScriptName -and $context.LineNumber -gt 0) {
$summary += " [File: $($context.ScriptName), Line: $($context.LineNumber)]"
}
if ($context.Command) {
$summary += " [Command: $($context.Command)]"
}
if ($context.Category -ne "NotSpecified") {
$summary += " [Category: $($context.Category)]"
}
$context.Summary = $summary
return $context
}
function Write-ErrorLog {
<#
.SYNOPSIS
Write a comprehensive error log entry with full context
.DESCRIPTION
Convenience function that combines error context extraction
and logging in one call. Provides detailed error information.
.PARAMETER Message
Custom error message (will be prefixed to error details)
.PARAMETER Module
Module or component name
.PARAMETER ErrorRecord
The error record to log (defaults to $_ in catch block)
.PARAMETER IncludeStackTrace
Include full stack trace in log (default: true)
.EXAMPLE
catch {
Write-ErrorLog -Message "Failed to apply security settings" -Module "SecurityBaseline" -ErrorRecord $_
}
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[string]$Module = "Framework",
[Parameter(Mandatory = $false)]
[System.Management.Automation.ErrorRecord]$ErrorRecord = $_,
[Parameter(Mandatory = $false)]
[bool]$IncludeStackTrace = $true
)
$errorContext = Get-ErrorContext -ErrorRecord $ErrorRecord
# Build comprehensive error message
$fullMessage = "$Message - $($errorContext.Summary)"
# Log error with basic info
Write-Log -Level ERROR -Message $fullMessage -Module $Module -Exception $ErrorRecord.Exception
# Log additional context if available
if ($IncludeStackTrace -and $errorContext.StackTrace) {
Write-Log -Level DEBUG -Message "Stack Trace: $($errorContext.StackTrace)" -Module $Module
}
}
# Note: Export-ModuleMember not used - this script is dot-sourced, not imported as module
# All functions are automatically available when dot-sourced

206
Core/NonInteractive.ps1 Normal file
View file

@ -0,0 +1,206 @@
#Requires -Version 5.1
<#
.SYNOPSIS
NonInteractive mode helper functions for NoID Privacy GUI integration
.DESCRIPTION
Provides helper functions to check if running in NonInteractive mode (GUI)
and to retrieve config values instead of prompting users.
Used by all modules to support both CLI (interactive) and GUI (non-interactive) modes.
.NOTES
Author: NexusOne23
Version: 2.2.0
Usage in modules:
1. Call Test-NonInteractiveMode to check if prompts should be skipped
2. Use Get-NonInteractiveValue to get config values with defaults
#>
<#
.SYNOPSIS
Test if running in NonInteractive mode (GUI)
.DESCRIPTION
Checks if the global config has nonInteractive=true set.
When true, all Read-Host prompts should be skipped and config values used instead.
.OUTPUTS
[bool] True if nonInteractive mode is enabled
.EXAMPLE
if (Test-NonInteractiveMode) {
# Use config value
$choice = Get-NonInteractiveValue -Module "DNS" -Key "provider" -Default "Quad9"
} else {
# Interactive prompt
$choice = Read-Host "Select provider"
}
#>
function Test-NonInteractiveMode {
[CmdletBinding()]
[OutputType([bool])]
param()
# Check environment variable FIRST (set by GUI before process starts)
if ($env:NOIDPRIVACY_NONINTERACTIVE -eq "true") {
return $true
}
# Check global config
if ($script:Config -and $script:Config.options) {
if ($script:Config.options.nonInteractive -eq $true) {
return $true
}
}
return $false
}
# Set global variable at load time if environment variable is set
# This allows modules to check $global:NonInteractiveMode directly
if ($env:NOIDPRIVACY_NONINTERACTIVE -eq "true") {
# Only show banner once per session, even if this file is dot-sourced multiple times
# Use Get-Variable to avoid strict-mode errors when the variable does not yet exist
$niVar = Get-Variable -Name NonInteractiveMode -Scope Global -ErrorAction SilentlyContinue
$niValue = if ($niVar) { [bool]$niVar.Value } else { $false }
if (-not $niValue) {
Write-Host "[GUI] Non-Interactive mode detected (environment variable)" -ForegroundColor Cyan
}
$global:NonInteractiveMode = $true
}
<#
.SYNOPSIS
Get a value from config for NonInteractive mode
.DESCRIPTION
Retrieves a module-specific config value when running in NonInteractive mode.
Falls back to default if not found.
.PARAMETER Module
The module name (SecurityBaseline, ASR, DNS, Privacy, AntiAI, EdgeHardening, AdvancedSecurity)
.PARAMETER Key
The config key to retrieve
.PARAMETER Default
Default value if key not found
.OUTPUTS
The config value or default
.EXAMPLE
$provider = Get-NonInteractiveValue -Module "DNS" -Key "provider" -Default "Quad9"
#>
function Get-NonInteractiveValue {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Module,
[Parameter(Mandatory = $true)]
[string]$Key,
[Parameter(Mandatory = $false)]
$Default = $null
)
try {
$hasConfig = $null -ne $script:Config
$hasModules = $hasConfig -and ($null -ne $script:Config.modules)
$hasModule = $hasModules -and ($null -ne $script:Config.modules.$Module)
if ($hasModule) {
$moduleConfig = $script:Config.modules.$Module
$hasKey = $null -ne $moduleConfig.$Key
if ($hasKey) {
$value = $moduleConfig.$Key
Write-Log -Level DEBUG -Message "[NonInteractive] $Module.$Key = $value (from config)" -Module "Core"
return $value
}
}
}
catch {
Write-Log -Level WARNING -Message "[NonInteractive] Failed to read $Module.$Key from config: $_" -Module "Core"
}
Write-Log -Level DEBUG -Message "[NonInteractive] $Module.$Key = $Default (default)" -Module "Core"
return $Default
}
<#
.SYNOPSIS
Check if auto-confirm is enabled
.DESCRIPTION
Returns true if autoConfirm or nonInteractive is enabled.
Used for Y/N confirmation prompts that should auto-confirm to Y.
.OUTPUTS
[bool] True if should auto-confirm
#>
function Test-AutoConfirm {
[CmdletBinding()]
[OutputType([bool])]
param()
if ($script:Config -and $script:Config.options) {
if ($script:Config.options.autoConfirm -eq $true) {
return $true
}
if ($script:Config.options.nonInteractive -eq $true) {
return $true
}
}
return $false
}
<#
.SYNOPSIS
Log a NonInteractive mode decision
.DESCRIPTION
Helper to log when a decision was made automatically in NonInteractive mode.
Outputs to both console and log file for transparency.
.PARAMETER Module
The module name
.PARAMETER Decision
Description of the decision made
.PARAMETER Value
The value that was used
#>
function Write-NonInteractiveDecision {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Module,
[Parameter(Mandatory = $true)]
[string]$Decision,
[Parameter(Mandatory = $false)]
$Value = $null
)
$message = if ($null -ne $Value) {
"[GUI] $Decision : $Value"
} else {
"[GUI] $Decision"
}
Write-Host $message -ForegroundColor Cyan
Write-Log -Level INFO -Message $message -Module $Module
}
# Functions are available globally when dot-sourced by Framework.ps1
# No Export-ModuleMember needed (script is not loaded as a module)

2969
Core/Rollback.ps1 Normal file

File diff suppressed because it is too large Load diff

426
Core/Validator.ps1 Normal file
View file

@ -0,0 +1,426 @@
<#
.SYNOPSIS
System validation for NoID Privacy Framework
.DESCRIPTION
Provides pre-execution validation checks and post-execution verification
to ensure system safety and compliance.
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: PowerShell 5.1+
#>
function Test-Prerequisites {
<#
.SYNOPSIS
Validate all system prerequisites before hardening
.OUTPUTS
PSCustomObject with validation results
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param()
Write-Log -Level INFO -Message "Starting prerequisite validation" -Module "Validator"
$result = [PSCustomObject]@{
Success = $true
Errors = @()
Warnings = @()
SystemInfo = $null
}
# Check 1: Administrator privileges
if (-not (Test-IsAdministrator)) {
Write-Log -Level ERROR -Message "Administrator privileges required" -Module "Validator"
$result.Success = $false
$result.Errors += "Administrator privileges required"
}
else {
Write-Log -Level SUCCESS -Message "Administrator check: PASSED" -Module "Validator"
}
# Check 2: Windows version
$osInfo = Get-WindowsVersion
if ($osInfo.IsSupported) {
Write-Log -Level SUCCESS -Message "Windows version check: PASSED ($($osInfo.Version))" -Module "Validator"
}
else {
Write-Log -Level ERROR -Message "Unsupported Windows version: $($osInfo.Version)" -Module "Validator"
$result.Success = $false
$result.Errors += "Unsupported Windows version: $($osInfo.Version)"
}
# Check 3: Disk space
$diskSpace = Get-AvailableDiskSpace
if ($diskSpace -gt 500MB) {
Write-Log -Level SUCCESS -Message "Disk space check: PASSED ($([math]::Round($diskSpace/1MB, 2)) MB available)" -Module "Validator"
}
else {
Write-Log -Level WARNING -Message "Low disk space: $([math]::Round($diskSpace/1MB, 2)) MB" -Module "Validator"
$result.Warnings += "Low disk space: $([math]::Round($diskSpace/1MB, 2)) MB"
}
# Check 4: PowerShell version
if ($PSVersionTable.PSVersion.Major -ge 5) {
Write-Log -Level SUCCESS -Message "PowerShell version check: PASSED ($($PSVersionTable.PSVersion))" -Module "Validator"
}
else {
Write-Log -Level ERROR -Message "PowerShell 5.1 or higher required" -Module "Validator"
$result.Success = $false
$result.Errors += "PowerShell 5.1 or higher required (found: $($PSVersionTable.PSVersion))"
}
# Get system info
$result.SystemInfo = Get-SystemInfo
if ($result.Success) {
Write-Log -Level SUCCESS -Message "All prerequisite checks passed" -Module "Validator"
}
else {
Write-Log -Level ERROR -Message "One or more prerequisite checks failed" -Module "Validator"
}
return $result
}
function Test-IsAdministrator {
<#
.SYNOPSIS
Check if script is running with administrator privileges
.OUTPUTS
Boolean indicating administrator status
#>
[CmdletBinding()]
[OutputType([bool])]
param()
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
return $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Get-WindowsVersion {
<#
.SYNOPSIS
Get Windows version information
.OUTPUTS
PSCustomObject with version details and support status
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param()
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$buildNumber = [int]$os.BuildNumber
# Windows 11 build numbers
# 22000 = 21H2, 22621 = 22H2, 22631 = 23H2, 26100 = 24H2, 26200 = 25H2
$isWindows11 = $buildNumber -ge 22000
$isSupported = $buildNumber -ge 26100 # 24H2 or newer
$versionName = switch ($buildNumber) {
{ $_ -ge 26200 } { "Windows 11 25H2"; break }
{ $_ -ge 26100 } { "Windows 11 24H2"; break }
{ $_ -ge 22631 } { "Windows 11 23H2"; break }
{ $_ -ge 22621 } { "Windows 11 22H2"; break }
{ $_ -ge 22000 } { "Windows 11 21H2"; break }
default { "Windows $($os.Version)" }
}
return [PSCustomObject]@{
Version = $versionName
BuildNumber = $buildNumber
IsWindows11 = $isWindows11
IsSupported = $isSupported
Edition = $os.Caption
Architecture = $os.OSArchitecture
}
}
function Get-AvailableDiskSpace {
<#
.SYNOPSIS
Get available disk space on system drive
.OUTPUTS
Int64 representing available bytes
#>
[CmdletBinding()]
[OutputType([Int64])]
param()
$systemDrive = $env:SystemDrive
$drive = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='$systemDrive'"
return $drive.FreeSpace
}
function Test-InternetConnectivity {
<#
.SYNOPSIS
Test internet connectivity
.OUTPUTS
Boolean indicating connectivity status
#>
[CmdletBinding()]
[OutputType([bool])]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingComputerNameHardcoded', '')]
param()
try {
# Using Google DNS (8.8.8.8) - intentional for internet connectivity check
$response = Test-Connection -ComputerName "8.8.8.8" -Count 1 -Quiet -ErrorAction Stop
return $response
}
catch {
return $false
}
}
function Test-TPMAvailable {
<#
.SYNOPSIS
Check if TPM 2.0 is available
.OUTPUTS
PSCustomObject with TPM information
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param()
try {
$tpm = Get-Tpm -ErrorAction SilentlyContinue
if ($null -eq $tpm) {
return [PSCustomObject]@{
Present = $false
Version = "N/A"
Enabled = $false
Activated = $false
}
}
return [PSCustomObject]@{
Present = $tpm.TpmPresent
Version = if ($tpm.ManufacturerVersion) { $tpm.ManufacturerVersion } else { "2.0" }
Enabled = $tpm.TpmEnabled
Activated = $tpm.TpmActivated
}
}
catch {
Write-Log -Level WARNING -Message "Unable to check TPM status: $_" -Module "Validator"
return [PSCustomObject]@{
Present = $false
Version = "Unknown"
Enabled = $false
Activated = $false
}
}
}
function Test-SecureBootEnabled {
<#
.SYNOPSIS
Check if Secure Boot is enabled
.OUTPUTS
Boolean indicating Secure Boot status
#>
[CmdletBinding()]
[OutputType([bool])]
param()
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
return $secureBoot
}
catch {
Write-Log -Level WARNING -Message "Unable to check Secure Boot status (may not be UEFI): $_" -Module "Validator"
return $false
}
}
function Test-VirtualizationEnabled {
<#
.SYNOPSIS
Check if CPU virtualization is enabled
.OUTPUTS
Boolean indicating virtualization status
#>
[CmdletBinding()]
[OutputType([bool])]
param()
try {
$cpu = Get-CimInstance -ClassName Win32_Processor
# Check for Intel VT-x or AMD-V
$vmxEnabled = $cpu.VirtualizationFirmwareEnabled
return $vmxEnabled
}
catch {
Write-Log -Level WARNING -Message "Unable to check virtualization status: $_" -Module "Validator"
return $false
}
}
function Get-SystemInfo {
<#
.SYNOPSIS
Get comprehensive system information
.OUTPUTS
PSCustomObject with detailed system information
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param()
$osInfo = Get-WindowsVersion
$tpmInfo = Test-TPMAvailable
$secureBoot = Test-SecureBootEnabled
$virtualization = Test-VirtualizationEnabled
$isAdmin = Test-IsAdministrator
$diskSpace = Get-AvailableDiskSpace
$internet = Test-InternetConnectivity
return [PSCustomObject]@{
OS = $osInfo
TPM = $tpmInfo
SecureBoot = $secureBoot
Virtualization = $virtualization
IsAdministrator = $isAdmin
DiskSpaceAvailable = $diskSpace
InternetConnected = $internet
PowerShellVersion = $PSVersionTable.PSVersion.ToString()
}
}
function Test-DomainJoined {
<#
.SYNOPSIS
Check if system is joined to an Active Directory domain
.DESCRIPTION
Detects if the system is domain-joined and warns about potential
Group Policy conflicts with local hardening settings.
.PARAMETER Interactive
If set, prompts user to confirm continuation on domain-joined systems
.OUTPUTS
PSCustomObject with domain status information
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
[switch]$Interactive
)
try {
$computerSystem = Get-CimInstance Win32_ComputerSystem -ErrorAction Stop
$isDomainJoined = $computerSystem.PartOfDomain
$result = [PSCustomObject]@{
IsDomainJoined = $isDomainJoined
DomainName = if ($isDomainJoined) { $computerSystem.Domain } else { "N/A" }
Workgroup = if (-not $isDomainJoined) { $computerSystem.Workgroup } else { "N/A" }
UserConfirmed = $false
}
if ($isDomainJoined) {
Write-Log -Level WARNING -Message "System is domain-joined: $($computerSystem.Domain)" -Module "Validator"
if ($Interactive) {
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " WARNING: DOMAIN-JOINED SYSTEM" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "This system is joined to domain: " -NoNewline -ForegroundColor White
Write-Host "$($computerSystem.Domain)" -ForegroundColor Cyan
Write-Host ""
Write-Host "IMPORTANT CONSIDERATIONS:" -ForegroundColor Red
Write-Host " - Domain Group Policies will override local policies" -ForegroundColor Yellow
Write-Host " - GPO refresh occurs every 90 minutes" -ForegroundColor Yellow
Write-Host " - Some hardening may be reset automatically" -ForegroundColor Yellow
Write-Host " - Coordinate with AD team before proceeding" -ForegroundColor Yellow
Write-Host ""
Write-Host "RECOMMENDED FOR DOMAIN ENVIRONMENTS:" -ForegroundColor Cyan
Write-Host " - Integrate these settings into Domain GPOs instead" -ForegroundColor White
Write-Host " - Use this tool only for testing/standalone systems" -ForegroundColor White
Write-Host ""
$continue = Read-Host "Do you want to continue anyway? (yes/no)"
if ($continue -ne "yes") {
Write-Log -Level INFO -Message "User cancelled due to domain-joined warning" -Module "Validator"
Write-Host ""
Write-Host "Operation cancelled by user." -ForegroundColor Gray
Write-Host ""
exit 1
}
$result.UserConfirmed = $true
Write-Log -Level INFO -Message "User confirmed continuation on domain-joined system" -Module "Validator"
}
}
else {
Write-Log -Level INFO -Message "System is standalone (workgroup: $($computerSystem.Workgroup))" -Module "Validator"
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to check domain status: $_" -Module "Validator" -Exception $_.Exception
return [PSCustomObject]@{
IsDomainJoined = $false
DomainName = "Error"
Workgroup = "Error"
UserConfirmed = $false
}
}
}
function Confirm-SystemBackup {
<#
.SYNOPSIS
Non-interactive system backup recommendation
.DESCRIPTION
Historically this function displayed an interactive prompt asking the
user to confirm that a full system backup exists before proceeding.
For modern CLI and GUI workflows this interaction is removed to avoid
blocking automation. The function now simply logs that a backup is
recommended and returns a confirmation object.
.PARAMETER Force
Retained for backwards compatibility. No longer changes behaviour.
.OUTPUTS
PSCustomObject with backup confirmation status
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param()
Write-Log -Level INFO -Message "Backup recommendation: non-interactive confirmation (no prompt shown)" -Module "Validator"
$result = [PSCustomObject]@{
UserConfirmed = $true
BackupRecommended = $true
}
return $result
}
# Note: Export-ModuleMember not used - this script is dot-sourced, not imported as module

742
Docs/FEATURES.md Normal file
View file

@ -0,0 +1,742 @@
# NoID Privacy - Complete Feature List
**Framework Version:** v2.2.0
**Total Security Settings:** 632 (Paranoid mode)
**Modules:** 7 (All Production-Ready)
**Last Updated:** December 7, 2025
---
## 📊 Module Overview
| Module | Settings | Status | Description |
|--------|----------|--------|-------------|
| **SecurityBaseline** | 425 | ✅ v2.2.0 | Microsoft Security Baseline for Windows 11 v25H2 |
| **ASR** | 19 | ✅ v2.2.0 | Attack Surface Reduction rules |
| **DNS** | 5 | ✅ v2.2.0 | Secure DNS with DoH encryption |
| **Privacy** | 77 | ✅ v2.2.0 | Telemetry control, Bloatware removal (53 Registry + 24 Bloatware) |
| **AntiAI** | 32 | ✅ v2.2.0 | AI lockdown (13 features, 32 compliance checks) |
| **EdgeHardening** | 24 | ✅ v2.2.0 | Microsoft Edge browser security (24 policies) |
| **AdvancedSecurity** | 50 | ✅ v2.2.0 | Advanced hardening beyond MS Baseline (incl. Wireless Display, Discovery Protocols, IPv6) |
| **TOTAL** | **632** | ✅ **100%** | **Complete Framework (Paranoid mode)** |
---
## 🔒 Module 1: SecurityBaseline (425 Settings)
**Description:** Complete implementation of Microsoft's official Windows 11 v25H2 Security Baseline
### Components:
#### Registry Policies (335 settings)
- Computer Configuration policies (330 settings)
- User Configuration policies (5 settings)
- Windows Defender Antivirus baseline
- Windows Firewall configuration
- BitLocker drive encryption settings
- Internet Explorer 11 security zones
#### Security Template (67 settings)
- **Password Policy:** MinimumPasswordLength (14), PasswordHistorySize (24), etc.
- **Account Lockout:** LockoutBadCount (10), LockoutDuration (10 minutes)
- **User Rights Assignment:** Administrative permissions and privileges
- **Security Options:** Network access, authentication, object access
- **Service Configuration:** Xbox services disabled for security
#### Audit Policies (23 subcategories)
- Logon/Logoff events
- Account Management
- Policy Change tracking
- Privilege Use monitoring
- System events
- Object Access auditing
### Key Features:
- ✅ VBS (Virtualization Based Security)
- ✅ Credential Guard
- ✅ System Guard Secure Launch
- ✅ Kernel CET Shadow Stacks (Win11 25H2)
- ✅ Memory Integrity (HVCI)
- ✅ Interactive BitLocker USB prompt (Home/Enterprise choice)
### Home User Adjustments:
- **BitLocker USB:** Default = 0 (Home Mode - USB works normally)
- **Password Policies:** Only affect local accounts (~5% of users)
---
## 🛡️ Module 2: ASR (19 Settings)
**Description:** All 19 Microsoft Defender Attack Surface Reduction rules
### What ASR Rules Block (and Why It's Important):
#### Email & Download Attacks
1. **Block executable content from email** - Stops malware from .exe/.dll/.ps1 email attachments
2. **Block JavaScript/VBScript from launching downloads** - Prevents drive-by downloads from malicious websites
3. **Block execution of obfuscated scripts** - Detects and blocks heavily obfuscated PowerShell/JS scripts used by malware
4. **Block untrusted/unsigned processes from USB** - Prevents USB-based malware execution (BadUSB attacks)
#### Office Exploits
5. **Block Office from creating child processes** - Stops Word/Excel macros from spawning cmd.exe/powershell.exe
6. **Block Office from creating executable content** - Prevents Office from writing .exe files to disk
7. **Block Office from injecting code into other processes** - Stops process injection attacks
8. **Block Win32 API calls from Office macros** - Prevents macros from calling dangerous Windows APIs
9. **Block Adobe Reader from creating child processes** - Same protection for PDF exploits
10. **Block Office communication apps (Outlook) child processes** - Stops email-based exploit chains
#### Credential Theft & Persistence
11. **Block credential stealing from LSASS** - Protects against Mimikatz and similar tools
12. **Block persistence through WMI** - Prevents malware from hiding in WMI event subscriptions
13. **Block process creation from PSExec/WMI** - Stops lateral movement tools (configurable: Block or Audit)
#### Ransomware Protection
14. **Use advanced ransomware protection** - AI-powered behavioral detection of ransomware
15. **Block executable files unless they meet reputation criteria** - SmartScreen integration
#### Advanced Threats
16. **Block abuse of exploited vulnerable signed drivers** - Prevents BYOVD (Bring Your Own Vulnerable Driver) attacks
17. **Block webshell creation** - Stops IIS/Apache webshell deployment (Server-focused)
18. **Block rebooting in Safe Mode** - Prevents ransomware from bypassing defenses
19. **Block use of copied/impersonated system tools** - Detects renamed legitimate tools (rundll32.exe → run.exe)
### Interactive Prompt:
- **PSExec/WMI Rule (d1e49aac):** Choose **Block** or **Audit**
- Block: Maximum security (may break SCCM/remote admin tools)
- Audit: Logs events only (good for enterprise compatibility testing)
---
## 🌐 Module 3: DNS (5 Settings)
**Description:** Secure DNS with DNS-over-HTTPS encryption
### Providers (3 available):
#### Quad9 (Default - Security)
- **IPv4:** 9.9.9.9, 149.112.112.112
- **IPv6:** 2620:fe::fe, 2620:fe::9
- **DoH:** https://dns.quad9.net/dns-query
- **Ratings:** Speed 4/5, Privacy 5/5, Security 5/5, Filtering 4/5
- **Best for:** Security-focused users, malware protection
#### Cloudflare (Speed)
- **IPv4:** 1.1.1.1, 1.0.0.1
- **IPv6:** 2606:4700:4700::1111, 2606:4700:4700::1001
- **DoH:** https://cloudflare-dns.com/dns-query
- **Ratings:** Speed 5/5, Privacy 4/5, Security 4/5, Filtering 2/5
- **Best for:** Speed-focused users, fastest resolver
#### AdGuard (Ad-Blocking)
- **IPv4:** 94.140.14.14, 94.140.15.15
- **IPv6:** 2a10:50c0::ad1:ff, 2a10:50c0::ad2:ff
- **DoH:** https://dns.adguard-dns.com/dns-query
- **Ratings:** Speed 4/5, Privacy 4/5, Security 4/5, Filtering 5/5
- **Best for:** Ad/tracker blocking at DNS level
### Features:
- ✅ **DoH Encryption with 2 Interactive Modes:**
- **[1] REQUIRE Mode (Default):** NO unencrypted fallback (AllowFallbackToUdp = $False)
- Best for: Home networks, single-location systems
- Maximum security - DNS queries always encrypted
- **[2] ALLOW Mode:** Fallback to UDP allowed (AllowFallbackToUdp = $True)
- Best for: VPN users, mobile devices, corporate networks, captive portals
- Balanced security - falls back to unencrypted if DoH unavailable
- **[3] Skip:** Keep current DNS settings unchanged
- ✅ DNSSEC validation (server-side by all providers)
- ✅ DHCP-aware backup/restore
- ✅ Physical adapter auto-detection (excludes virtual/VPN adapters)
- ✅ Connectivity validation before apply
---
## 🔇 Module 4: Privacy (77 Settings)
**Description:** Windows telemetry control, OneDrive/MS Store telemetry, and bloatware removal
### What's Actually Done:
- ✅ **Windows Telemetry:** 3 modes (MSRecommended/Strict/Paranoid)
- ✅ **OneDrive Telemetry:** Feedback & sync reports disabled
- ✅ **OneDrive Sync:** Remains FUNCTIONAL (DisablePersonalSync = 0)
- ✅ **MS Store Telemetry:** AutoDownload = 3 (auto-update apps, no upgrade prompts)
- ✅ **Bloatware Removal:** 10-24+ apps removed (PolicyMethod for ENT/EDU, ClassicMethod for others)
### Operating Modes (Interactive Selection):
#### MSRecommended (Default - Fully Supported)
- AllowTelemetry = 1 (Required)
- Services NOT disabled (policies only)
- AppPrivacy: Selective (Location/Radios Force Deny, Mic/Camera user decides)
- **Best for:** Production, business environments
#### Strict (Maximum Privacy)
- AllowTelemetry = 0 (Off)
- Services: DiagTrack + dmwappushservice disabled
- AppPrivacy: Force Deny Mic/Camera/Contacts/Calendar
- **Warning:** Breaks Teams/Zoom, Windows Update error reporting
- **Best for:** High-security, standalone systems
#### Paranoid (Hardcore - NOT Recommended)
- Everything from Strict + WerSvc disabled
- Tasks: CEIP/AppExperience/DiskDiag disabled
- **Warning:** Breaks error analysis, support severely limited
- **Best for:** Air-gapped, extreme privacy only
### ⚠️ Windows Insider Program Compatibility
**MSRecommended mode** sets `AllowTelemetry=1` via Group Policy, which blocks Windows Insider Program enrollment. The Insider Program requires "Optional diagnostic data" (AllowTelemetry=3) for initial enrollment.
**Workaround:** Temporarily remove the `AllowTelemetry` policy before Insider enrollment:
```powershell
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection" -Name "AllowTelemetry"
```
After enrollment, you can optionally re-apply Privacy hardening. Insider builds will continue to download even with `AllowTelemetry=1` restored.
**See:** [README Troubleshooting - Windows Insider Program Compatibility](../README.md#windows-insider-program-compatibility)
---
### Bloatware Removal:
**PolicyMethod (10 apps - ENT/EDU Win11 25H2+):**
- BingNews, BingWeather, MicrosoftSolitaireCollection
- MicrosoftStickyNotes, GamingApp, WindowsFeedbackHub
- Xbox components (GamingOverlay, IdentityProvider, SpeechToTextOverlay, TCUI)
**ClassicMethod (24 apps - All other editions):**
```
Microsoft.BingNews, Microsoft.BingWeather
Microsoft.MicrosoftSolitaireCollection, Microsoft.MicrosoftStickyNotes
Microsoft.GamingApp, Microsoft.XboxApp
Microsoft.XboxGamingOverlay, Microsoft.XboxIdentityProvider
Microsoft.XboxSpeechToTextOverlay, Microsoft.Xbox.TCUI
Microsoft.ZuneMusic, Microsoft.ZuneVideo
Microsoft.WindowsFeedbackHub, Microsoft.GetHelp
Microsoft.Getstarted, Microsoft.MixedReality.Portal
Microsoft.People, Microsoft.YourPhone
Clipchamp.Clipchamp, SpotifyAB.SpotifyMusic
*CandyCrush*, Disney.*, Facebook.*, TikTok.TikTok
```
### Protected Apps (18 kept):
- **Core Apps:** WindowsStore, WindowsCalculator, Photos, Paint
- **Productivity:** WindowsNotepad, WindowsTerminal, WindowsCamera, ScreenSketch, WindowsSoundRecorder
- **System:** DesktopAppInstaller (winget), StorePurchaseApp
- **Media Codecs:** HEIF, HEVC, WebP, VP9, WebMedia, AV1, MPEG2, RAW (8 extensions)
### OneDrive Settings:
- Telemetry: Disabled
- Sync: Functional (not broken)
- Store: Enabled (app updates needed)
---
## 🤖 Module 5: AntiAI (32 Policies)
**Description:** Disable 13 Windows AI features via 32 registry policies (v2.2.0)
### 13 AI Features Disabled:
| # | Feature | Policies | Description |
|---|---------|----------|-------------|
| 1 | **Generative AI Master Switch** | 2 | Blocks ALL apps from using on-device AI models |
| 2 | **Windows Recall** | 8 | Screenshots, OCR, component removal + Enterprise Protection |
| 3 | **Windows Copilot** | 6 | 4-layer disable: WindowsAI, WindowsCopilot, Taskbar, Explorer |
| 4 | **Click to Do** | 2 | Screenshot AI analysis with action suggestions |
| 5 | **Paint Cocreator** | 1 | Cloud-based text-to-image generation |
| 6 | **Paint Generative Fill** | 1 | AI-powered image editing |
| 7 | **Paint Image Creator** | 1 | DALL-E art generator |
| 8 | **Notepad AI** | 1 | Write, Summarize, Rewrite features (GPT) |
| 9 | **Settings Agent** | 1 | AI-powered Settings search |
| 10 | **Recall Export Block** | 1 | Prevents export of Recall data |
| 11 | **Edge Copilot Sidebar** | 3 | EdgeSidebarEnabled, ShowHubsSidebar, HubsSidebarEnabled |
| 12 | **Edge Copilot Context** | 2 | CopilotPageContext, CopilotCDPPageContext |
| 13 | **File Explorer AI Actions** | 1 | HideAIActionsMenu in Explorer context menu |
### Recall Enterprise Protection:
- **App Deny List:** Browser, Terminal, Password managers, RDP never captured
- **URI Deny List:** Banking (*.bank.*), Email (mail.*), Login pages (*password*, *login*)
- **Storage Duration:** Maximum 30 days retention
- **Storage Space:** Maximum 10 GB allocated
### Automatically Blocked (by Master Switch):
- Photos Generative Erase / Background effects
- Clipchamp Auto Compose
- Snipping Tool AI-OCR / Quick Redact
- All future generative AI apps
### 32 Registry Policies Applied:
```
HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy\LetAppsAccessSystemAIModels = 2
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\systemAIModels\Value = Deny
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\AllowRecallEnablement = 0
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableAIDataAnalysis = 1
HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableAIDataAnalysis = 1
HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableRecallDataProviders = 1
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetDenyAppListForRecall = [...]
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetDenyUriListForRecall = [...]
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetMaximumStorageDurationForRecallSnapshots = 30
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetMaximumStorageSpaceForRecallSnapshots = 10
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\TurnOffWindowsCopilot = 1
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot\TurnOffWindowsCopilot = 1
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot\ShowCopilotButton = 0
HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer\DisableWindowsCopilot = 1
HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot\TurnOffWindowsCopilot = 1
HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot\ShowCopilotButton = 0
HKCU:\Software\Policies\Microsoft\Windows\WindowsAI\SetCopilotHardwareKey = Notepad (redirect)
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableClickToDo = 1
HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableClickToDo = 1
HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint\DisableCocreator = 1
HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint\DisableGenerativeFill = 1
HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint\DisableImageCreator = 1
HKLM:\SOFTWARE\Policies\WindowsNotepad\DisableAIFeatures = 1
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableSettingsAgent = 1
HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\AllowRecallExport = 0
HKLM:\SOFTWARE\Policies\Microsoft\Edge\EdgeSidebarEnabled = 0
HKLM:\SOFTWARE\Policies\Microsoft\Edge\ShowHubsSidebar = 0
HKLM:\SOFTWARE\Policies\Microsoft\Edge\HubsSidebarEnabled = 0
HKLM:\SOFTWARE\Policies\Microsoft\Edge\CopilotPageContext = 0
HKLM:\SOFTWARE\Policies\Microsoft\Edge\CopilotCDPPageContext = 0
```
### Impact:
- ✅ No AI data collection
- ✅ No cloud processing of local data
- ✅ Copilot completely hidden from taskbar and Start menu
- ✅ Edge Copilot sidebar disabled
- ✅ Traditional app experience restored
- ✅ **Reboot required** for Recall component removal
### ⚠️ Known Limitations:
Some UI elements in Paint and Photos apps may **still be visible** but non-functional due to lack of Microsoft-provided policies:
- **Photos:** Generative Erase button, Background Blur/Remove options
- **Paint:** Some AI feature UI elements
**Why?** Microsoft does NOT provide dedicated policies to hide these UI elements. Functionality is **blocked via systemAIModels API Master Switch** (LetAppsAccessSystemAIModels = 2), but UI removal requires Microsoft to add policies in future Windows updates.
**Result:** Buttons are visible but clicking them does nothing (API access blocked).
---
## 🌐 Module 6: EdgeHardening (24 Settings)
**Description:** Microsoft Edge v139 Security Baseline
### Core Security:
- EnhanceSecurityMode = 2 (Strict)
- SmartScreenEnabled = 1
- SmartScreenPuaEnabled = 1
- PreventSmartScreenPromptOverride = 1
- SitePerProcess = 1 (Site isolation)
### Privacy:
- TrackingPrevention = 2 (Strict)
- PersonalizationReportingEnabled = 0
- DiagnosticData = 0
- DoNotTrack = 1
### Security Mitigations:
- SSL/TLS error override blocked
- Extension blocklist (blocks all by default)
- IE Mode restrictions
- SharedArrayBuffer disabled (Spectre protection)
- Application-bound encryption enabled
### Features:
- ✅ Native PowerShell implementation (no LGPO.exe)
- ✅ AllowExtensions parameter available
- ✅ Full backup/restore support
---
## 🔐 Module 7: AdvancedSecurity (50 Settings)
**Description:** Advanced hardening beyond Microsoft Security Baseline
### Profile-Based Execution:
| Feature | Balanced | Enterprise | Maximum |
|---------|------|------------|-----------|
| RDP NLA Enforcement | ✅ | ✅ | ✅ |
| WDigest Protection | ✅ | ✅ | ✅ |
| Risky Ports/Services | ✅ | ✅ | ✅ |
| Legacy TLS Disable | ✅ | ✅ | ✅ |
| WPAD Disable | ✅ | ✅ | ✅ |
| PowerShell v2 Removal | ✅ | ✅ | ✅ |
| Admin Shares Disable | ✅ | ⚠️ Domain Check | ✅ |
| RDP Complete Disable | ⚠️ Optional | ❌ | ✅ |
| UPnP/SSDP Block | ⚠️ Optional | ✅ | ✅ |
| Wireless Display Hardening | ✅ | ✅ | ✅ |
| Wireless Display Full Disable | ⚠️ Optional | ⚠️ Optional | ⚠️ Optional |
| Discovery Protocols (WSD/mDNS) Disable | ❌ | ❌ | ⚠️ Optional |
| Firewall Shields Up | ❌ | ❌ | ⚠️ Optional |
| IPv6 Disable (mitm6 mitigation) | ❌ | ❌ | ⚠️ Optional |
| SRP .lnk Protection | ✅ | ✅ | ✅ |
| Windows Update Config | ✅ | ✅ | ✅ |
| Finger Protocol Block | ✅ | ✅ | ✅ |
### Components:
#### 1. RDP Hardening (3 settings)
- **NLA Enforcement:** UserAuthentication = 1, SecurityLayer = 2
- **Optional Disable:** fDenyTSConnections = 1 (Maximum profile only, for air-gapped systems)
- **Protection:** Prevents RDP brute-force attacks
#### 2. WDigest Credential Protection (1 setting)
- **Registry:** UseLogonCredential = 0
- **Protection:** Prevents LSASS memory credential theft (Mimikatz)
- **Note:** Deprecated in Win11 24H2+ but kept for backwards compatibility
#### 3. Risky Ports Closure (15 firewall rules)
- **LLMNR:** Port 5355 TCP/UDP (MITM attack prevention)
- **NetBIOS:** Ports 137-138 TCP/UDP (name resolution hijacking)
- **UPnP:** Ports 1900, 2869 TCP/UDP (NAT traversal exploits)
#### 4. Risky Services (3 services)
- **SSDP Discovery:** Disabled (UPnP)
- **UPnP Device Host:** Disabled
- **TCP/IP NetBIOS Helper:** Disabled
#### 5. Administrative Shares (2 registry keys)
- **AutoShareWks = 0:** Disables C$, ADMIN$
- **AutoShareServer = 0:** Server shares
- **Domain-Aware:** Auto-skipped for domain-joined systems unless -Force
#### 6. Legacy TLS Disable (8 registry keys)
- **TLS 1.0:** Client + Server disabled
- **TLS 1.1:** Client + Server disabled
- **Protection:** BEAST, CRIME, POODLE attacks prevented
#### 7. WPAD Disable (3 registry keys)
- **User + Machine:** AutoDetect = 0
- **WinHTTP:** DisableWpad = 1
- **Protection:** Proxy hijacking attacks prevented
#### 8. PowerShell v2 Removal (1 Windows Feature)
- **Feature:** MicrosoftWindowsPowerShellV2Root
- **Protection:** Prevents downgrade attacks (bypasses logging, AMSI, CLM)
#### 9. SRP .lnk Protection - CVE-2025-9491 (2 rules)
- **Rule 1:** Block %LOCALAPPDATA%\Temp\*.lnk (Outlook attachments)
- **Rule 2:** Block %USERPROFILE%\Downloads\*.lnk (Browser downloads)
- **Protection:** Prevents zero-day LNK RCE exploitation
- **Status:** CRITICAL - Actively exploited since 2017, no patch available
#### 10. Windows Update Configuration (3 Simple GUI Settings)
**Aligns with Windows Settings GUI toggles** NO forced schedules, NO auto-reboot, and only the documented policy keys needed to drive the visible switches
**Settings Applied:**
**1. Get Latest Updates Immediately (ON, managed by policy)**
- Registry: `HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate`
- Keys:
- `AllowOptionalContent = 1`
- `SetAllowOptionalContent = 1`
- Effect: Enables optional/content configuration updates so the toggle "Get the latest updates as soon as they're available" is effectively ON and enforced by policy
- GUI Path: Settings > Windows Update > Advanced options > Get the latest updates as soon as they're available (will show as managed by your organization)
**2. Microsoft Update for Other Products (ON, user-toggleable)**
- Registry: `HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings`
- Key: `AllowMUUpdateService = 1`
- Effect: Get updates for Office, drivers, and other Microsoft products when updating Windows
- GUI Path: Settings > Windows Update > Advanced options > Receive updates for other Microsoft products (user can still toggle)
**3. Delivery Optimization - Downloads from Other Devices (OFF, managed by policy)**
- Registry: `HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization`
- Key: `DODownloadMode = 0`
- Effect: HTTP only (Microsoft servers) no peer-to-peer, no LAN sharing
- GUI Path: Settings > Windows Update > Advanced options > Delivery Optimization > Allow downloads from other devices = OFF (managed by your organization)
**User Control & Transparency:**
- ✅ NO forced installation schedules
- ✅ NO auto-reboot policies
- ✅ Microsoft Update toggle remains user-controlled in the GUI
- ✅ Windows clearly indicates where policies manage settings ("Some settings are managed by your organization")
**Why This Approach?**
- Follows Microsoft Best Practice - matches GUI behavior
- User keeps control over installation timing
- No unexpected reboots at 3 AM
- Transparent - exactly what Windows Settings shows
#### 11. Finger Protocol Block (1 firewall rule)
- **Port:** TCP 79 outbound
- **Protection:** ClickFix malware campaign mitigation
- **Attack:** Malware uses finger.exe to retrieve commands from attacker servers
- **Impact:** Zero (Finger protocol obsolete since 1990s)
#### 12. Wireless Display Security (9 settings)
**Default Hardening (always applied, all profiles):**
- **AllowProjectionToPC = 0:** Block receiving projections (PC can't be used as display)
- **RequirePinForPairing = 2:** Always require PIN for pairing
**Optional Full Disable (user choice):**
- **AllowProjectionFromPC = 0:** Block sending projections
- **AllowMdnsAdvertisement = 0:** Don't advertise as receiver via mDNS
- **AllowMdnsDiscovery = 0:** Don't discover displays via mDNS
- **AllowProjectionFromPCOverInfrastructure = 0:** Block infrastructure projection
- **AllowProjectionToPCOverInfrastructure = 0:** Block infrastructure receiving
- **Firewall Rules:** Block Miracast ports 7236/7250 (TCP + UDP)
**Protection:**
- Prevents rogue Miracast receiver attacks (screen capture by attackers in network)
- Blocks WPS PIN brute-force on Miracast connections
- Prevents mDNS spoofing for fake display discovery
- Defense-in-depth for Miracast attack surface
**Impact:**
- Default: Presentations to TV/projector still work (sending allowed)
- Full Disable: Use HDMI/USB-C cables instead of Miracast
#### 13. Discovery Protocols Security (WS-Discovery + mDNS)
**Optional (Maximum profile - user choice):**
- **mDNS Resolver:** Disabled via registry (EnableMDNS = 0)
- **WS-Discovery Services:** FDResPub + SSDPSRV disabled
- **Firewall Blocks:**
- WS-Discovery ports: UDP 3702 (blocked inbound/outbound)
- mDNS port: UDP 5353 (blocked inbound/outbound)
**Protection:**
- Prevents network mapping via WS-Discovery
- Blocks mDNS spoofing attacks (fake printers/devices)
- Reduces lateral movement attack surface
- Stops automatic device enumeration by attackers
**Impact:**
- Automatic network printer/scanner discovery stops
- Smart TV discovery via mDNS stops
- Miracast discovery via mDNS stops (even if Feature 12 allows sending)
- Manual IP configuration required for network devices
#### 14. Firewall Shields Up (Maximum profile only)
**Optional (Maximum profile):**
- **Block All Inbound:** DefaultInboundAction = Block
- **Block All Outbound:** DefaultOutboundAction = Block (with exceptions)
- Applies to Domain, Private, and Public profiles
**Protection:**
- Maximum network isolation
- Blocks all unsolicited inbound connections
- Prevents unauthorized outbound connections
**Impact:**
- Only explicitly allowed traffic passes
- Recommended for air-gapped or high-security systems
#### 15. IPv6 Disable (Maximum profile only - optional)
**Optional (Maximum profile - user choice):**
- **DisabledComponents = 0xFF:** Completely disables IPv6 stack
- Prevents all IPv6 traffic including DHCPv6 Solicitation
**Protection (mitm6 attack):**
- Prevents DHCPv6 spoofing attacks
- Blocks fake DHCPv6 server → DNS takeover
- Prevents NTLM credential relay via IPv6
- Defense-in-depth (WPAD already disabled)
**Impact:**
- IPv6-only services/websites won't work
- Exchange Server may have issues if using IPv6
- Some Active Directory features may be affected
- **REBOOT REQUIRED**
**Recommended for:**
- Air-gapped systems
- Standalone workstations (no Exchange/AD)
- High-security environments where IPv6 is not needed
---
## 🎯 Protection Coverage
### Zero-Day Vulnerabilities:
#### CVE-2025-9491 - Windows LNK RCE ✅ MITIGATED
- **Status:** Unpatched (Microsoft: "does not meet servicing threshold")
- **Exploited Since:** 2017 by APT groups
- **Our Protection:** SRP rules block .lnk execution from Temp/Downloads
- **Why ASR Fails:** .lnk files not classified as "executable content"
- **Why SmartScreen Fails:** .lnk points to legitimate cmd.exe (trusted)
#### ClickFix Malware Campaign ✅ MITIGATED
- **Attack Vector:** finger.exe abuse to retrieve malicious commands
- **Our Protection:** Outbound TCP port 79 blocked
- **Impact:** Zero (legacy protocol unused in 2025)
### Attack Surface Reduction:
| Attack Type | Protection |
|-------------|-----------|
| **Email Malware** | ASR: Block executables from email |
| **USB Malware** | ASR: Block untrusted USB processes |
| **Office Macros** | ASR: Block Win32 API calls |
| **Credential Theft** | ASR: Block LSASS access + WDigest disabled |
| **Ransomware** | ASR: Advanced ransomware protection |
| **MITM Attacks** | DNS DoH + LLMNR/NetBIOS disabled |
| **RDP Brute-Force** | NLA enforcement + optional disable |
| **Proxy Hijacking** | WPAD disabled |
| **TLS Exploits** | TLS 1.0/1.1 disabled (BEAST/CRIME) |
| **PowerShell Downgrade** | PSv2 removed |
| **DMA Attacks** | FireWire (IEEE 1394) blocked |
---
## 📋 Interactive Features
### User Prompts (13 Total):
#### SecurityBaseline (1 prompt):
1. **BitLocker USB Policy** (Home/Enterprise)
- Home Mode: USB works normally (no encryption enforcement)
- Enterprise Mode: Require BitLocker encryption on USB drives
#### ASR (2 prompts):
2. **PSExec/WMI rule mode** (Block/Audit)
- Block: Maximum security (may break SCCM/remote admin)
- Audit: Log only (compatibility testing)
3. **New Software rule mode** (Block/Audit)
- Block: Block executables that don't meet prevalence criteria
- Audit: Log only (recommended for new software installs)
#### DNS (2 prompts):
4. **Provider selection** (Quad9/Cloudflare/AdGuard/Skip)
- 3 DNS providers available with ratings
- Skip option to keep current DNS
5. **DoH Mode selection** (REQUIRE/ALLOW/Skip)
- REQUIRE: No unencrypted fallback (maximum security)
- ALLOW: Fallback to UDP if needed (VPN/corporate/mobile)
- Skip: Keep current DNS settings
#### Privacy (3 prompts):
6. **Mode selection** (MSRecommended/Strict/Paranoid)
- MSRecommended: Fully supported, production-safe
- Strict: Maximum privacy (may break Teams/Zoom)
- Paranoid: Extreme privacy (very limited support)
7. **Cloud Clipboard** (Enable/Disable) - *only in MSRecommended mode*
- Disable: No cross-device clipboard sync (privacy)
- Enable: Keep cloud clipboard functionality
8. **Bloatware Removal** (Yes/No)
- Yes: Remove 10-24 pre-installed apps
- No: Keep all apps installed
#### AdvancedSecurity (5 prompts):
9. **Profile selection** (Balanced/Enterprise/Maximum)
- Balanced: Safe defaults for home users
- Enterprise: Domain-aware checks
- Maximum: Maximum hardening
10. **RDP Disable** (Yes/No) - *Balanced profile only, Maximum always disables*
- Yes: Completely disable Remote Desktop
- No: Keep RDP enabled (with NLA hardening)
11. **UPnP/SSDP Block** (Yes/No) - *Balanced profile only, others always block*
- Yes: Block UPnP/SSDP (may break DLNA streaming)
- No: Keep UPnP enabled
12. **Wireless Display Disable** (Yes/No) - *all profiles*
- Yes: Completely disable Miracast (use HDMI instead)
- No: Keep Miracast hardened but usable
13. **Admin Shares Disable** (Yes/No) - *Domain-joined systems only*
- Yes: Disable C$/ADMIN$ even on domain (may break IT tools)
- No: Keep admin shares for IT management (SCCM, PDQ, etc.)
### Backup & Restore:
- ✅ Session-based backup system (Initialize-BackupSystem)
- ✅ Full registry backup before changes
- ✅ Service state backup
- ✅ Feature state backup
- ✅ DHCP settings backup (DNS module)
- ✅ Restore capability for all modules
### Verification:
- ✅ Test-BaselineCompliance (SecurityBaseline)
- ✅ Test-ASRCompliance (ASR)
- ✅ Test-DNSConnectivity (DNS)
- ✅ Test-AntiAI (AntiAI)
- ✅ Test-PrivacyCompliance (Privacy)
- ✅ Test-EdgeHardening (EdgeHardening)
- ✅ Test-AdvancedSecurity (AdvancedSecurity)
---
## 🔧 Safety Features
### Pre-Flight Checks:
- ✅ Administrator elevation required
- ✅ OS version detection (Windows 11 24H2+)
- ✅ Hardware capability detection (TPM, VBS)
- ✅ Domain-joined system detection
### Execution Safety:
- ✅ WhatIf mode (dry-run preview)
- ✅ Profile-based execution (Balanced/Enterprise/Maximum)
- ✅ Incremental backups
- ✅ Error handling with graceful degradation
- ✅ Comprehensive logging
### Rollback:
- ✅ Restore-SecurityBaseline
- ✅ Restore-DNSSettings
- ✅ Restore-PrivacySettings
- ✅ Restore-AdvancedSecuritySettings
---
## 📊 Home User Friendly
### Password Policies (Low Impact):
- ✅ Only affect local accounts (~5% of home users)
- ✅ 95%+ use Microsoft Accounts (managed online by Microsoft)
- ✅ Policies: MinimumPasswordLength (14), PasswordHistory (24), Lockout (10)
### BitLocker USB (User Choice):
- ✅ Default: Home Mode (USB works normally)
- ✅ Option: Enterprise Mode (encryption enforcement)
- ✅ Interactive prompt during SecurityBaseline
### FireWire Blocking:
- ✅ Blocks IEEE 1394 devices (DMA attack prevention)
- ✅ Impact: <1% of users (obsolete technology)
---
## 🎉 Framework Status
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NoID Privacy v2.2.0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total Settings: 632 ✅
Modules: 7/7 (100%) ✅
Production Status: Ready ✅
Verification: 100% ✅
BACKUP-APPLY-VERIFY-RESTORE: Complete ✅
Zero-Day Protection: ✅ CVE-2025-9491 + ClickFix
Microsoft Best Practices: 100% ✅
Home User Friendly: ✅ Interactive prompts
Enterprise Ready: ✅ Profile-based execution
Framework Completion: 🎉 100% COMPLETE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
---
**Last Updated:** December 7, 2025
**Framework Version:** v2.2.0

86
Docs/LICENSE-HISTORY.md Normal file
View file

@ -0,0 +1,86 @@
# License History
This document tracks the license history of NoID Privacy.
---
## Current License (v2.x Series)
**GNU General Public License v3.0** (GPL-3.0)
**Effective:** November 20, 2025
**Applies to:** All v2.x releases (v2.0.0 and later)
See [LICENSE](LICENSE) for full text.
---
## Previous License (v1.0.0 - v1.8.3)
**MIT License**
**Effective:** 2024 - November 13, 2025
**Applies to:** All v1.x releases (v1.0.0 through v1.8.3)
### Why the License Changed
**Reason for Change:**
- Version 2.0+ represents a **complete rewrite** from scratch
- 100% new codebase with modular architecture
- Zero code from v1.x carried forward
- New author retains full copyright over v2.x codebase
**Legal Basis:**
- As the sole copyright holder of v2.x code, relicensing is permitted
- MIT License (v1.x) allows derivative works under different licenses
- No external contributors' rights are affected (complete rewrite)
**Impact:**
- **v1.8.3 and earlier:** Remain under MIT License (cannot be changed retroactively)
- **v2.2.0 and later:** Licensed under GPL v3.0
- Forks of v1.x can remain MIT-licensed
- Forks of v2.x must comply with GPL v3.0
---
## MIT License Text (v1.x)
```
MIT License
Copyright (c) 2024-2025 NexusOne23
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
---
## Dual-Licensing (v2.x+)
Version 2.x+ is available under dual-licensing:
1. **GPL v3.0** (Open Source) - Free for open-source projects
2. **Commercial License** - For closed-source commercial use
Contact via [GitHub Discussions](https://github.com/NexusOne23/noid-privacy/discussions) for commercial licensing inquiries.
---
## Questions?
If you have questions about licensing, please open a discussion on GitHub:
https://github.com/NexusOne23/noid-privacy/discussions

467
Docs/NONINTERACTIVE-MODE.md Normal file
View file

@ -0,0 +1,467 @@
# NonInteractive Mode - CI/CD & Automation Guide
## Overview
NoID Privacy supports fully automated, non-interactive execution for CI/CD pipelines, group policy deployment, and mass system hardening. This guide explains how to run the framework without any interactive prompts.
---
## Configuration-Based Execution
The framework automatically enters non-interactive mode when all required parameters are pre-configured in `config.json`.
### Required Configuration Keys
#### **1. DNS Module - Provider Selection**
```json
{
"modules": {
"DNS": {
"enabled": true,
"priority": 3,
"status": "IMPLEMENTED",
"description": "Secure DNS with DoH",
"provider": "Quad9"
}
}
}
```
**Valid provider values:**
- `"Quad9"` (default, security-focused, Swiss privacy)
- `"Cloudflare"` (fastest resolver)
- `"AdGuard"` (ad/tracker blocking)
**When provider is set:**
- No interactive DNS provider selection prompt
- Direct application of specified provider
---
#### **2. Privacy Module - Mode Selection**
```json
{
"modules": {
"Privacy": {
"enabled": true,
"priority": 4,
"status": "IMPLEMENTED",
"description": "Privacy hardening",
"mode": "MSRecommended"
}
}
}
```
**Valid mode values:**
- `"MSRecommended"` (default, fully supported, production-ready)
- `"Strict"` (maximum privacy, breaks some apps)
- `"Paranoid"` (hardcore, not recommended for production)
**When mode is set:**
- No interactive privacy mode selection prompt
- Direct application of specified mode with warnings logged
---
#### **3. Global Options - Automation Settings**
```json
{
"options": {
"dryRun": false,
"createBackup": true,
"verboseLogging": false,
"autoReboot": false,
"nonInteractive": true
}
}
```
**Key options:**
- `nonInteractive`: Explicitly disable all prompts (optional, auto-detected)
- `autoReboot`: Automatically restart after hardening (use with caution)
- `createBackup`: Always create backups (highly recommended)
---
## Command-Line Execution
### **Basic Non-Interactive Execution**
```powershell
# Run all enabled modules from config.json
.\NoIDPrivacy.ps1 -Module All
# Run specific module with provider pre-configured
.\NoIDPrivacy.ps1 -Module DNS
# Run with command-line overrides
.\NoIDPrivacy.ps1 -Module Privacy -DryRun
# Run in verbose mode for logging
.\NoIDPrivacy.ps1 -Module All -VerboseLogging
```
---
### **CI/CD Pipeline Example**
#### **Azure DevOps Pipeline**
```yaml
steps:
- task: PowerShell@2
displayName: 'NoID Privacy Hardening'
inputs:
targetType: 'filePath'
filePath: '$(System.DefaultWorkingDirectory)/NoIDPrivacy.ps1'
arguments: '-Module All -VerboseLogging'
errorActionPreference: 'stop'
pwsh: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish Hardening Logs'
inputs:
PathtoPublish: 'Logs'
ArtifactName: 'hardening-logs'
```
#### **GitHub Actions Workflow**
```yaml
name: Windows Hardening
on:
schedule:
- cron: '0 0 * * 0' # Weekly on Sunday
workflow_dispatch:
jobs:
harden:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Run NoID Privacy
shell: powershell
run: |
.\NoIDPrivacy.ps1 -Module All -VerboseLogging
- name: Upload Logs
if: always()
uses: actions/upload-artifact@v3
with:
name: hardening-logs
path: Logs/
```
#### **Jenkins Pipeline**
```groovy
pipeline {
agent { label 'windows' }
stages {
stage('Hardening') {
steps {
powershell '''
Set-ExecutionPolicy Bypass -Scope Process -Force
.\\NoIDPrivacy.ps1 -Module All -VerboseLogging
'''
}
}
}
post {
always {
archiveArtifacts artifacts: 'Logs/**/*', fingerprint: true
}
}
}
```
---
## Group Policy Deployment
### **Method 1: Startup Script**
1. Copy NoID Privacy to network share:
```
\\domain.local\NETLOGON\NoIDPrivacy\
```
2. Create GPO startup script:
```powershell
# Startup-Hardening.ps1
$scriptPath = "\\domain.local\NETLOGON\NoIDPrivacy\NoIDPrivacy.ps1"
if (Test-Path $scriptPath) {
& $scriptPath -Module All -VerboseLogging
}
```
3. Link GPO to target OU
4. Result logged to: `C:\NoIDPrivacy\Logs\`
---
### **Method 2: Scheduled Task (Recommended)**
Deploy via GPO Scheduled Task:
```xml
<!-- Task XML for GPO deployment -->
<Task>
<Triggers>
<CalendarTrigger>
<StartBoundary>2025-01-01T03:00:00</StartBoundary>
<ScheduleByWeek>
<DaysOfWeek><Sunday /></DaysOfWeek>
<WeeksInterval>1</WeeksInterval>
</ScheduleByWeek>
</CalendarTrigger>
</Triggers>
<Actions>
<Exec>
<Command>powershell.exe</Command>
<Arguments>-ExecutionPolicy Bypass -File "\\domain.local\NETLOGON\NoIDPrivacy\NoIDPrivacy.ps1" -Module All</Arguments>
</Exec>
</Actions>
</Task>
```
---
## Verification Without Interaction
### **Silent Verification**
```powershell
# Run verification and export structured JSON
.\Tools\Verify-Complete-Hardening.ps1 -ExportPath "verification-result.json"
# Parse results programmatically
$verification = Get-Content "verification-result.json" | ConvertFrom-Json
if ($verification.Failed -eq 0) {
Write-Output "All settings verified successfully"
exit 0
} else {
Write-Error "Verification failed: $($verification.Failed) settings did not match expected values"
exit 1
}
```
---
## Environment Variables (Alternative)
Instead of modifying `config.json`, use environment variables:
```powershell
# Set environment variables
$env:NOIDPRIVACY_DNS_PROVIDER = "Quad9"
$env:NOIDPRIVACY_PRIVACY_MODE = "MSRecommended"
$env:NOIDPRIVACY_NONINTERACTIVE = "true"
# Run framework
.\NoIDPrivacy.ps1 -Module All
```
**Note:** Environment variables require framework support and are currently a roadmap feature (not yet implemented).
---
## Return Codes
**Note:** Exit codes are currently not implemented. Error handling should be done via try/catch blocks and checking the log files.
### **Example: Error Handling in Scripts**
```powershell
try {
.\NoIDPrivacy.ps1 -Module All -ErrorAction Stop
Write-Output "Hardening completed successfully"
}
catch {
Write-Error "Hardening failed: $_"
# Check logs for details
$latestLog = Get-ChildItem "Logs" -Filter "NoIDPrivacy-*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-Content $latestLog.FullName | Select-String "ERROR"
exit 1
}
```
---
## Best Practices for Automation
### **1. Always Use DryRun First**
```powershell
# Test configuration without applying
.\NoIDPrivacy.ps1 -Module All -DryRun -VerboseLogging
# Review logs before production run
Get-Content "Logs\NoIDPrivacy-*.log" | Select-String "ERROR|WARNING"
```
---
### **2. Centralized Logging**
Configure log aggregation for enterprise deployment:
```powershell
# Example: Copy logs to central location
$logPath = "C:\NoIDPrivacy\Logs"
$centralPath = "\\fileserver\HardeningLogs\$env:COMPUTERNAME"
if (Test-Path $logPath) {
Copy-Item -Path "$logPath\*" -Destination $centralPath -Recurse -Force
}
```
---
### **3. Rollback Plan**
Always maintain rollback capability:
```powershell
# Before mass deployment, test rollback
.\NoIDPrivacy.ps1 -Module DNS
# Restore from latest backup (uses Core\Rollback.ps1)
.\Core\Rollback.ps1 -RestoreLatest
# Or restore specific module
.\Modules\DNS\Public\Restore-DNSSettings.ps1
# Verify rollback worked
.\Tools\Verify-Complete-Hardening.ps1
```
---
## Troubleshooting Non-Interactive Mode
### **Issue: Still Showing Prompts**
**Cause:** Provider/mode not configured in `config.json`
**Solution:**
```json
{
"modules": {
"DNS": { "provider": "Quad9" },
"Privacy": { "mode": "MSRecommended" }
}
}
```
---
### **Issue: Script Fails Silently**
**Cause:** Error suppression in CI/CD
**Solution:**
```powershell
# Use verbose logging + error action
.\NoIDPrivacy.ps1 -Module All -VerboseLogging -ErrorAction Stop
```
---
### **Issue: Insufficient Permissions**
**Cause:** Not running as Administrator
**Solution:**
```powershell
# For scheduled tasks, use SYSTEM account or admin user
# For GPO, startup scripts run as SYSTEM automatically
```
---
## Complete Example: Enterprise Deployment Script
```powershell
<#
.SYNOPSIS
Enterprise deployment wrapper for NoID Privacy
.DESCRIPTION
Automated hardening with centralized logging and email reporting
#>
param(
[switch]$DryRun,
[string]$EmailRecipient = "security@company.com"
)
$ErrorActionPreference = "Stop"
$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
try {
# Pre-flight checks
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw "Must run as Administrator"
}
# Run hardening
Write-Output "Starting NoID Privacy hardening..."
$result = & "$scriptRoot\NoIDPrivacy.ps1" -Module All -DryRun:$DryRun -VerboseLogging
# Collect logs
$logPath = "$scriptRoot\Logs"
$latestLog = Get-ChildItem $logPath -Filter "NoIDPrivacy-*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
# Send report email
$emailBody = @"
NoID Privacy Hardening Report
Computer: $env:COMPUTERNAME
Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Mode: $(if($DryRun){"DRY RUN"}else{"APPLY"})
Log file attached.
"@
Send-MailMessage -To $EmailRecipient `
-From "hardening@company.com" `
-Subject "Hardening Report - $env:COMPUTERNAME" `
-Body $emailBody `
-Attachments $latestLog.FullName `
-SmtpServer "smtp.company.com"
Write-Output "Hardening completed successfully"
exit 0
}
catch {
Write-Error "Hardening failed: $_"
exit 1
}
```
---
## Summary
**For non-interactive execution:**
1. ✅ Configure `provider` and `mode` in `config.json`
2. ✅ Use `-Module All` parameter
3. ✅ Enable `-VerboseLogging` for CI/CD
4. ✅ Always test with `-DryRun` first
5. ✅ Implement centralized logging
6. ✅ Plan rollback procedures
**The framework is fully automation-ready when configured correctly!**

105
Docs/SECURITY-ANALYSIS.md Normal file
View file

@ -0,0 +1,105 @@
# 🛡️ Security Impact Analysis for Home Users
**Understanding how Enterprise Security affects your Home PC**
This document explains the impact of applying the **Microsoft Security Baseline** (designed for Enterprise) to a standalone **Windows 11 Home/Pro** workstation.
> **Executive Summary:**
> 98% of the settings improve security without visible impact. The remaining 2% (BitLocker, Password Policy) have been adjusted or documented to ensure usability for home users.
---
## 1. Password Policies
**Setting:** `MinimumPasswordLength = 14`, `PasswordHistory = 24`
### 🏠 Home User Impact: **Low / None**
- **Microsoft Accounts:** These policies DO NOT affect your Microsoft Account (Outlook/Live/Hotmail) login. Microsoft manages those policies in the cloud.
- **Local Accounts:** If you use a local "Offline" account, you will be forced to set a 14-character password next time you change it.
- **PIN / FaceID:** Unaffected. You can still use Windows Hello PIN (4-6 digits) to sign in. The complex password is only for the underlying account.
**Recommendation:** Use a password manager generated password for your local account, and use PIN for daily login.
---
## 2. BitLocker USB Protection
**Setting:** `DenyWriteAccessOnFixedDrivesIfNotProtected` (Registry Policy)
### 🏠 Home User Impact: **High (if enabled)**
- **Enterprise Default:** Windows blocks writing to ANY USB drive unless it is encrypted with BitLocker.
- **NoID Privacy Default:** **DISABLED (Home Mode)**.
- **Why?** Home users often share USB sticks with TVs, cars, or friends (Mac/Linux). Enforcing BitLocker makes the drive unreadable on non-Windows devices.
**Your Choice:**
The tool asks you interactively:
- **[N] No (Default):** USB drives work normally. Safe for home use.
- **[Y] Yes:** Maximum security. USB drives are Read-Only until you encrypt them.
---
## 3. FireWire (IEEE 1394) Blocking
**Setting:** DMA Protection / Device Installation Restrictions
### 🏠 Home User Impact: **Near Zero**
- **What is it?** An obsolete connection standard (pre-USB 3.0) used by old camcorders.
- **Why block it?** Vulnerable to Direct Memory Access (DMA) attacks where an attacker plugs a device in and steals RAM content (passwords/keys) in seconds.
- **Reality:** Most modern PCs don't even have FireWire ports.
**Workaround:**
If you absolutely need to transfer video from a 2005 camcorder:
```powershell
# Run as Admin to temporarily allow
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeviceInstall\Restrictions" -Name "DenyDeviceClasses" -Value 0
```
---
## 4. Attack Surface Reduction (ASR)
**Setting:** 19 Defender Rules (Block Mode)
### 🏠 Home User Impact: **Low**
- **Blocked:** running `.exe` files directly from an email attachment (Outlook).
- **Solution:** Save the file to Downloads folder first, then run it. This simple friction stops 90% of malware.
- **Blocked:** Office Macros downloading files.
- **Solution:** Don't enable macros in documents from unknown sources.
**PSExec / WMI Rule:**
- Enterprise admin tools used for remote management.
- Home users don't use these. Blocking them stops malware lateral movement.
- **Safe to Block.**
---
## 5. App Compatibility
### Known Issues
- **Legacy Games (Pre-2010):** Some old games require **DirectPlay** or **SMBv1**.
- *NoID Privacy* disables SMBv1 (WannaCry ransomware vector).
- **Network Scanners:** Old Canon/HP printers might use SMBv1 for "Scan to Folder".
- *Solution:* Use "Scan to Email" or update printer firmware.
- **Cheater Software:** Some game hacks/trainers inject code into processes. ASR rules will block this.
### Troubleshooting
If an app fails to launch:
1. Check `Windows Security` > `Protection History`.
2. It will show if an **ASR Rule** or **Controlled Folder Access** blocked it.
---
## 6. AI & Privacy
**Setting:** Recall & Copilot disabled, Telemetry minimized (Security-Essential level)
### 🏠 Home User Impact: **Positive**
- **Performance:** Less background activity (indexing, analyzing).
- **Privacy:** Screenshots (Recall) are not taken.
- **Experience:** Start Menu and Taskbar are cleaner (no Copilot ads).
- **Functionality:** Paint/Notepad AI features (Cocreator) will be disabled. If you pay for Copilot Pro, you might want to skip the **AntiAI** module.
---
**Conclusion:**
NoID Privacy transforms a "leaky" Home edition into an "Enterprise Fortress" for everyday use, ohne die Fähigkeit zu verlieren, Spiele zu spielen oder normal zu surfen. Die wenigen Reibungspunkte (USB, Makros, alte Protokolle) sind bewusst gesetzte Sicherheitstore.

697
LICENSE Normal file
View file

@ -0,0 +1,697 @@
 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================================================
APPLICATION TO NoID Privacy:
NoID Privacy
Copyright (C) 2025 NexusOne23
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing options (dual-licensing), please see Docs/LICENSE-HISTORY.md
or contact via GitHub Discussions at https://github.com/NexusOne23/noid-privacy/discussions

39
Modules/ASR/ASR.psd1 Normal file
View file

@ -0,0 +1,39 @@
@{
RootModule = 'ASR.psm1'
ModuleVersion = '2.2.0'
GUID = 'b2c3d4e5-f6a7-8901-bcde-f23456789012'
Author = 'NexusOne23'
CompanyName = 'Open Source Project'
Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.'
Description = 'Attack Surface Reduction (ASR) - All 19 Microsoft Defender ASR rules in Block mode for maximum protection against modern threats'
PowerShellVersion = '5.1'
RequiredModules = @()
FunctionsToExport = @(
'Invoke-ASRRules'
)
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
PrivateData = @{
PSData = @{
Tags = @('Security', 'ASR', 'AttackSurfaceReduction', 'Defender', 'Windows11', 'Ransomware')
LicenseUri = ''
ProjectUri = ''
ReleaseNotes = @"
v2.2.0 - Production Release
- All 19 ASR rules implementation
- Hybrid approach: Registry backup + Set-MpPreference application
- SCCM/Configuration Manager detection
- Cloud protection verification
- Exclusions management support
- Full BACKUP/APPLY/VERIFY/RESTORE support
- Security Baseline overlap detection and logging
"@
}
}
}

52
Modules/ASR/ASR.psm1 Normal file
View file

@ -0,0 +1,52 @@
<#
.SYNOPSIS
Attack Surface Reduction (ASR) Module
.DESCRIPTION
Enables all 19 Microsoft Defender ASR rules in Block mode for comprehensive protection.
Hybrid implementation:
- Registry for backup/verification
- Set-MpPreference for clean application
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: PowerShell 5.1+, Administrator privileges, Windows Defender
#>
# Get the module root path
$ModuleRoot = $PSScriptRoot
# Dot source all Private functions
$PrivatePath = Join-Path $ModuleRoot "Private"
if (Test-Path $PrivatePath) {
Get-ChildItem -Path $PrivatePath -Filter "*.ps1" | ForEach-Object {
try {
. $_.FullName
}
catch {
Write-Host "WARNING: Failed to import private function $($_.Name): $_" -ForegroundColor Yellow
}
}
}
# Dot source all Public functions
$PublicPath = Join-Path $ModuleRoot "Public"
if (Test-Path $PublicPath) {
Get-ChildItem -Path $PublicPath -Filter "*.ps1" | ForEach-Object {
try {
. $_.FullName
}
catch {
Write-Host "WARNING: Failed to import public function $($_.Name): $_" -ForegroundColor Yellow
}
}
}
# Export public functions + Test-ASRCompliance (needed for Invoke-ASRRules verification)
Export-ModuleMember -Function @('Invoke-ASRRules', 'Test-ASRCompliance')
# Alias for naming consistency (non-breaking change)
New-Alias -Name 'Invoke-ASR' -Value 'Invoke-ASRRules' -Force
Export-ModuleMember -Alias 'Invoke-ASR'

View file

@ -0,0 +1,173 @@
[
{
"Name": "Block abuse of exploited vulnerable signed drivers",
"GUID": "56a863a9-875e-4185-98a7-b882c64b5ce5",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Prevents applications from writing vulnerable signed drivers to disk"
},
{
"Name": "Block Adobe Reader from creating child processes",
"GUID": "7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Blocks Adobe Reader from creating processes to prevent malware spread"
},
{
"Name": "Block all Office applications from creating child processes",
"GUID": "d4f940ab-401b-4efc-aadc-ad5f3c50688a",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Blocks Office apps from creating child processes to prevent malware execution"
},
{
"Name": "Block credential stealing from LSASS",
"GUID": "9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": ["Produces high volume of events - safe to ignore most blocks", "Not required if LSA Protection enabled"],
"Description": "Locks down LSASS to prevent credential theft (Mimikatz protection)"
},
{
"Name": "Block executable content from email client and webmail",
"GUID": "be9ba2d9-53ea-4cdc-84e5-9b1eeee46550",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Blocks executable files from being launched from Outlook/webmail"
},
{
"Name": "Block executable files unless they meet prevalence, age, or trusted list",
"GUID": "01443614-cd74-433a-b99e-2ecdc07bfc25",
"Action": 1,
"BaselineStatus": "Missing",
"RequiresCloudProtection": true,
"Warnings": ["Requires cloud-delivered protection", "May block legitimate software - test thoroughly"],
"Description": "Blocks untrusted or unknown executable files based on reputation"
},
{
"Name": "Block execution of potentially obfuscated scripts",
"GUID": "5beb7efe-fd9a-4556-801d-275e5ffc04cc",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": true,
"Warnings": ["Requires cloud-delivered protection"],
"Description": "Detects and blocks suspicious properties in obfuscated scripts (JS/VBS/PS)"
},
{
"Name": "Block JavaScript or VBScript from launching downloaded executable content",
"GUID": "d3e037e1-3eb8-44c8-a917-57927947596d",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Prevents scripts from launching potentially malicious downloaded content"
},
{
"Name": "Block Office applications from creating executable content",
"GUID": "3b576869-a4ec-4529-8536-b80a7769e899",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Prevents Office from saving malicious components to disk for persistence"
},
{
"Name": "Block Office applications from injecting code into other processes",
"GUID": "75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": ["Requires restarting Office applications after configuration"],
"Description": "Blocks code injection from Office apps into other processes"
},
{
"Name": "Block Office communication application from creating child processes",
"GUID": "26190899-1602-49e8-8b27-eb1d0a1ce869",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Prevents Outlook from creating child processes (social engineering protection)"
},
{
"Name": "Block persistence through WMI event subscription",
"GUID": "e6db77e5-3df2-4cf1-b95a-636979351e5b",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": ["If using SCCM (CcmExec.exe), audit for 60 days first"],
"Description": "Prevents malware from abusing WMI to attain persistence"
},
{
"Name": "Block process creations from PSExec and WMI commands",
"GUID": "d1e49aac-8f56-4280-b9ba-993a6d77406c",
"Action": 1,
"BaselineStatus": "Audit",
"RequiresCloudProtection": false,
"Warnings": ["INCOMPATIBLE with SCCM/Configuration Manager", "Security Baseline uses Audit mode", "Only enable Block if NOT using SCCM"],
"Description": "Blocks processes created through PsExec and WMI (lateral movement protection)"
},
{
"Name": "Block rebooting machine in Safe Mode",
"GUID": "33ddedf1-c6e0-47cb-833e-de6133960387",
"Action": 1,
"BaselineStatus": "Missing",
"RequiresCloudProtection": false,
"Warnings": ["New rule (2024) - not yet in TVM"],
"Description": "Blocks commands to restart machines in Safe Mode (ransomware protection)"
},
{
"Name": "Block untrusted and unsigned processes that run from USB",
"GUID": "b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Prevents unsigned/untrusted executables from running from USB drives"
},
{
"Name": "Block use of copied or impersonated system tools",
"GUID": "c0033c00-d16d-4114-a5a0-dc9b3a7d2ceb",
"Action": 1,
"BaselineStatus": "Missing",
"RequiresCloudProtection": false,
"Warnings": ["New rule (2024) - not yet in TVM"],
"Description": "Blocks executables identified as copies/impostors of Windows system tools"
},
{
"Name": "Block Webshell creation for Servers",
"GUID": "a8f5898e-1dc8-49a9-9878-85004b8a61e6",
"Action": 1,
"BaselineStatus": "Missing",
"RequiresCloudProtection": false,
"Warnings": ["New rule (2024) - not yet in TVM", "Server-focused but safe on clients"],
"Description": "Blocks web shell script creation on servers"
},
{
"Name": "Block Win32 API calls from Office macros",
"GUID": "92e97fa1-2edf-4476-bdd6-9dd0b4dddc7b",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": false,
"Warnings": [],
"Description": "Prevents VBA macros from calling Win32 APIs to launch malicious shellcode"
},
{
"Name": "Use advanced protection against ransomware",
"GUID": "c1db55ab-c21a-4637-bb3f-a12568109d35",
"Action": 1,
"BaselineStatus": "Block",
"RequiresCloudProtection": true,
"Warnings": ["Requires cloud-delivered protection"],
"Description": "Extra layer of protection against ransomware using client and cloud heuristics"
}
]

View file

@ -0,0 +1,105 @@
<#
.SYNOPSIS
Backup current ASR registry settings
.DESCRIPTION
Creates backup of ASR registry keys before modification
.PARAMETER BackupId
Identifier for this backup
.OUTPUTS
PSCustomObject with backup info
#>
function Backup-ASRRegistry {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$BackupId = "ASR_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
)
$result = [PSCustomObject]@{
Success = $true
BackupPath = $null
Errors = @()
}
try {
$asrPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Windows Defender Exploit Guard\ASR"
# BACKUP 1: Registry (for reference/verify)
# CRITICAL FIX: Call Backup-RegistryKey unconditionally!
# If key exists: Creates .reg backup
# If key missing: Creates _EMPTY.json marker (Required for proper cleanup during restore)
try {
$regBackup = Backup-RegistryKey -KeyPath $asrPath -BackupName "ASR_Config"
if ($regBackup) {
if ($regBackup -match "_EMPTY\.json$") {
Write-Log -Level INFO -Message "ASR registry key does not exist - Created Empty Marker for cleanup" -Module "ASR"
}
else {
Write-Log -Level INFO -Message "ASR registry backed up with ID: $BackupId" -Module "ASR"
}
}
}
catch {
Write-Log -Level WARNING -Message "Registry backup failed: $_" -Module "ASR"
$result.Errors += "Registry backup failed: $_"
}
# BACKUP 2: Get-MpPreference (CRITICAL for restore)
# Registry-only restore doesn't work after Clear-ASRRules
# We MUST save the active Defender configuration
# IMPORTANT: We backup even if 0 rules are active (pre-hardening state)
try {
$mpPref = Get-MpPreference -ErrorAction Stop
$asrBackupData = @{
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
BackupId = $BackupId
Rules = @()
}
# If rules exist, save them
if ($mpPref.AttackSurfaceReductionRules_Ids -and $mpPref.AttackSurfaceReductionRules_Ids.Count -gt 0) {
# Pair IDs with Actions
for ($i = 0; $i -lt $mpPref.AttackSurfaceReductionRules_Ids.Count; $i++) {
$asrBackupData.Rules += @{
GUID = $mpPref.AttackSurfaceReductionRules_Ids[$i]
Action = $mpPref.AttackSurfaceReductionRules_Actions[$i]
}
}
Write-Log -Level INFO -Message "Backing up $($asrBackupData.Rules.Count) active ASR rules from Get-MpPreference" -Module "ASR"
}
else {
Write-Log -Level INFO -Message "No active ASR rules in Get-MpPreference - backing up empty state (pre-hardening)" -Module "ASR"
}
# ALWAYS create the JSON file, even if Rules array is empty
# This is critical for restore to know "system had 0 rules before hardening"
$asrJson = $asrBackupData | ConvertTo-Json -Depth 5
$backupFile = Register-Backup -Type "ASR" -Data $asrJson -Name "ASR_ActiveConfiguration"
if ($backupFile) {
Write-Log -Level SUCCESS -Message "ASR MpPreference configuration backed up ($($asrBackupData.Rules.Count) rules)" -Module "ASR"
}
else {
Write-Log -Level WARNING -Message "Failed to register ASR MpPreference backup" -Module "ASR"
$result.Errors += "MpPreference backup registration failed"
}
}
catch {
Write-Log -Level WARNING -Message "Get-MpPreference backup failed: $_" -Module "ASR"
$result.Errors += "MpPreference backup failed: $_"
}
}
catch {
$result.Success = $false
$result.Errors += "Backup failed: $($_.Exception.Message)"
Write-Log -Level ERROR -Message "ASR backup failed: $($_.Exception.Message)" -Module "ASR"
}
return $result
}

View file

@ -0,0 +1,34 @@
<#
.SYNOPSIS
Load all 19 ASR rule definitions
.DESCRIPTION
Loads ASR rules from JSON data file with all metadata
.OUTPUTS
Array of ASR rule objects
#>
function Get-ASRRuleDefinitions {
[CmdletBinding()]
[OutputType([Array])]
param()
try {
$configPath = Join-Path $PSScriptRoot "..\Config\ASR-Rules.json"
if (-not (Test-Path $configPath)) {
throw "ASR rules configuration file not found: $configPath"
}
$rules = Get-Content $configPath -Raw | ConvertFrom-Json
Write-Log -Level INFO -Message "Loaded $($rules.Count) ASR rule definitions" -Module "ASR"
return $rules
}
catch {
Write-Log -Level ERROR -Message "Failed to load ASR rules: $($_.Exception.Message)" -Module "ASR"
throw
}
}

View file

@ -0,0 +1,28 @@
<#
.SYNOPSIS
Restore ASR settings from backup
.DESCRIPTION
Restores ASR registry settings from a previous backup created by Backup-ASRRegistry
.PARAMETER BackupId
Identifier of the backup to restore
.OUTPUTS
PSCustomObject with restore results
#>
function Restore-ASRSettings {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$BackupId
)
# This helper is deprecated. ASR restore is handled centrally by the
# framework rollback system (Restore-Session / Restore-AllBackups).
# Keeping this function to avoid breaking existing scripts, but make
# its behavior explicit and safe.
Write-Log -Level WARNING -Message "Restore-ASRSettings is deprecated. Use the main rollback workflow (Restore-AllBackups or GUI Restore) instead." -Module "ASR"
throw "Restore-ASRSettings is deprecated. Use the framework rollback (Core\Rollback.ps1) instead."
}

View file

@ -0,0 +1,131 @@
<#
.SYNOPSIS
Apply ASR rules using Set-MpPreference (PowerShell)
.DESCRIPTION
Uses Microsoft's recommended PowerShell cmdlet to apply ASR rules
Cleaner and more validated than direct registry manipulation
.PARAMETER Rules
Array of rule objects to apply
.PARAMETER DryRun
Preview changes without applying
.OUTPUTS
PSCustomObject with applied count and errors
#>
function Set-ASRViaPowerShell {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[Array]$Rules,
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
$result = [PSCustomObject]@{
Applied = 0
Errors = @()
Warnings = @()
}
try {
# Build arrays for Set-MpPreference
$ruleIds = @()
$ruleActions = @()
foreach ($rule in $Rules) {
$ruleIds += $rule.GUID
$ruleActions += $rule.Action
}
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would apply $($ruleIds.Count) ASR rules via Set-MpPreference" -Module "ASR"
$result.Applied = $ruleIds.Count
return $result
}
Write-Log -Level INFO -Message "Applying $($ruleIds.Count) ASR rules via Set-MpPreference..." -Module "ASR"
# Apply all rules at once
Set-MpPreference -AttackSurfaceReductionRules_Ids $ruleIds `
-AttackSurfaceReductionRules_Actions $ruleActions `
-ErrorAction Stop | Out-Null
$result.Applied = $ruleIds.Count
# WORKAROUND: GPO Registry has higher priority than Set-MpPreference
# We must set BOTH Set-MpPreference AND GPO Registry to ensure the rule is actually applied
# This applies to user-configurable rules (PSExec/WMI and Prevalence)
$asrRegistryPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Windows Defender Exploit Guard\ASR\Rules"
# User-configurable rules that need GPO Registry sync
$userConfigurableRules = @(
"d1e49aac-8f56-4280-b9ba-993a6d77406c", # PSExec/WMI (Management Tools)
"01443614-cd74-433a-b99e-2ecdc07bfc25" # Prevalence (New/Unknown Software)
)
foreach ($rule in $Rules) {
# For user-configurable rules: Always sync to GPO Registry (Block OR Audit)
if ($userConfigurableRules -contains $rule.GUID) {
try {
$currentValue = Get-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -ErrorAction SilentlyContinue
$needsUpdate = $false
if ($currentValue) {
# Registry exists - check if value differs
if ([int]$currentValue.($rule.GUID) -ne $rule.Action) {
$needsUpdate = $true
}
} else {
# Registry doesn't exist - need to create it
$needsUpdate = $true
}
if ($needsUpdate) {
# Ensure path exists
if (-not (Test-Path $asrRegistryPath)) {
New-Item -Path $asrRegistryPath -Force | Out-Null
}
# Set the registry value (using string type like Security Baseline does)
Set-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -Value $rule.Action.ToString() -Type String -Force -ErrorAction Stop | Out-Null
$modeName = switch ($rule.Action) { 1 { "Block" } 2 { "Audit" } default { "Unknown" } }
Write-Log -Level INFO -Message "Synced $($rule.Name) to GPO Registry: $modeName mode" -Module "ASR"
}
}
catch {
$result.Warnings += "Could not sync rule $($rule.Name) to GPO registry: $($_.Exception.Message)"
Write-Log -Level WARNING -Message "Could not sync $($rule.Name) to GPO registry: $($_.Exception.Message)" -Module "ASR"
}
}
# For non-configurable rules: Only override if user wants Block and Baseline had Audit
elseif ($rule.Action -eq 1 -and $rule.BaselineStatus -eq "Audit") {
try {
$currentValue = Get-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -ErrorAction SilentlyContinue
if ($currentValue -and [int]$currentValue.($rule.GUID) -ne 1) {
Set-ItemProperty -Path $asrRegistryPath -Name $rule.GUID -Value "1" -Type String -Force -ErrorAction Stop | Out-Null
Write-Log -Level INFO -Message "Force-applied $($rule.Name) to Block mode via registry (was Audit from Baseline)" -Module "ASR"
}
}
catch {
$result.Warnings += "Could not force-apply rule $($rule.Name) via registry: $($_.Exception.Message)"
Write-Log -Level WARNING -Message "Could not force-apply $($rule.Name) via registry: $($_.Exception.Message)" -Module "ASR"
}
}
}
Write-Log -Level INFO -Message "Successfully applied $($ruleIds.Count) ASR rules" -Module "ASR"
}
catch {
$result.Errors += "Failed to apply ASR rules: $($_.Exception.Message)"
Write-Log -Level ERROR -Message "Set-MpPreference failed: $($_.Exception.Message)" -Module "ASR"
}
return $result
}

View file

@ -0,0 +1,117 @@
<#
.SYNOPSIS
Verify ASR rules are correctly applied
.DESCRIPTION
Uses Get-MpPreference to verify all ASR rules are active with correct actions
.PARAMETER ExpectedRules
Array of rule objects with GUID and Action properties
.OUTPUTS
PSCustomObject with verification results
#>
function Test-ASRCompliance {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[Array]$ExpectedRules
)
$result = [PSCustomObject]@{
Passed = $true
CheckedCount = 0
FailedCount = 0
FailedRules = @()
}
try {
# Get current ASR configuration from Defender
$mpPref = Get-MpPreference -ErrorAction Stop
# Get configured ASR rule IDs and actions
$configuredIds = $mpPref.AttackSurfaceReductionRules_Ids
$configuredActions = $mpPref.AttackSurfaceReductionRules_Actions
if (-not $configuredIds -or $configuredIds.Count -eq 0) {
$result.Passed = $false
$result.FailedCount = $ExpectedRules.Count
Write-Log -Level WARNING -Message "No ASR rules found in Defender configuration" -Module "ASR"
return $result
}
# Create hashtable for quick lookup
$configuredRules = @{}
for ($i = 0; $i -lt $configuredIds.Count; $i++) {
$configuredRules[$configuredIds[$i]] = $configuredActions[$i]
}
# Rules where both BLOCK (1) and AUDIT (2) are considered "Pass"
# These are user-configurable rules where either mode is valid
$flexibleRules = @(
"d1e49aac-8f56-4280-b9ba-993a6d77406c", # PSExec/WMI (Management Tools)
"01443614-cd74-433a-b99e-2ecdc07bfc25" # Prevalence (New/Unknown Software)
)
# Verify each expected rule
foreach ($rule in $ExpectedRules) {
$result.CheckedCount++
if ($configuredRules.ContainsKey($rule.GUID)) {
$actualAction = $configuredRules[$rule.GUID]
# Check if this is a flexible rule (Block or Audit both count as Pass)
$isFlexibleRule = $flexibleRules -contains $rule.GUID
$isActiveMode = $actualAction -in @(1, 2) # Block or Audit
# For flexible rules: Pass if Block OR Audit
# For other rules: Pass only if exact match
$rulePassed = if ($isFlexibleRule) { $isActiveMode } else { $actualAction -eq $rule.Action }
if (-not $rulePassed) {
$result.FailedCount++
$result.Passed = $false
$result.FailedRules += $rule.GUID
$actionName = switch ($actualAction) {
0 { "Disabled" }
1 { "Block" }
2 { "Audit" }
6 { "Warn" }
default { "Unknown($actualAction)" }
}
$expectedName = switch ($rule.Action) {
0 { "Disabled" }
1 { "Block" }
2 { "Audit" }
6 { "Warn" }
default { "Unknown($($rule.Action))" }
}
Write-Log -Level WARNING -Message "Rule '$($rule.Name)' has action $actionName, expected $expectedName" -Module "ASR"
}
}
else {
$result.FailedCount++
$result.Passed = $false
$result.FailedRules += $rule.GUID
Write-Log -Level WARNING -Message "Rule '$($rule.Name)' not found in Defender configuration" -Module "ASR"
}
}
if ($result.Passed) {
Write-Log -Level INFO -Message "ASR compliance check passed - all $($result.CheckedCount) rules verified" -Module "ASR"
}
else {
Write-Log -Level WARNING -Message "ASR compliance check found $($result.FailedCount) issues out of $($result.CheckedCount) rules" -Module "ASR"
}
}
catch {
$result.Passed = $false
$result.FailedCount = $ExpectedRules.Count
Write-Log -Level ERROR -Message "Compliance check failed: $($_.Exception.Message)" -Module "ASR"
}
return $result
}

View file

@ -0,0 +1,34 @@
<#
.SYNOPSIS
Verify cloud-delivered protection is enabled
.DESCRIPTION
Some ASR rules require cloud protection to be enabled
This function checks if it's active
.OUTPUTS
Boolean - True if cloud protection is enabled
#>
function Test-CloudProtection {
[CmdletBinding()]
[OutputType([bool])]
param()
try {
# Check via Get-MpPreference
$mpPref = Get-MpPreference -ErrorAction Stop
if ($mpPref.MAPSReporting -eq 0) {
Write-Log -Level WARNING -Message "Cloud-delivered protection (MAPS) is disabled" -Module "ASR"
return $false
}
Write-Log -Level INFO -Message "Cloud-delivered protection is enabled (MAPS: $($mpPref.MAPSReporting))" -Module "ASR"
return $true
}
catch {
Write-Log -Level WARNING -Message "Failed to check cloud protection status: $_" -Module "ASR"
return $false
}
}

View file

@ -0,0 +1,41 @@
<#
.SYNOPSIS
Detect SCCM/Configuration Manager presence
.DESCRIPTION
Checks if Configuration Manager client (CcmExec.exe) is running
This is critical because PSExec/WMI ASR rule conflicts with SCCM
.OUTPUTS
Boolean - True if ConfigMgr detected
#>
function Test-ConfigMgrPresence {
[CmdletBinding()]
[OutputType([bool])]
param()
try {
# Check for CCM service
$ccmService = Get-Service -Name "CcmExec" -ErrorAction SilentlyContinue
if ($ccmService -and $ccmService.Status -eq "Running") {
Write-Log -Level WARNING -Message "Configuration Manager (SCCM) client detected" -Module "ASR"
return $true
}
# Check for CCM process
$ccmProcess = Get-Process -Name "CcmExec" -ErrorAction SilentlyContinue
if ($ccmProcess) {
Write-Log -Level WARNING -Message "Configuration Manager process detected" -Module "ASR"
return $true
}
return $false
}
catch {
Write-Log -Level WARNING -Message "Failed to detect ConfigMgr: $_. Assuming not present." -Module "ASR"
return $false
}
}

View file

@ -0,0 +1,571 @@
<#
.SYNOPSIS
Apply all 19 Microsoft Defender ASR rules
.DESCRIPTION
Enables all 19 Attack Surface Reduction rules in Block mode for comprehensive protection.
Rules Applied:
- All 19 ASR rules in Block mode (Action = 1)
- Includes 4 rules missing from Security Baseline
- Upgrades 1 rule from Audit to Block (PSExec/WMI - with SCCM check)
Features:
- SCCM/Configuration Manager detection (PSExec/WMI rule warning)
- Cloud protection verification
- BACKUP/APPLY/VERIFY/RESTORE pattern
- DryRun mode for testing
- Security Baseline overlap detection
.PARAMETER DryRun
Preview changes without applying them
.PARAMETER SkipBackup
Skip backup creation (not recommended)
.PARAMETER SkipVerify
Skip post-application verification
.PARAMETER Force
Apply even if validation warnings occur (SCCM, Cloud Protection)
.PARAMETER AllowPSExecWMI
Force enable PSExec/WMI rule even if SCCM detected (use with caution)
.EXAMPLE
Invoke-ASRRules
Apply all 19 ASR rules with full backup and verification
.EXAMPLE
Invoke-ASRRules -DryRun
Preview what changes would be made
.EXAMPLE
Invoke-ASRRules -AllowPSExecWMI -Force
Force enable PSExec/WMI rule despite SCCM detection
.OUTPUTS
PSCustomObject with results including success status, rules applied, and any errors
#>
function Invoke-ASRRules {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun,
[Parameter(Mandatory = $false)]
[switch]$SkipBackup,
[Parameter(Mandatory = $false)]
[switch]$SkipVerify,
[Parameter(Mandatory = $false)]
[switch]$Force,
[Parameter(Mandatory = $false)]
[switch]$AllowPSExecWMI
)
begin {
$moduleName = "ASR"
$startTime = Get-Date
# Ensure core functions are available when the module is imported directly (outside Framework.ps1)
if (-not (Get-Command Initialize-BackupSystem -ErrorAction SilentlyContinue)) {
try {
$frameworkRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$coreFiles = @(
"Core\Logger.ps1",
"Core\Config.ps1",
"Core\Validator.ps1",
"Core\Rollback.ps1",
"Utils\Compatibility.ps1"
)
foreach ($file in $coreFiles) {
$corePath = Join-Path $frameworkRoot $file
if (Test-Path $corePath) {
. $corePath
}
}
}
catch {
Write-Host "ERROR: Failed to load core dependencies for ASR module: $_" -ForegroundColor Red
}
}
# Initialize result object
$result = [PSCustomObject]@{
ModuleName = $moduleName
Success = $false
RulesApplied = 0
Errors = @()
Warnings = @()
BackupCreated = $false
VerificationPassed = $false
ConfigMgrDetected = $false
CloudProtectionEnabled = $false
Duration = $null
Details = @{
TotalRules = 19
BlockMode = 0
AuditMode = 0
DisabledMode = 0
}
}
Write-Log -Level INFO -Message "Starting ASR rules application (all 19 rules)" -Module $moduleName
if ($DryRun) {
Write-Log -Level INFO -Message "DRY RUN MODE - No changes will be applied" -Module $moduleName
}
}
process {
try {
# Step 1: Prerequisites validation
Write-Log -Level INFO -Message "Validating prerequisites..." -Module $moduleName
if (-not (Test-IsAdmin)) {
throw "Administrator privileges required"
}
if (-not (Test-WindowsVersion -MinimumBuild 22000)) {
throw "Windows 11 or later required"
}
# Check Windows Defender status and third-party AV
$defenderService = Get-Service -Name "WinDefend" -ErrorAction SilentlyContinue
$defenderRunning = $defenderService -and $defenderService.Status -eq "Running"
# Check for third-party antivirus (they disable Defender)
$thirdPartyAV = $null
try {
$avProducts = Get-CimInstance -Namespace "root/SecurityCenter2" -ClassName "AntiVirusProduct" -ErrorAction SilentlyContinue
$thirdPartyAV = $avProducts | Where-Object { $_.displayName -notmatch "Windows Defender|Microsoft Defender" } | Select-Object -First 1
}
catch {
# SecurityCenter2 not available - continue with Defender check only
$null = $null
}
if (-not $defenderRunning) {
if ($thirdPartyAV) {
# Third-party AV detected - skip ASR gracefully (not an error!)
$avName = $thirdPartyAV.displayName
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " ASR Module Skipped" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Third-party antivirus detected: $avName" -ForegroundColor Cyan
Write-Host ""
Write-Host "ASR rules require Windows Defender to be active." -ForegroundColor Yellow
Write-Host "Your antivirus ($avName) has its own protection features." -ForegroundColor Yellow
Write-Host ""
Write-Host "This is NOT an error - ASR will be skipped." -ForegroundColor Green
Write-Host ""
Write-Log -Level WARNING -Message "ASR skipped: Third-party AV detected ($avName). Defender disabled." -Module $moduleName
$result.Success = $true # Not an error - intentional skip
$result.Warnings += "ASR skipped: Third-party antivirus detected ($avName). Your AV provides similar protection."
$result.RulesApplied = 0
return $result
}
else {
# No third-party AV but Defender not running - this IS a problem
throw "Windows Defender service is not running and no third-party antivirus detected. ASR rules require Defender to be active."
}
}
# Load ASR rule definitions
Write-Log -Level INFO -Message "Loading ASR rule definitions..." -Module $moduleName
$asrRules = Get-ASRRuleDefinitions
# Step 2: Check for Remote Management Tools (SCCM/Intune/etc.)
Write-Log -Level INFO -Message "Checking for remote management tools..." -Module $moduleName
# Automatic detection
$configMgrDetected = Test-ConfigMgrPresence
$result.ConfigMgrDetected = $configMgrDetected
# Check for management tools - NonInteractive or Interactive
$usesManagementTools = $false
if (Test-NonInteractiveMode) {
# NonInteractive mode (GUI) - use config value
$usesManagementTools = Get-NonInteractiveValue -Module "ASR" -Key "usesManagementTools" -Default $false
Write-NonInteractiveDecision -Module $moduleName -Decision "Management tools setting" -Value $(if ($usesManagementTools) { "Yes (1 AUDIT)" } else { "No (ALL BLOCK)" })
}
elseif (-not $Force -and -not $AllowPSExecWMI -and -not $DryRun) {
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Remote Management Tool Check" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
if ($configMgrDetected) {
Write-Host "DETECTED: SCCM/Configuration Manager is currently installed" -ForegroundColor Yellow
Write-Host ""
}
Write-Host "Do you use ANY of these remote management tools?" -ForegroundColor White
Write-Host ""
Write-Host " - Microsoft SCCM (Configuration Manager)" -ForegroundColor Gray
Write-Host " - Microsoft Intune / Endpoint Manager" -ForegroundColor Gray
Write-Host " - PDQ Deploy / PDQ Inventory" -ForegroundColor Gray
Write-Host " - ManageEngine Desktop Central" -ForegroundColor Gray
Write-Host " - Any other WMI/PSExec based management tools" -ForegroundColor Gray
Write-Host ""
Write-Host "These tools use PSExec and WMI for remote management." -ForegroundColor Yellow
Write-Host "If you use them, one ASR rule must be set to AUDIT mode." -ForegroundColor Yellow
Write-Host ""
Write-Host "Options:" -ForegroundColor Cyan
Write-Host " [Y] Yes - I use management tools" -ForegroundColor Yellow
Write-Host " > 1 rule: AUDIT mode (PSExec/WMI only)" -ForegroundColor Gray
Write-Host " > 18 rules: BLOCK mode (full protection)" -ForegroundColor Gray
Write-Host ""
Write-Host " [N] No - I don't use any of these" -ForegroundColor Green
Write-Host " > ALL 19 rules: BLOCK mode (maximum protection)" -ForegroundColor Gray
Write-Host ""
do {
$choice = Read-Host "Select option [Y/N] (default: N)"
if ([string]::IsNullOrWhiteSpace($choice)) { $choice = "N" }
$choice = $choice.ToUpper()
if ($choice -notin @('Y', 'N')) {
Write-Host ""
Write-Host "Invalid input. Please enter Y or N." -ForegroundColor Red
Write-Host ""
}
} while ($choice -notin @('Y', 'N'))
switch ($choice) {
"Y" {
$usesManagementTools = $true
Write-Host ""
Write-Host "1 rule set to AUDIT (PSExec/WMI), 18 rules set to BLOCK" -ForegroundColor Yellow
Write-Log -Level INFO -Message "User confirmed use of management tools - 1 AUDIT + 18 BLOCK" -Module $moduleName
}
"N" {
$usesManagementTools = $false
Write-Host ""
Write-Host "ALL 19 rules will be set to BLOCK mode" -ForegroundColor Green
Write-Log -Level INFO -Message "User confirmed no management tools - ALL 19 BLOCK" -Module $moduleName
}
}
Write-Host ""
}
elseif ($Force -and -not $AllowPSExecWMI) {
# Force flag: Auto-detect or assume safe
$usesManagementTools = $configMgrDetected
Write-Log -Level INFO -Message "Force flag: Using detection result (ConfigMgr: $configMgrDetected)" -Module $moduleName
}
# Apply PSExec/WMI rule mode based on user choice or detection
if (($usesManagementTools -or $configMgrDetected) -and -not $AllowPSExecWMI) {
$psexecRule = $asrRules | Where-Object { $_.GUID -eq "d1e49aac-8f56-4280-b9ba-993a6d77406c" }
# Set PSExec/WMI to Audit mode (user confirmed or detected)
$psexecRule.Action = 2
$result.Warnings += "Management tools detected/confirmed: PSExec/WMI rule set to Audit mode"
Write-Log -Level INFO -Message "PSExec/WMI rule set to Audit mode (management tools in use)" -Module $moduleName
}
# Step 2b: Prevalence rule (new/unknown software) - NonInteractive or Interactive
if (-not $DryRun) {
$prevalenceRule = $asrRules | Where-Object { $_.GUID -eq "01443614-cd74-433a-b99e-2ecdc07bfc25" }
if ($prevalenceRule) {
$allowNewSoftware = $false
if (Test-NonInteractiveMode) {
# NonInteractive mode (GUI) - use config value
$allowNewSoftware = Get-NonInteractiveValue -Module "ASR" -Key "allowNewSoftware" -Default $false
if ($allowNewSoftware) {
$prevalenceRule.Action = 2
$result.Warnings += "ASR prevalence rule set to AUDIT (less restrictive; see README for details)."
} else {
$prevalenceRule.Action = 1
}
Write-NonInteractiveDecision -Module $moduleName -Decision "New/Unknown software rule" -Value $(if ($allowNewSoftware) { "AUDIT (allow)" } else { "BLOCK (secure)" })
}
else {
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " ASR Rule: New / Unknown Software" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Rule: Block executable files unless they meet prevalence, age, or trusted list" -ForegroundColor White
Write-Host "GUID: $($prevalenceRule.GUID)" -ForegroundColor DarkGray
Write-Host ""
Write-Host "This rule blocks very new or unknown executables that" -ForegroundColor Yellow
Write-Host "are not yet trusted by Microsoft's reputation systems." -ForegroundColor Yellow
Write-Host ""
Write-Host "Do you install NEW or UNTRUSTED software frequently?" -ForegroundColor White
Write-Host ""
Write-Host " - Games from independent developers" -ForegroundColor Gray
Write-Host " - Beta software / Early access programs" -ForegroundColor Gray
Write-Host " - Custom/in-house business applications" -ForegroundColor Gray
Write-Host " - Open-source tools without Microsoft reputation" -ForegroundColor Gray
Write-Host ""
Write-Host "Options:" -ForegroundColor Cyan
Write-Host " [Y] Yes - I need to install untrusted software" -ForegroundColor Yellow
Write-Host " > AUDIT mode: Events logged, installs allowed" -ForegroundColor Gray
Write-Host " > Developer/test mode (less secure)" -ForegroundColor Gray
Write-Host ""
Write-Host " [N] No - I only install trusted software" -ForegroundColor Green
Write-Host " > BLOCK mode: Maximum security (recommended)" -ForegroundColor Gray
Write-Host " > New/unknown installers may be blocked" -ForegroundColor Gray
Write-Host ""
do {
$prevalenceChoice = Read-Host "Select option [Y/N] (default: N)"
if ([string]::IsNullOrWhiteSpace($prevalenceChoice)) { $prevalenceChoice = "N" }
$prevalenceChoice = $prevalenceChoice.ToUpper()
if ($prevalenceChoice -notin @('Y', 'N')) {
Write-Host ""
Write-Host "Invalid input. Please enter Y or N." -ForegroundColor Red
Write-Host ""
}
} while ($prevalenceChoice -notin @('Y', 'N'))
switch ($prevalenceChoice) {
"N" {
$prevalenceRule.Action = 1
Write-Host ""
Write-Host "New/Unknown Software rule set to BLOCK mode (maximum security)" -ForegroundColor Green
Write-Log -Level INFO -Message "Prevalence rule configured to BLOCK (recommended)" -Module $moduleName
}
"Y" {
$prevalenceRule.Action = 2
Write-Host ""
Write-Host "New/Unknown Software rule set to AUDIT mode (developer/test)" -ForegroundColor Yellow
$result.Warnings += "ASR prevalence rule set to AUDIT (less restrictive; see README for details)."
Write-Log -Level INFO -Message "Prevalence rule configured to AUDIT (developer/compat mode)" -Module $moduleName
}
}
Write-Host ""
}
}
}
# Step 3: Check cloud protection
Write-Log -Level INFO -Message "Checking cloud-delivered protection..." -Module $moduleName
$cloudProtectionEnabled = Test-CloudProtection
$result.CloudProtectionEnabled = $cloudProtectionEnabled
if (-not $cloudProtectionEnabled) {
$cloudRules = $asrRules | Where-Object { $_.RequiresCloudProtection -eq $true }
$result.Warnings += "Cloud protection disabled: $($cloudRules.Count) rules require it for optimal operation"
Write-Log -Level WARNING -Message "$($cloudRules.Count) ASR rules require cloud protection for full functionality" -Module $moduleName
if (Test-NonInteractiveMode) {
# NonInteractive mode (GUI) - use config value
$continueWithoutCloud = Get-NonInteractiveValue -Module "ASR" -Key "continueWithoutCloud" -Default $true
if (-not $continueWithoutCloud) {
Write-NonInteractiveDecision -Module $moduleName -Decision "Cloud protection required - aborting"
throw "ASR application cancelled (cloud protection required, continueWithoutCloud=false)"
}
Write-NonInteractiveDecision -Module $moduleName -Decision "Continuing without cloud protection (limited functionality)"
}
elseif (-not $Force -and -not $DryRun) {
# Interactive prompt for cloud protection
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " Cloud Protection Not Enabled!" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "$($cloudRules.Count) ASR rules require cloud-delivered protection for optimal functionality:" -ForegroundColor Yellow
Write-Host ""
foreach ($cloudRule in $cloudRules) {
Write-Host " - $($cloudRule.Name)" -ForegroundColor Gray
}
Write-Host ""
Write-Host "These rules will work in limited capacity without cloud protection." -ForegroundColor Yellow
Write-Host ""
Write-Host "Options:" -ForegroundColor Cyan
Write-Host " [C] Continue - Apply rules anyway (limited functionality)" -ForegroundColor Green
Write-Host " [A] Abort - Cancel ASR rule application" -ForegroundColor Yellow
Write-Host ""
do {
$choice = Read-Host "Select option [C/A] (default: A)"
if ([string]::IsNullOrWhiteSpace($choice)) { $choice = "A" }
$choice = $choice.ToUpper()
if ($choice -notin @('C', 'A')) {
Write-Host ""
Write-Host "Invalid input. Please enter C or A." -ForegroundColor Red
Write-Host ""
}
} while ($choice -notin @('C', 'A'))
switch ($choice) {
"C" {
Write-Host ""
Write-Host "Continuing with cloud protection disabled" -ForegroundColor Yellow
Write-Log -Level INFO -Message "User chose to continue despite cloud protection disabled" -Module $moduleName
}
"A" {
Write-Host ""
Write-Host "ASR rule application cancelled by user" -ForegroundColor Yellow
Write-Log -Level INFO -Message "ASR application cancelled due to cloud protection requirement" -Module $moduleName
throw "ASR application cancelled by user due to cloud protection requirement"
}
}
Write-Host ""
}
elseif ($Force) {
# Force flag - continue silently
Write-Log -Level INFO -Message "Continuing despite cloud protection disabled (Force flag)" -Module $moduleName
}
}
# Step 3a: Initialize and start module backup
if (-not $SkipBackup -and -not $DryRun) {
try {
Initialize-BackupSystem
$null = Start-ModuleBackup -ModuleName $moduleName
Write-Log -Level INFO -Message "Session backup initialized" -Module $moduleName
}
catch {
$result.Warnings += "Failed to initialize/start module backup: $_"
Write-Log -Level WARNING -Message "Failed to initialize/start module backup: $_" -Module $moduleName
}
}
# Step 4: Create backup
if (-not $SkipBackup -and -not $DryRun) {
Write-Log -Level INFO -Message "Creating backup..." -Module $moduleName
$backupResult = Backup-ASRRegistry
if ($backupResult.Errors.Count -gt 0) {
foreach ($err in $backupResult.Errors) {
$result.Warnings += $err
}
}
else {
# Register backup in session manifest
Complete-ModuleBackup -ItemsBackedUp 1 -Status "Success"
$result.BackupCreated = $true
}
}
# Step 5: Apply ASR rules via PowerShell
# Note: Set-ASRViaPowerShell logs internally, no need to log here
$applyResult = Set-ASRViaPowerShell -Rules $asrRules -DryRun:$DryRun
# In DryRun mode, no rules are actually applied, so keep RulesApplied = 0
if (-not $DryRun) {
$result.RulesApplied = $applyResult.Applied
}
# Add errors and warnings individually to avoid nested arrays
foreach ($err in $applyResult.Errors) {
$result.Errors += $err
}
foreach ($warn in $applyResult.Warnings) {
$result.Warnings += $warn
}
# Count rule modes from actual system state
$mpPref = Get-MpPreference
$currentActions = $mpPref.AttackSurfaceReductionRules_Actions
if ($currentActions) {
$result.Details.BlockMode = ($currentActions | Where-Object { $_ -eq 1 }).Count
$result.Details.AuditMode = ($currentActions | Where-Object { $_ -eq 2 }).Count
$result.Details.DisabledMode = ($currentActions | Where-Object { $_ -eq 0 }).Count
} else {
# Fallback to array count
$result.Details.BlockMode = ($asrRules | Where-Object { $_.Action -eq 1 }).Count
$result.Details.AuditMode = ($asrRules | Where-Object { $_.Action -eq 2 }).Count
$result.Details.DisabledMode = ($asrRules | Where-Object { $_.Action -eq 0 }).Count
}
# Step 6: Verification
if (-not $SkipVerify -and -not $DryRun) {
Write-Log -Level INFO -Message "Verifying applied ASR rules..." -Module $moduleName
$verificationResult = Test-ASRCompliance -ExpectedRules $asrRules
$result.VerificationPassed = $verificationResult.Passed
if (-not $verificationResult.Passed) {
$result.Warnings += "Verification found $($verificationResult.FailedCount) rules not applied correctly"
Write-Log -Level WARNING -Message "Verification found $($verificationResult.FailedCount) failed rules" -Module $moduleName
}
else {
Write-Log -Level INFO -Message "Verification passed - all $($verificationResult.CheckedCount) rules confirmed" -Module $moduleName
}
}
# Log baseline overlap
$baselineRules = $asrRules | Where-Object { $_.BaselineStatus -in @("Block", "Audit") }
Write-Log -Level INFO -Message "Security Baseline overlap: $($baselineRules.Count) rules already in baseline" -Module $moduleName
$newRules = $asrRules | Where-Object { $_.BaselineStatus -eq "Missing" }
if ($newRules.Count -gt 0) {
Write-Log -Level INFO -Message "Added $($newRules.Count) rules not in Security Baseline:" -Module $moduleName
foreach ($newRule in $newRules) {
Write-Log -Level INFO -Message " + $($newRule.Name)" -Module $moduleName
}
}
$upgradedRules = $asrRules | Where-Object { $_.BaselineStatus -eq "Audit" -and $_.Action -eq 1 }
if ($upgradedRules.Count -gt 0) {
Write-Log -Level INFO -Message "Upgraded $($upgradedRules.Count) rules from Audit to Block:" -Module $moduleName
foreach ($upgradedRule in $upgradedRules) {
Write-Log -Level INFO -Message " [UPGRADE] $($upgradedRule.Name)" -Module $moduleName
}
}
# Mark as successful if no critical errors
if ($result.Errors.Count -eq 0) {
$result.Success = $true
Write-Log -Level INFO -Message "ASR rules applied successfully" -Module $moduleName
}
else {
Write-Log -Level ERROR -Message "ASR application completed with $($result.Errors.Count) errors" -Module $moduleName
}
}
catch {
$result.Success = $false
$result.Errors += $_.Exception.Message
Write-Log -Level ERROR -Message "ASR application failed: $($_.Exception.Message)" -Module $moduleName
}
}
end {
$result.Duration = (Get-Date) - $startTime
Write-Log -Level INFO -Message "ASR application completed in $($result.Duration.TotalSeconds) seconds" -Module $moduleName
$blockCount = $result.Details.BlockMode
$auditCount = $result.Details.AuditMode
Write-Log -Level INFO -Message "Rules applied: $($result.RulesApplied) ($blockCount Block, $auditCount Audit)" -Module $moduleName
Write-Log -Level INFO -Message "Errors: $($result.Errors.Count), Warnings: $($result.Warnings.Count)" -Module $moduleName
# Log warning details for transparency
if ($result.Warnings.Count -gt 0) {
foreach ($warn in $result.Warnings) {
Write-Log -Level INFO -Message " Warning: $warn" -Module $moduleName
}
}
# GUI parsing marker for settings count (19 ASR rules)
Write-Log -Level SUCCESS -Message "Applied 19 settings" -Module "ASR"
return $result
}
}

View file

@ -0,0 +1,73 @@
@{
# Module manifest for AdvancedSecurity
# Version
ModuleVersion = '2.2.0'
# Unique ID
GUID = 'e7f5a3d2-8c9b-4f1e-a6d3-9b2c8f4e5a1d'
# Author
Author = 'NexusOne23'
# Company
CompanyName = 'Open Source Project'
# Copyright
Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.'
# Description
Description = 'Advanced Security hardening beyond Microsoft Security Baseline: RDP hardening, WDigest protection, Admin Shares disable, Risky Ports/Services, Legacy TLS/WPAD/PSv2, SRP .lnk protection (CVE-2025-9491), Windows Update (3 simple GUI settings), Finger Protocol block, Wireless Display (Miracast) security. 38+ settings total with profile-based execution (Balanced/Enterprise/Maximum) and domain-safety checks plus full backup/restore.'
# Minimum PowerShell version
PowerShellVersion = '5.1'
# Root module
RootModule = 'AdvancedSecurity.psm1'
# Functions to export
FunctionsToExport = @(
'Invoke-AdvancedSecurity',
'Test-AdvancedSecurity',
'Restore-AdvancedSecuritySettings'
)
# Cmdlets to export
CmdletsToExport = @()
# Variables to export
VariablesToExport = @()
# Aliases to export
AliasesToExport = @()
# Private data
PrivateData = @{
PSData = @{
Tags = @('Security', 'Hardening', 'Windows11', 'Advanced', 'RDP', 'Credentials', 'NetworkSecurity')
LicenseUri = ''
ProjectUri = ''
ReleaseNotes = @'
v2.2.0 (2025-12-08)
- Production release of AdvancedSecurity module
- 49 advanced hardening settings implemented (was 36)
- NEW: Wireless Display (Miracast) security hardening
- Default: Block receiving projections + require PIN (all profiles)
- Optional: Complete disable (blocks sending, mDNS, ports 7236/7250)
- Prevents screen interception attacks from network attackers
- Profile-based execution (Balanced/Enterprise/Maximum)
- RDP NLA enforcement + optional complete disable
- WDigest credential protection (backwards compatible)
- Administrative shares disable (domain-aware)
- Risky firewall ports closure (LLMNR, NetBIOS, UPnP/SSDP)
- Risky network services stop (SSDPSRV, upnphost, lmhosts)
- Legacy TLS 1.0/1.1 disable
- WPAD auto-discovery disable
- PowerShell v2 removal
- Full backup/restore capability
- WhatIf mode and change log export
- Compliance testing function
'@
}
}
}

View file

@ -0,0 +1,65 @@
# AdvancedSecurity Module Loader
# Version: 2.2.0
# Description: Advanced Security Hardening - Beyond Microsoft Security Baseline
# Get module path
$ModulePath = $PSScriptRoot
# Load Private functions
$PrivateFunctions = @(
'Enable-RdpNLA',
'Set-WDigestProtection',
'Disable-AdminShares',
'Disable-RiskyPorts',
'Stop-RiskyServices',
'Disable-WPAD',
'Disable-LegacyTLS',
'Remove-PowerShellV2',
'Block-FingerProtocol',
'Set-SRPRules',
'Set-WindowsUpdate',
'Set-WirelessDisplaySecurity',
'Set-DiscoveryProtocolsSecurity',
'Set-FirewallShieldsUp',
'Set-IPv6Security',
'Test-RdpSecurity',
'Test-WDigest',
'Test-RiskyPorts',
'Test-RiskyServices',
'Test-AdminShares',
'Test-SRPCompliance',
'Test-WindowsUpdate',
'Test-LegacyTLS',
'Test-WPAD',
'Test-PowerShellV2',
'Test-FingerProtocol',
'Test-WirelessDisplaySecurity',
'Test-DiscoveryProtocolsSecurity',
'Test-FirewallShieldsUp',
'Test-IPv6Security',
'Backup-AdvancedSecuritySettings'
)
foreach ($function in $PrivateFunctions) {
$functionPath = Join-Path $ModulePath "Private\$function.ps1"
if (Test-Path $functionPath) {
. $functionPath
}
}
# Load Public functions
$PublicFunctions = @(
'Invoke-AdvancedSecurity',
'Test-AdvancedSecurity',
'Restore-AdvancedSecuritySettings'
)
foreach ($function in $PublicFunctions) {
$functionPath = Join-Path $ModulePath "Public\$function.ps1"
if (Test-Path $functionPath) {
. $functionPath
}
}
# Export only Public functions
Export-ModuleMember -Function $PublicFunctions

View file

@ -0,0 +1,116 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Administrative Shares Configuration",
"description": "Configuration for disabling administrative shares (C$, ADMIN$, etc.) to prevent lateral movement",
"version": "2.2.0",
"Administrative_Shares": {
"description": "Disable automatic creation and remove existing administrative shares",
"risk_level": "CRITICAL",
"attack_vectors": [
"Lateral movement in networks (WannaCry, NotPetya propagation)",
"Remote file access by attackers with stolen credentials",
"Pass-the-Hash attacks using admin shares",
"Automated malware propagation"
],
"shares_affected": {
"C$": "Root of C: drive",
"D$": "Root of D: drive (if exists)",
"E$": "Root of E: drive (if exists)",
"ADMIN$": "Windows directory (C:\\Windows)",
"IPC$": "Named pipes - CANNOT be removed (required by Windows)"
},
"registry_settings": {
"path": "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters",
"AutoShareWks": {
"description": "Disable automatic shares on Workstation (Home/Pro editions)",
"value": 0,
"type": "DWORD",
"default": 1
},
"AutoShareServer": {
"description": "Disable automatic shares on Server editions",
"value": 0,
"type": "DWORD",
"default": 1
}
},
"firewall_protection": {
"description": "Block SMB on Public network profile",
"rule_name": "Block Admin Shares (NoID Privacy)",
"direction": "Inbound",
"protocol": "TCP",
"local_port": 445,
"profile": "Public",
"action": "Block"
},
"domain_safety": {
"enabled": true,
"description": "Automatically detect domain-joined systems and skip unless -Force",
"check": "Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object PartOfDomain",
"warnings": [
"Group Policy management may be affected",
"SCCM/Management tools may require admin shares",
"Remote administration tools may stop working"
],
"force_required": true,
"enterprise_recommendation": "Test in staging environment before deployment"
}
},
"Profiles": {
"Balanced": {
"enabled": true,
"domain_check": true,
"force_required": false
},
"Enterprise": {
"enabled": "conditional",
"domain_check": true,
"force_required": true,
"note": "Auto-disabled for domain-joined systems unless -Force"
},
"Maximum": {
"enabled": true,
"domain_check": false,
"force_required": false,
"note": "Always enabled for maximum security"
}
},
"Impact": {
"positive": [
"Prevents lateral movement in case of credential theft",
"Stops automated ransomware propagation",
"Blocks Pass-the-Hash attack vectors using admin shares"
],
"negative": [
"Remote administration tools may not work",
"Group Policy remote management affected",
"Some enterprise monitoring tools may require admin shares",
"SCCM and similar tools may need explicit shares"
],
"recommendations": {
"home_users": "Recommended - high security benefit",
"enterprise": "Requires testing - may break management tools",
"workaround": "Create explicit shares for required management tools"
}
},
"Important_Notes": [
"REQUIRES REBOOT to prevent share recreation",
"Shares will NOT be recreated after reboot (if registry set)",
"IPC$ cannot be disabled (required by Windows)",
"File sharing via explicit shares still works",
"Can be restored by setting AutoShareWks/AutoShareServer = 1 + reboot"
],
"Compatibility": {
"windows_versions": ["Windows 10", "Windows 11", "Windows Server 2016+"],
"tested": "Windows 11 25H2 (Nov 16, 2025)"
}
}

View file

@ -0,0 +1,78 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Credential Protection Configuration",
"description": "Configuration for credential hardening including WDigest protection",
"version": "2.2.0",
"WDigest_Protection": {
"description": "Prevent WDigest from storing plaintext passwords in LSASS memory",
"enabled": true,
"deprecated_in": "Windows 11 24H2",
"status": "Deprecated in Win11 24H2+ but kept for backwards compatibility and defense-in-depth",
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest",
"settings": {
"UseLogonCredential": {
"description": "Control whether WDigest stores credentials in memory",
"value": 0,
"type": "DWORD",
"values": {
"0": "Secure - Do NOT store plaintext credentials in memory",
"1": "Insecure - Store plaintext credentials in memory (VULNERABLE!)"
},
"attack_prevention": [
"Prevents Mimikatz from dumping plaintext passwords",
"Prevents Windows Credential Editor (WCE) attacks",
"Prevents other memory-dumping credential theft tools"
],
"impact": "None - Modern systems (Win 8.1+) already default to 0"
}
},
"default_behavior": {
"Windows_7": 1,
"Windows_8": 1,
"Windows_8.1": 0,
"Windows_10": 0,
"Windows_11": 0,
"Windows_11_24H2_plus": "Setting ignored (deprecated)"
},
"rationale": {
"why_set_if_deprecated": [
"Protects older Windows versions (Win7/8/Server 2008/2012)",
"Protects early Win10/11 builds that may not be fully patched",
"Defense-in-depth: Explicit is better than implicit",
"Ensures compatibility in mixed environments",
"No negative impact on Win11 24H2+ (setting is ignored)"
]
},
"microsoft_advisory": {
"kb_article": "KB2871997",
"date": "May 2014",
"title": "Update to improve credentials protection and management",
"url": "https://support.microsoft.com/en-us/topic/microsoft-security-advisory-update-to-improve-credentials-protection-and-management-may-13-2014-93434251-04ac-b7f3-52aa-9f951c14b649",
"baseline_removal": {
"version": "Windows 11 25H2 Security Baseline",
"reason": "Engineering teams deprecated this policy in Windows 11 24H2",
"url": "https://techcommunity.microsoft.com/blog/microsoft-security-baselines/windows-11-version-25h2-security-baseline/4456231"
}
}
},
"Profiles": {
"Balanced": true,
"Enterprise": true,
"Maximum": true
},
"Compatibility": {
"windows_versions": ["All Windows versions"],
"notes": [
"Setting is ignored on Windows 11 24H2+ (deprecated)",
"No compatibility issues or breakage on any Windows version",
"Recommended for all profiles for defense-in-depth"
]
}
}

View file

@ -0,0 +1,20 @@
{
"Description": "Firewall Shields Up - Block all incoming connections on Public network",
"Purpose": "Extra protection in public WiFi networks (airports, cafes, hotels)",
"Note": "This goes BEYOND Microsoft Security Baseline",
"ShieldsUp": {
"description": "Block ALL incoming connections on Public profile, including allowed apps",
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\PublicProfile",
"value_name": "DoNotAllowExceptions",
"enabled_value": 1,
"disabled_value": 0,
"profiles": {
"Balanced": false,
"Enterprise": false,
"Maximum": true
},
"warning": "When enabled, apps like Teams, Discord, Zoom cannot receive incoming calls on Public networks",
"recommendation": "Enable only for maximum security (Maximum / air-gapped profile)"
}
}

View file

@ -0,0 +1,64 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RDP Hardening Configuration",
"description": "Configuration for RDP (Remote Desktop Protocol) hardening including NLA enforcement and optional complete disable",
"version": "2.2.0",
"NLA_Enforcement": {
"description": "Network Level Authentication (NLA) enforcement settings",
"enabled": true,
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp",
"settings": {
"UserAuthentication": {
"description": "Require NLA (Network Level Authentication) before session establishment",
"value": 1,
"type": "DWORD",
"attack_prevention": "Prevents brute-force attacks before login screen appears",
"impact": "Minimal - NLA is Windows 7+ standard. May affect pre-Vista RDP clients."
},
"SecurityLayer": {
"description": "Require SSL/TLS encryption for all RDP connections",
"value": 2,
"type": "DWORD",
"attack_prevention": "Forces SSL/TLS encryption, prevents plaintext RDP traffic",
"impact": "Minimal - SSL/TLS is standard since Windows Vista"
}
}
},
"Complete_Disable": {
"description": "Complete RDP disable for air-gapped/high-security environments",
"enabled_by_default": false,
"profiles": {
"Balanced": false,
"Enterprise": false,
"Maximum": "optional"
},
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
"settings": {
"fDenyTSConnections": {
"description": "Completely disable Remote Desktop",
"value": 1,
"type": "DWORD",
"attack_prevention": "Complete RDP attack surface removal",
"impact": "HIGH - Remote administration will not work. Windows automatically adjusts firewall rules."
}
},
"requires": {
"force_parameter": true,
"domain_check": true,
"warning": "This will completely disable RDP. Remote administration will not be possible."
}
},
"Compatibility": {
"windows_versions": ["Windows 10", "Windows 11", "Windows Server 2016+"],
"minimum_rdp_client": "Windows Vista+",
"notes": [
"NLA is standard since Windows Vista / Server 2008",
"Pre-Vista clients will not be able to connect with NLA enforcement",
"Complete disable affects all remote management via RDP",
"Domain-joined systems should NOT disable RDP without explicit -Force"
]
}
}

View file

@ -0,0 +1,85 @@
{
"Description": "Software Restriction Policies (SRP) for CVE-2025-9491 Mitigation",
"Documentation": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-9491",
"CVE": "CVE-2025-9491",
"Threat": "Windows LNK Remote Code Execution",
"Status": "Zero-Day, Actively Exploited since 2017",
"Severity": "High (CVSS 7.0)",
"SRPConfiguration": {
"DefaultLevel": 262144,
"Description": "Unrestricted - Allow all programs except explicitly blocked",
"TransparentEnabled": 1,
"ExecutableTypes": [
".ADE", ".ADP", ".BAS", ".BAT", ".CHM", ".CMD", ".COM", ".CPL", ".CRT",
".EXE", ".HLP", ".HTA", ".INF", ".INS", ".ISP", ".LNK", ".MDB", ".MDE",
".MSC", ".MSI", ".MSP", ".MST", ".OCX", ".PCD", ".PIF", ".REG", ".SCR",
".SHS", ".URL", ".VB", ".WSC", ".WSF", ".WSH"
]
},
"PathRules": [
{
"Name": "Block LNK from Outlook Temp",
"Path": "%LOCALAPPDATA%\\Temp\\*.lnk",
"SecurityLevel": 0,
"Description": "Blocks .lnk files from Outlook email attachments to prevent CVE-2025-9491 exploitation. Outlook saves attachments to %LOCALAPPDATA%\\Temp\\Content.Outlook\\ before execution.",
"SaferFlags": 0,
"Enabled": true,
"AttackVector": "Email attachments (malicious.lnk via Outlook)",
"Impact": "Prevents execution of .lnk files from email attachments. Legitimate shortcuts from Start Menu/Desktop/Taskbar still work (different paths)."
},
{
"Name": "Block LNK from Downloads",
"Path": "%USERPROFILE%\\Downloads\\*.lnk",
"SecurityLevel": 0,
"Description": "Blocks .lnk files from browser Downloads folder to prevent CVE-2025-9491 exploitation from web downloads.",
"SaferFlags": 0,
"Enabled": true,
"AttackVector": "Browser downloads (malicious.lnk from web)",
"Impact": "Prevents execution of .lnk files downloaded from internet. Move .lnk to another location to execute if needed."
}
],
"Windows11BugFix": {
"Description": "Windows 11 has a bug where SRP is disabled by presence of certain keys in HKLM\\SYSTEM\\CurrentControlSet\\Control\\Srp\\Gp",
"Action": "Remove RuleCount and LastWriteTime keys",
"RegistryPath": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Srp\\Gp",
"KeysToRemove": ["RuleCount", "LastWriteTime"],
"Reason": "These keys cause SRP to be ignored on Windows 11. Removing them re-enables SRP functionality."
},
"RegistryPaths": {
"PolicyRoot": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers",
"PathRules": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers\\0\\Paths",
"Win11BugFix": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Srp\\Gp"
},
"SecurityLevels": {
"Disallowed": 0,
"Unrestricted": 262144,
"Description": "0 = Block execution, 262144 = Allow execution"
},
"SafeScenarios": [
"Start Menu shortcuts (C:\\ProgramData\\Microsoft\\Windows\\Start Menu)",
"Desktop shortcuts (C:\\Users\\<user>\\Desktop)",
"Taskbar shortcuts (pinned applications)",
"Program Files shortcuts (C:\\Program Files)",
"System shortcuts (C:\\Windows)"
],
"BlockedScenarios": [
"Outlook email attachments (%LOCALAPPDATA%\\Temp\\Content.Outlook)",
"Browser downloads (%USERPROFILE%\\Downloads)",
"Temporary Internet Files",
"Other Temp locations matching patterns"
],
"Testing": {
"VerifyBlockedPath": "%USERPROFILE%\\Downloads\\test.lnk",
"ExpectedResult": "Execution blocked with 'This program is blocked by group policy' message",
"VerifySafePath": "%USERPROFILE%\\Desktop\\test.lnk",
"ExpectedResult2": "Execution allowed (Desktop not in blocked path list)"
}
}

View file

@ -0,0 +1,57 @@
{
"Description": "Simple Windows Update Configuration - MS Best Practice (GUI Settings Only)",
"Documentation": "Matches Windows Settings > Windows Update > Advanced options",
"Purpose": "Enable immediate updates from Microsoft using Windows built-in settings",
"Settings": {
"1_ReceiveUpdatesImmediately": {
"Name": "Get the latest updates as soon as they're available",
"RegistryPath": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate",
"Values": {
"AllowOptionalContent": {
"Type": "DWord",
"Value": 1,
"Description": "Policy: enable optional content & configuration updates immediately (grays out GUI toggle)"
},
"SetAllowOptionalContent": {
"Type": "DWord",
"Value": 1,
"Description": "Policy: enforce AllowOptionalContent setting (managed by organization)"
}
},
"GUIPath": "Settings > Windows Update > Advanced options > Get the latest updates as soon as they're available"
},
"2_MicrosoftUpdate": {
"Name": "Receive updates for other Microsoft products",
"RegistryPath": "HKLM:\\SOFTWARE\\Microsoft\\WindowsUpdate\\UX\\Settings",
"Values": {
"AllowMUUpdateService": {
"Type": "DWord",
"Value": 1,
"Description": "Get updates for Office, drivers, and other Microsoft products with Windows Update"
}
},
"GUIPath": "Settings > Windows Update > Advanced options > Receive updates for other Microsoft products"
},
"3_DeliveryOptimization": {
"Name": "Downloads from other devices (DISABLED for privacy)",
"RegistryPath": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\DeliveryOptimization",
"Values": {
"DODownloadMode": {
"Type": "DWord",
"Value": 0,
"Description": "0 = HTTP only (Microsoft servers), no P2P, no LAN sharing"
}
},
"GUIPath": "Settings > Windows Update > Advanced options > Delivery Optimization > Allow downloads from other devices = OFF"
}
},
"TotalRegistryKeys": 4,
"MSBestPractice": "These are the EXACT settings shown in Windows Settings GUI - no hidden schedules, no auto-reboot config",
"UserControl": "User keeps full control over installation timing via Windows Settings (except Setting 1 is enforced by policy if enabled)",
"NoInteractivePrompt": "No mode selection needed - simple ON/ON/OFF configuration",
"CRITICAL_NOTE": "Setting 1 uses Policies\\Microsoft\\Windows\\WindowsUpdate (AllowOptionalContent/SetAllowOptionalContent) and will appear as 'managed by organization'. Setting 2 MUST use UX\\Settings path (NOT Policies path) to avoid locking the Microsoft Update toggle."
}

View file

@ -0,0 +1,380 @@
function Backup-AdvancedSecuritySettings {
<#
.SYNOPSIS
Create a comprehensive backup of all Advanced Security settings
.DESCRIPTION
Backs up all registry keys, services, firewall rules, and Windows features
that will be modified by the AdvancedSecurity module.
This is called automatically by Invoke-AdvancedSecurity before applying changes.
.EXAMPLE
Backup-AdvancedSecuritySettings
.NOTES
Uses the Core/Rollback.ps1 backup system
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Creating comprehensive backup of Advanced Security settings..." -Module "AdvancedSecurity"
$backupCount = 0
# Start module backup session
$backupSession = Start-ModuleBackup -ModuleName "AdvancedSecurity"
if (-not $backupSession) {
Write-Log -Level ERROR -Message "Failed to start backup session" -Module "AdvancedSecurity"
return $false
}
# 1. RDP Settings
Write-Log -Level DEBUG -Message "Backing up RDP settings..." -Module "AdvancedSecurity"
$rdpBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -BackupName "RDP_Settings"
if ($rdpBackup) { $backupCount++ }
# CRITICAL: Create JSON backup for RDP (Rollback fallback)
# .reg import often fails for RDP keys due to permissions, so we need values for PowerShell restore
try {
$rdpData = @{}
# System Settings
$systemPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
if (Test-Path $systemPath) {
$val = Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue
if ($val) { $rdpData["System_fDenyTSConnections"] = $val.fDenyTSConnections }
}
# Policy Settings
$policyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
if (Test-Path $policyPath) {
$val1 = Get-ItemProperty -Path $policyPath -Name "UserAuthentication" -ErrorAction SilentlyContinue
if ($val1) { $rdpData["Policy_UserAuthentication"] = $val1.UserAuthentication }
$val2 = Get-ItemProperty -Path $policyPath -Name "SecurityLayer" -ErrorAction SilentlyContinue
if ($val2) { $rdpData["Policy_SecurityLayer"] = $val2.SecurityLayer }
}
if ($rdpData.Count -gt 0) {
$rdpJson = $rdpData | ConvertTo-Json
$rdpJsonBackup = Register-Backup -Type "AdvancedSecurity" -Data $rdpJson -Name "RDP_Hardening"
if ($rdpJsonBackup) {
Write-Log -Level DEBUG -Message "Created RDP JSON backup for rollback fallback" -Module "AdvancedSecurity"
$backupCount++
}
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create RDP JSON backup: $_" -Module "AdvancedSecurity"
}
# 2. WDigest Settings
Write-Log -Level DEBUG -Message "Backing up WDigest settings..." -Module "AdvancedSecurity"
$wdigestBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" -BackupName "WDigest_Settings"
if ($wdigestBackup) { $backupCount++ }
# Discovery Protocol Settings (mDNS resolver)
Write-Log -Level DEBUG -Message "Backing up discovery protocol settings (mDNS)" -Module "AdvancedSecurity"
$mdnsBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters" -BackupName "DiscoveryProtocols_DnscacheParameters"
if ($mdnsBackup) { $backupCount++ }
# 3. Admin Shares Settings
Write-Log -Level DEBUG -Message "Backing up Admin Shares settings..." -Module "AdvancedSecurity"
$adminSharesBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" -BackupName "AdminShares_Settings"
if ($adminSharesBackup) { $backupCount++ }
# 4. TLS Settings
Write-Log -Level DEBUG -Message "Backing up TLS settings..." -Module "AdvancedSecurity"
$tlsVersions = @("TLS 1.0", "TLS 1.1")
$components = @("Server", "Client")
foreach ($version in $tlsVersions) {
foreach ($component in $components) {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$version\$component"
$tlsBackup = Backup-RegistryKey -KeyPath $regPath -BackupName "TLS_${version}_${component}".Replace(" ", "_").Replace(".", "")
if ($tlsBackup) { $backupCount++ }
}
}
# 5. WPAD Settings (3 paths: WinHttp for official MS key, Wpad for legacy, Internet Settings for AutoDetect)
Write-Log -Level DEBUG -Message "Backing up WPAD settings..." -Module "AdvancedSecurity"
$wpadPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp", # Official MS DisableWpad key
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad", # Legacy WpadOverride
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" # AutoDetect
)
foreach ($wpadPath in $wpadPaths) {
$pathName = $wpadPath.Split('\')[-1]
$wpadBackup = Backup-RegistryKey -KeyPath $wpadPath -BackupName "WPAD_${pathName}"
if ($wpadBackup) { $backupCount++ }
}
# CRITICAL: Create JSON backup for WPAD (Rollback fallback) - all paths combined
try {
$wpadData = @{}
foreach ($wpadPath in $wpadPaths) {
if (Test-Path $wpadPath) {
$wpadProps = Get-ItemProperty -Path $wpadPath -ErrorAction SilentlyContinue
# Capture all relevant properties in format expected by Rollback.ps1
# Format: "FullPath\ValueName" = Value
foreach ($prop in $wpadProps.PSObject.Properties) {
if ($prop.Name -notin @('PSPath','PSParentPath','PSChildName','PSDrive','PSProvider')) {
$fullKey = "$wpadPath\$($prop.Name)"
$wpadData[$fullKey] = $prop.Value
}
}
}
}
if ($wpadData.Count -gt 0) {
$wpadJson = $wpadData | ConvertTo-Json
$wpadJsonBackup = Register-Backup -Type "AdvancedSecurity" -Data $wpadJson -Name "WPAD"
if ($wpadJsonBackup) {
Write-Log -Level DEBUG -Message "Created WPAD JSON backup for rollback fallback ($($wpadData.Count) values)" -Module "AdvancedSecurity"
$backupCount++
}
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create WPAD JSON backup: $_" -Module "AdvancedSecurity"
}
# 6. Services (including WiFi Direct for Wireless Display and WS-Discovery)
Write-Log -Level DEBUG -Message "Backing up risky services state..." -Module "AdvancedSecurity"
# Note: Computer Browser (Browser) is deprecated in Win10/11 - not included
$services = @("SSDPSRV", "upnphost", "lmhosts", "WFDSConMgrSvc", "FDResPub", "fdPHost")
foreach ($svc in $services) {
$svcBackup = Backup-ServiceConfiguration -ServiceName $svc
if ($svcBackup) { $backupCount++ }
}
# 7. PowerShell v2 Feature State
Write-Log -Level DEBUG -Message "Backing up PowerShell v2 feature state..." -Module "AdvancedSecurity"
# Canonical detection: use Windows Optional Feature state
$psv2Feature = $null
try {
$psv2Feature = Get-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -ErrorAction SilentlyContinue
}
catch {
$psv2Feature = $null
}
if (-not $psv2Feature -or $psv2Feature.State -ne 'Enabled') {
# Feature not present or not enabled nothing to back up
Write-Log -Level INFO -Message "PowerShell v2 optional feature not enabled/present - skipping feature backup" -Module "AdvancedSecurity"
}
else {
$psv2Data = @{
FeatureName = $psv2Feature.FeatureName
State = $psv2Feature.State
DetectionMethod = "WindowsOptionalFeature"
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
} | ConvertTo-Json
$psv2Backup = Register-Backup -Type "WindowsFeature" -Data $psv2Data -Name "PowerShellV2"
if ($psv2Backup) { $backupCount++ }
}
# 8. Firewall Rules Snapshot
Write-Host ""
Write-Host " ============================================" -ForegroundColor Cyan
Write-Host " FIREWALL RULES BACKUP - PLEASE WAIT" -ForegroundColor Cyan
Write-Host " ============================================" -ForegroundColor Cyan
Write-Host " Creating snapshot for risky ports..." -ForegroundColor White
Write-Host " Ports: 79, 137-139, 1900, 2869, 5355, 3702, 5353, 5357, 5358" -ForegroundColor Gray
Write-Host ""
Write-Host " [!] This operation takes 60-120 seconds" -ForegroundColor Yellow
Write-Host " System is working - do not interrupt!" -ForegroundColor Yellow
Write-Host " ============================================" -ForegroundColor Cyan
Write-Host ""
Write-Log -Level INFO -Message "Backing up firewall rules snapshot for risky ports (79, 137, 138, 139, 1900, 2869, 5355, 3702, 5353, 5357, 5358)..." -Module "AdvancedSecurity"
$firewallRules = Get-NetFirewallRule | Where-Object {
$portFilter = $_ | Get-NetFirewallPortFilter
(($portFilter.LocalPort -in @(79, 137, 138, 139, 1900, 2869, 5355, 3702, 5353, 5357, 5358)) -or
($portFilter.RemotePort -in @(79, 137, 138, 139, 1900, 2869, 5355, 3702, 5353, 5357, 5358))) -and
($_.Direction -eq 'Inbound' -or $_.Direction -eq 'Outbound')
} | Select-Object Name, DisplayName, Enabled, Direction, Action
$firewallData = @{
Rules = $firewallRules
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
RuleCount = $firewallRules.Count
} | ConvertTo-Json -Depth 10
$firewallBackup = Register-Backup -Type "Firewall_Rules" -Data $firewallData -Name "RiskyPorts_Firewall"
if ($firewallBackup) { $backupCount++ }
Write-Host " [OK] Firewall rules backup completed ($($firewallRules.Count) rules processed)" -ForegroundColor Green
Write-Host ""
# 9. SMB Shares Snapshot
Write-Log -Level DEBUG -Message "Backing up SMB shares snapshot..." -Module "AdvancedSecurity"
# Check if LanmanServer service is running (required for Get-SmbShare)
$serverService = Get-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
if (-not $serverService -or $serverService.Status -ne 'Running') {
Write-Log -Level INFO -Message "LanmanServer service is not running - no SMB shares to backup" -Module "AdvancedSecurity"
$adminShares = @()
}
else {
try {
$adminShares = Get-SmbShare | Where-Object { $_.Name -match '^[A-Z]\$$|^ADMIN\$$' } |
Select-Object Name, Path, Description
}
catch {
Write-Log -Level INFO -Message "Could not query SMB shares: $($_.Exception.Message)" -Module "AdvancedSecurity"
$adminShares = @()
}
}
$sharesData = @{
Shares = $adminShares
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
ShareCount = $adminShares.Count
} | ConvertTo-Json -Depth 10
$sharesBackup = Register-Backup -Type "SMB_Shares" -Data $sharesData -Name "AdminShares"
if ($sharesBackup) { $backupCount++ }
$netbiosAdapters = @()
try {
$netbiosAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE" -ErrorAction SilentlyContinue
}
catch {
$netbiosAdapters = @()
}
if ($netbiosAdapters) {
$netbiosSnapshot = @()
foreach ($adapter in $netbiosAdapters) {
$netbiosSnapshot += [PSCustomObject]@{
Description = $adapter.Description
Index = $adapter.Index
TcpipNetbiosOptions = $adapter.TcpipNetbiosOptions
}
}
if ($netbiosSnapshot.Count -gt 0) {
$netbiosJson = $netbiosSnapshot | ConvertTo-Json -Depth 5
$netbiosBackup = Register-Backup -Type "AdvancedSecurity" -Data $netbiosJson -Name "NetBIOS_Adapters"
if ($netbiosBackup) { $backupCount++ }
}
}
# 10. Windows Update Settings (3 simple GUI settings)
Write-Log -Level DEBUG -Message "Backing up Windows Update settings..." -Module "AdvancedSecurity"
# Setting 1: Get latest updates immediately
$wuUXBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -BackupName "WindowsUpdate_UX_Settings"
if ($wuUXBackup) { $backupCount++ }
# Setting 1 Policy: Windows Update optional content/config updates
$wuPoliciesBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -BackupName "WindowsUpdate_Policies"
if ($wuPoliciesBackup) { $backupCount++ }
# Setting 2: Microsoft Update for other products (moved to UX\Settings - same as Setting 1)
# No separate backup needed - already backed up in WindowsUpdate_UX_Settings
# Setting 3: Delivery Optimization
$wuDOBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization" -BackupName "WindowsUpdate_DeliveryOptimization"
if ($wuDOBackup) { $backupCount++ }
# 11. SRP (Software Restriction Policies) Settings
Write-Log -Level DEBUG -Message "Backing up SRP settings..." -Module "AdvancedSecurity"
$srpBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers" -BackupName "SRP_Settings"
if ($srpBackup) { $backupCount++ }
# 12. CRITICAL: Create comprehensive JSON Pre-State Snapshot (counter Registry tattooing)
# This captures EXACT state of ALL AdvancedSecurity registry keys before hardening
Write-Log -Level INFO -Message "Creating AdvancedSecurity registry pre-state snapshot (JSON)..." -Module "AdvancedSecurity"
$preStateSnapshot = @()
# All registry keys that AdvancedSecurity modifies
$allAdvSecKeys = @(
"HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest",
"HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client",
"HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp", # Official MS DisableWpad key
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad", # Legacy WpadOverride
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", # AutoDetect
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate",
"HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\Connect", # Wireless Display / Miracast
"HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile", # Firewall Shields Up
"HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" # IPv6 disable (mitm6 mitigation)
)
foreach ($keyPath in $allAdvSecKeys) {
if (Test-Path $keyPath) {
try {
# Get all properties for this key
$properties = Get-ItemProperty -Path $keyPath -ErrorAction Stop
$propertyNames = $properties.PSObject.Properties.Name | Where-Object {
$_ -notin @('PSPath', 'PSParentPath', 'PSChildName', 'PSProvider', 'PSDrive')
}
foreach ($propName in $propertyNames) {
$propValue = $properties.$propName
# Get value type
try {
$propType = (Get-Item $keyPath).GetValueKind($propName)
}
catch {
$propType = "String" # Default fallback
}
$preStateSnapshot += [PSCustomObject]@{
Path = $keyPath
Name = $propName
Value = $propValue
Type = $propType.ToString()
Exists = $true
}
}
}
catch {
Write-Log -Level DEBUG -Message "Could not read properties from $keyPath : $_" -Module "AdvancedSecurity"
}
}
# If key doesn't exist, we don't add it to snapshot (only existing values are tracked)
}
# Save JSON snapshot
try {
$snapshotJson = $preStateSnapshot | ConvertTo-Json -Depth 5
$result = Register-Backup -Type "AdvancedSecurity" -Data $snapshotJson -Name "AdvancedSecurity_PreState"
if ($result) {
$backupCount++
Write-Log -Level SUCCESS -Message "AdvancedSecurity pre-state snapshot created ($($preStateSnapshot.Count) registry values)" -Module "AdvancedSecurity"
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create AdvancedSecurity pre-state snapshot: $_" -Module "AdvancedSecurity"
}
Write-Log -Level SUCCESS -Message "Advanced Security backup completed: $backupCount items backed up" -Module "AdvancedSecurity"
return $backupCount
}
catch {
Write-Log -Level ERROR -Message "Failed to backup Advanced Security settings: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,107 @@
function Block-FingerProtocol {
<#
.SYNOPSIS
Blocks outbound connections to TCP Port 79 (Finger protocol) via Windows Firewall
.DESCRIPTION
Creates a Windows Firewall rule to block all outbound connections to TCP port 79,
preventing abuse of the finger.exe command in ClickFix malware campaigns.
THREAT: ClickFix attacks use finger.exe to retrieve commands from remote servers
on port 79, which are then piped to cmd.exe for execution.
MITIGATION: Block outbound port 79 to prevent finger.exe from reaching C2 servers.
.PARAMETER DryRun
Preview changes without applying them
.EXAMPLE
Block-FingerProtocol
Blocks outbound finger protocol connections
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: Administrator privileges
REFERENCES:
- https://www.bleepingcomputer.com/news/security/decades-old-finger-protocol-abused-in-clickfix-malware-attacks/
- https://redteamnews.com/threat-intelligence/clickfix-malware-campaigns-resurrect-decades-old-finger-protocol-for-command-retrieval/
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
try {
$ruleName = "NoID Privacy - Block Finger Protocol (Port 79)"
Write-Log -Level INFO -Message "Checking for existing Finger protocol block rule..." -Module "AdvancedSecurity"
# Check if rule already exists
$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($existingRule) {
Write-Log -Level INFO -Message "Finger protocol block rule already exists" -Module "AdvancedSecurity"
# Show user that protection is already active
Write-Host ""
Write-Host "Finger Protocol Block: Already Protected" -ForegroundColor Green
Write-Host " Rule: $ruleName" -ForegroundColor Gray
Write-Host " Status: Active (Outbound TCP port 79 blocked)" -ForegroundColor Gray
Write-Host " Protection: ClickFix malware using finger.exe" -ForegroundColor Gray
Write-Host ""
return $true
}
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would create firewall rule to block outbound TCP port 79" -Module "AdvancedSecurity"
return $true
}
Write-Log -Level INFO -Message "Creating Windows Firewall rule to block outbound finger protocol (TCP 79)..." -Module "AdvancedSecurity"
# Create outbound firewall rule
$ruleParams = @{
DisplayName = $ruleName
Description = "Blocks outbound connections to TCP port 79 (Finger protocol) to prevent ClickFix malware attacks. The finger.exe command is abused to retrieve malicious commands from remote servers."
Direction = "Outbound"
Action = "Block"
Protocol = "TCP"
RemotePort = 79
Profile = "Any"
Enabled = "True"
ErrorAction = "Stop"
}
New-NetFirewallRule @ruleParams | Out-Null
# Verify rule was created
$verifyRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($verifyRule) {
Write-Log -Level SUCCESS -Message "Finger protocol (TCP port 79) outbound connections blocked" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "ClickFix malware campaigns using finger.exe are now mitigated" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "Firewall Rule Created:" -ForegroundColor Green
Write-Host "Name: $ruleName" -ForegroundColor Gray
Write-Host "Blocks: Outbound TCP port 79 (Finger protocol)" -ForegroundColor Gray
Write-Host "Protection: ClickFix malware using finger.exe" -ForegroundColor Gray
Write-Host ""
return $true
}
else {
Write-Log -Level ERROR -Message "Firewall rule creation failed - verification unsuccessful" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to create finger protocol block rule: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,219 @@
function Disable-AdminShares {
<#
.SYNOPSIS
Disable administrative shares (C$, ADMIN$, etc.) to prevent lateral movement
.DESCRIPTION
Disables the automatic creation of administrative shares and removes existing shares.
Administrative shares (C$, D$, ADMIN$) are used by attackers for:
- Lateral movement (WannaCry, NotPetya propagation)
- Remote file access with stolen credentials
- Pass-the-Hash attacks
- Automated malware propagation
CRITICAL: Includes domain-safety check. On domain-joined systems, admin shares
are often required for Group Policy, SCCM, and remote management tools.
REQUIRES REBOOT to prevent share recreation.
.PARAMETER Force
Force disable even on domain-joined systems (NOT RECOMMENDED for enterprise!)
.EXAMPLE
Disable-AdminShares
Disables admin shares with domain-safety check
.EXAMPLE
Disable-AdminShares -Force
Forces disable even on domain-joined systems (DANGEROUS!)
.NOTES
Impact:
- Home/Workgroup: Highly recommended
- Enterprise Domain: May break management tools - TEST FIRST!
- IPC$ cannot be removed (required by Windows)
Shares will NOT be recreated after reboot (if registry values set to 0).
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$Force
)
try {
Write-Log -Level INFO -Message "Configuring administrative shares disable..." -Module "AdvancedSecurity"
# CRITICAL: Check if system is domain-joined
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
if ($computerSystem.PartOfDomain -and -not $Force) {
Write-Log -Level WARNING -Message "Domain-joined system detected. Admin shares disable SKIPPED." -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Admin shares are often required for:" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - Group Policy management" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - SCCM/Management tools" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - Remote administration" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Use -Force to override (NOT RECOMMENDED!)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Yellow
Write-Host " DOMAIN-JOINED SYSTEM DETECTED" -ForegroundColor Yellow
Write-Host "================================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Administrative shares are often required for:" -ForegroundColor White
Write-Host " - Group Policy remote management" -ForegroundColor Gray
Write-Host " - SCCM and other management tools" -ForegroundColor Gray
Write-Host " - Remote administration via WMI/PowerShell" -ForegroundColor Gray
Write-Host ""
Write-Host "Skipping admin shares disable to prevent breakage." -ForegroundColor Green
Write-Host "Use -DisableAdminShares -Force to override (NOT RECOMMENDED)." -ForegroundColor Red
Write-Host ""
return $true # Not an error, just skipped
}
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
# Check if LanmanServer service is running (required for Get-SmbShare)
$serverService = Get-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
$serviceRunning = $serverService -and $serverService.Status -eq 'Running'
# Backup current shares and registry settings
Write-Log -Level INFO -Message "Backing up current administrative shares..." -Module "AdvancedSecurity"
if (-not $serviceRunning) {
# Server service not running - admin shares are already effectively disabled
Write-Log -Level INFO -Message "LanmanServer service is not running - admin shares already disabled" -Module "AdvancedSecurity"
$currentShares = @()
}
else {
try {
$currentShares = Get-SmbShare | Where-Object { $_.Name -match '^[A-Z]\$$|^ADMIN\$$' } |
Select-Object Name, Path, Description
}
catch {
Write-Log -Level INFO -Message "Could not query SMB shares: $($_.Exception.Message)" -Module "AdvancedSecurity"
$currentShares = @()
}
}
$backupData = @{
Shares = $currentShares
AutoShareWks = (Get-ItemProperty -Path $regPath -Name "AutoShareWks" -ErrorAction SilentlyContinue).AutoShareWks
AutoShareServer = (Get-ItemProperty -Path $regPath -Name "AutoShareServer" -ErrorAction SilentlyContinue).AutoShareServer
DomainJoined = $computerSystem.PartOfDomain
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
# Register backup
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "AdminShares_Settings" -Data $backupJson -Name "AdminShares_Disable"
Write-Log -Level INFO -Message "Backed up $($currentShares.Count) administrative shares" -Module "AdvancedSecurity"
# Disable automatic creation
Write-Log -Level INFO -Message "Disabling automatic administrative share creation..." -Module "AdvancedSecurity"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Disable for Workstation (Home/Pro)
$existing = Get-ItemProperty -Path $regPath -Name "AutoShareWks" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "AutoShareWks" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "AutoShareWks" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Disabled AutoShareWks (Workstation shares)" -Module "AdvancedSecurity"
# Disable for Server editions
$existing = Get-ItemProperty -Path $regPath -Name "AutoShareServer" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "AutoShareServer" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "AutoShareServer" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Disabled AutoShareServer (Server edition shares)" -Module "AdvancedSecurity"
# Remove existing shares
Write-Log -Level INFO -Message "Removing existing administrative shares..." -Module "AdvancedSecurity"
$removedCount = 0
$skippedShares = @()
foreach ($share in $currentShares) {
try {
Remove-SmbShare -Name $share.Name -Force -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Removed share: $($share.Name) ($($share.Path))" -Module "AdvancedSecurity"
$removedCount++
}
catch {
# ADMIN$ and C$ cannot be removed while system is running (expected behavior)
# They will NOT be recreated after reboot due to registry settings
Write-Log -Level INFO -Message "Share $($share.Name) protected by system (will not be recreated after reboot)" -Module "AdvancedSecurity"
$skippedShares += $share.Name
}
}
if ($skippedShares.Count -gt 0) {
Write-Log -Level INFO -Message "System-protected shares: $($skippedShares -join ', ') - Will NOT be recreated after reboot" -Module "AdvancedSecurity"
}
Write-Log -Level SUCCESS -Message "Removed $removedCount administrative shares, $($skippedShares.Count) protected by system" -Module "AdvancedSecurity"
# Add firewall protection for Public networks
Write-Log -Level INFO -Message "Adding firewall protection for SMB on Public networks..." -Module "AdvancedSecurity"
$firewallRuleName = "Block Admin Shares - NoID Privacy"
# Check if rule already exists
$existingRule = Get-NetFirewallRule -DisplayName $firewallRuleName -ErrorAction SilentlyContinue
if ($existingRule) {
Write-Log -Level INFO -Message "Firewall rule already exists, updating..." -Module "AdvancedSecurity"
Remove-NetFirewallRule -DisplayName $firewallRuleName -ErrorAction SilentlyContinue
}
# Create new firewall rule
New-NetFirewallRule -DisplayName $firewallRuleName `
-Direction Inbound `
-Protocol TCP `
-LocalPort 445 `
-Profile Public `
-Action Block `
-ErrorAction Stop | Out-Null
Write-Log -Level SUCCESS -Message "Firewall rule created: Block SMB (port 445) on Public networks" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " ADMINISTRATIVE SHARES DISABLED" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Registry settings:" -ForegroundColor White
Write-Host " AutoShareWks: 0 (Disabled)" -ForegroundColor Gray
Write-Host " AutoShareServer: 0 (Disabled)" -ForegroundColor Gray
Write-Host ""
Write-Host "Removed shares: $removedCount" -ForegroundColor White
if ($skippedShares.Count -gt 0) {
Write-Host "Protected shares: $($skippedShares -join ', ') (cannot be removed while running)" -ForegroundColor Gray
}
Write-Host "Firewall: SMB blocked on Public networks" -ForegroundColor White
Write-Host ""
Write-Host "IMPORTANT: REBOOT REQUIRED" -ForegroundColor Yellow
$exampleShares = if ($skippedShares.Count -gt 0) { $skippedShares -join ', ' } else { 'C$, ADMIN$' }
Write-Host "All admin shares (including $exampleShares) will NOT be recreated after reboot." -ForegroundColor Green
Write-Host ""
Write-Host "Note: IPC$ cannot be removed (required by Windows)" -ForegroundColor Gray
Write-Host "Note: Explicit file shares will still work" -ForegroundColor Gray
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to disable administrative shares: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,73 @@
function Disable-LegacyTLS {
<#
.SYNOPSIS
Disable legacy TLS 1.0 and TLS 1.1
.DESCRIPTION
Disables TLS 1.0 and TLS 1.1 for both Client and Server to prevent
BEAST, CRIME, and other attacks.
Attack Prevention: BEAST, CRIME, weak cipher suites
Impact: May break old internal web applications that haven't been updated
.EXAMPLE
Disable-LegacyTLS
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Disabling legacy TLS 1.0 and TLS 1.1..." -Module "AdvancedSecurity"
$tlsVersions = @("TLS 1.0", "TLS 1.1")
$components = @("Server", "Client")
$setCount = 0
foreach ($version in $tlsVersions) {
foreach ($component in $components) {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$version\$component"
# Create path if needed
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Disable TLS version
$existing = Get-ItemProperty -Path $regPath -Name "Enabled" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "Enabled" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "Enabled" -Value 0 -PropertyType DWord -Force | Out-Null
}
$existing = Get-ItemProperty -Path $regPath -Name "DisabledByDefault" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisabledByDefault" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "DisabledByDefault" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Disabled $version $component" -Module "AdvancedSecurity"
$setCount += 2
}
}
Write-Log -Level SUCCESS -Message "Legacy TLS disabled ($setCount registry keys set)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "Legacy TLS Disabled:" -ForegroundColor Green
Write-Host " TLS 1.0: Client + Server" -ForegroundColor Gray
Write-Host " TLS 1.1: Client + Server" -ForegroundColor Gray
Write-Host ""
Write-Host "WARNING: Old web applications may not work!" -ForegroundColor Yellow
Write-Host "Only TLS 1.2 and TLS 1.3 are now allowed." -ForegroundColor Gray
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to disable legacy TLS: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,245 @@
function Disable-RiskyPorts {
<#
.SYNOPSIS
Disable risky firewall ports (LLMNR, NetBIOS, UPnP/SSDP)
.DESCRIPTION
Closes firewall ports that are commonly exploited for MITM attacks,
network enumeration, and credential theft:
- LLMNR (Port 5355) - HIGH RISK: Responder poisoning, credential theft
- NetBIOS (Port 137-139) - MEDIUM RISK: Network enumeration
- UPnP/SSDP (Port 1900, 2869) - MEDIUM RISK: Port forwarding vulnerabilities
Uses language-independent port-based filtering to avoid DisplayName issues.
.PARAMETER SkipUPnP
Skip disabling UPnP/SSDP ports (for users who need DLNA/media streaming)
.EXAMPLE
Disable-RiskyPorts
Disables all risky firewall ports including UPnP
.EXAMPLE
Disable-RiskyPorts -SkipUPnP
Disables LLMNR and NetBIOS but keeps UPnP enabled
.NOTES
Defense in Depth: Security Baseline disables protocols via registry,
but firewall rules may still be active. This function closes the ports
at the firewall level for additional protection.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$SkipUPnP
)
try {
Write-Log -Level INFO -Message "Disabling risky firewall ports..." -Module "AdvancedSecurity"
$disabledRules = 0
$errors = @()
# PERFORMANCE: Get all firewall rules ONCE and cache port filters
Write-Log -Level INFO -Message "Loading firewall rules for analysis..." -Module "AdvancedSecurity"
$allRules = Get-NetFirewallRule | Where-Object { $_.Direction -eq 'Inbound' -and $_.Enabled -eq $true }
# Pre-fetch port filters to avoid repeated Get-NetFirewallPortFilter calls
# NOTE: We cache both the rule and its ports so we can later filter ONLY
# ALLOW rules for disabling. NoID block rules must remain enabled.
$rulesWithPorts = @()
foreach ($rule in $allRules) {
$portFilter = $rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue
if ($portFilter) {
$rulesWithPorts += [PSCustomObject]@{
Rule = $rule
LocalPort = $portFilter.LocalPort
RemotePort = $portFilter.RemotePort
}
}
}
Write-Log -Level INFO -Message "Analyzed $($rulesWithPorts.Count) firewall rules with port filters" -Module "AdvancedSecurity"
# Backup firewall rules
Write-Log -Level INFO -Message "Backing up firewall rules..." -Module "AdvancedSecurity"
$backupData = @{
FirewallRules = $allRules | Select-Object Name, DisplayName, Enabled, Direction, Action
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "Firewall_Rules" -Data $backupJson -Name "RiskyPorts_Firewall"
# 1. LLMNR (Port 5355 UDP) - HIGH RISK
Write-Log -Level INFO -Message "Disabling LLMNR firewall rules (Port 5355)..." -Module "AdvancedSecurity"
try {
# Filter from pre-loaded cache (ONLY ALLOW rules - keep NoID block rules enabled)
$llmnrRules = $rulesWithPorts | Where-Object {
($_.LocalPort -eq 5355 -or $_.RemotePort -eq 5355) -and $_.Rule.Action -eq 'Allow'
} | Select-Object -ExpandProperty Rule
foreach ($rule in $llmnrRules) {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction Stop
Write-Log -Level DEBUG -Message "Disabled LLMNR rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$disabledRules++
}
if ($llmnrRules.Count -eq 0) {
Write-Log -Level INFO -Message "No active LLMNR rules found (already disabled or not present)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Disabled $($llmnrRules.Count) LLMNR firewall rules" -Module "AdvancedSecurity"
}
}
catch {
$errors += "LLMNR: $_"
Write-Log -Level WARNING -Message "Failed to disable some LLMNR rules: $_" -Module "AdvancedSecurity"
}
# 2. NetBIOS (Port 137-139) - MEDIUM RISK
Write-Log -Level INFO -Message "Disabling NetBIOS firewall rules (Port 137-139)..." -Module "AdvancedSecurity"
try {
# Filter from pre-loaded cache (ONLY ALLOW rules - keep NoID block rules enabled)
$netbiosRules = $rulesWithPorts | Where-Object {
($_.LocalPort -in @(137, 138, 139)) -or ($_.RemotePort -in @(137, 138, 139))
} | Where-Object { $_.Rule.Action -eq 'Allow' } | Select-Object -ExpandProperty Rule
foreach ($rule in $netbiosRules) {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction Stop
Write-Log -Level DEBUG -Message "Disabled NetBIOS rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$disabledRules++
}
if ($netbiosRules.Count -eq 0) {
Write-Log -Level INFO -Message "No active NetBIOS rules found (already disabled or not present)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Disabled $($netbiosRules.Count) NetBIOS firewall rules" -Module "AdvancedSecurity"
}
}
catch {
$errors += "NetBIOS: $_"
Write-Log -Level WARNING -Message "Failed to disable some NetBIOS rules: $_" -Module "AdvancedSecurity"
}
# Also disable NetBIOS over TCP/IP on all network adapters
Write-Log -Level INFO -Message "Disabling NetBIOS over TCP/IP on all adapters..." -Module "AdvancedSecurity"
try {
$adapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE"
$adaptedCount = 0
foreach ($adapter in $adapters) {
try {
$result = Invoke-CimMethod -InputObject $adapter -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions = 2 }
if ($result.ReturnValue -eq 0) {
Write-Log -Level DEBUG -Message "Disabled NetBIOS on adapter: $($adapter.Description)" -Module "AdvancedSecurity"
$adaptedCount++
}
}
catch {
Write-Log -Level WARNING -Message "Could not disable NetBIOS on adapter $($adapter.Description): $_" -Module "AdvancedSecurity"
}
}
Write-Log -Level SUCCESS -Message "Disabled NetBIOS over TCP/IP on $adaptedCount adapters" -Module "AdvancedSecurity"
}
catch {
$errors += "NetBIOS TCP/IP: $_"
Write-Log -Level WARNING -Message "Failed to disable NetBIOS over TCP/IP: $_" -Module "AdvancedSecurity"
}
# 3. UPnP/SSDP (Port 1900, 2869) - MEDIUM RISK (conditional)
if (-not $SkipUPnP) {
Write-Log -Level INFO -Message "Disabling UPnP/SSDP firewall rules (Port 1900, 2869)..." -Module "AdvancedSecurity"
try {
# Filter from pre-loaded cache (ONLY ALLOW rules - keep NoID block rules enabled)
$upnpRules = $rulesWithPorts | Where-Object {
($_.LocalPort -in @(1900, 2869)) -or ($_.RemotePort -in @(1900, 2869))
} | Where-Object { $_.Rule.Action -eq 'Allow' } | Select-Object -ExpandProperty Rule
foreach ($rule in $upnpRules) {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction Stop
Write-Log -Level DEBUG -Message "Disabled UPnP/SSDP rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$disabledRules++
}
if ($upnpRules.Count -eq 0) {
Write-Log -Level INFO -Message "No active UPnP/SSDP rules found (already disabled or not present)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Disabled $($upnpRules.Count) UPnP/SSDP firewall rules" -Module "AdvancedSecurity"
}
}
catch {
$errors += "UPnP/SSDP: $_"
Write-Log -Level WARNING -Message "Failed to disable some UPnP/SSDP rules: $_" -Module "AdvancedSecurity"
}
# Ensure a dedicated inbound block rule exists for SSDP (UDP 1900)
try {
$ssdpRuleName = "NoID Privacy - Block SSDP (UDP 1900)"
$existingSsdpRule = Get-NetFirewallRule -DisplayName $ssdpRuleName -ErrorAction SilentlyContinue
if (-not $existingSsdpRule) {
New-NetFirewallRule -DisplayName $ssdpRuleName `
-Direction Inbound `
-Action Block `
-Enabled True `
-Protocol UDP `
-LocalPort 1900 `
-Profile Any `
-ErrorAction Stop | Out-Null
Write-Log -Level SUCCESS -Message "Created SSDP block rule: $ssdpRuleName" -Module "AdvancedSecurity"
}
else {
Write-Log -Level INFO -Message "SSDP block rule already exists: $ssdpRuleName" -Module "AdvancedSecurity"
}
}
catch {
$errors += "SSDP BlockRule: $_"
Write-Log -Level WARNING -Message "Failed to ensure SSDP block rule: $_" -Module "AdvancedSecurity"
}
}
else {
Write-Log -Level INFO -Message "UPnP/SSDP blocking skipped (user choice for DLNA compatibility)" -Module "AdvancedSecurity"
}
# Summary
if ($errors.Count -eq 0) {
if ($disabledRules -gt 0) {
Write-Log -Level SUCCESS -Message "Disabled $disabledRules risky firewall rules" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "No risky firewall rules required changes (system already protected at firewall level)" -Module "AdvancedSecurity"
}
Write-Host ""
Write-Host "Risky Firewall Ports Disabled: $disabledRules rules" -ForegroundColor Green
if ($disabledRules -eq 0) {
Write-Host " System already protected - no risky ALLOW rules were found for these ports:" -ForegroundColor Gray
}
Write-Host " - LLMNR (5355)" -ForegroundColor Gray
Write-Host " - NetBIOS (137-139)" -ForegroundColor Gray
if (-not $SkipUPnP) {
Write-Host " - UPnP/SSDP (1900, 2869)" -ForegroundColor Gray
}
else {
Write-Host " - UPnP/SSDP (1900, 2869) - SKIPPED" -ForegroundColor Yellow
}
Write-Host ""
return $true
}
else {
Write-Log -Level WARNING -Message "Completed with $($errors.Count) errors. Disabled $disabledRules rules." -Module "AdvancedSecurity"
return $true # Partial success is still success
}
}
catch {
Write-Log -Level ERROR -Message "Failed to disable risky ports: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,147 @@
function Disable-WPAD {
<#
.SYNOPSIS
Disable WPAD (Web Proxy Auto-Discovery) to prevent proxy hijacking
.DESCRIPTION
Disables WPAD auto-discovery to prevent MITM attacks and proxy hijacking.
Uses the official Microsoft-recommended registry key (DisableWpad) plus
browser-level AutoDetect settings for third-party app compatibility.
Reference: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/disable-http-proxy-auth-features
Attack Prevention: MITM attacks, proxy hijacking, credential theft
.EXAMPLE
Disable-WPAD
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Disabling WPAD (Web Proxy Auto-Discovery)..." -Module "AdvancedSecurity"
# HKLM keys (machine-wide)
# Key 1: Official Microsoft-recommended key (Windows 10 1809+ / Server 2019+)
# Key 2: Legacy WpadOverride (for older compatibility)
# Key 3: AutoDetect for HKLM (browser-level setting)
$hklmKeys = @(
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp"
Name = "DisableWpad"
Value = 1
Description = "Official MS key - disables WPAD for all WinHTTP API calls"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad"
Name = "WpadOverride"
Value = 1
Description = "Legacy override key"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
Name = "AutoDetect"
Value = 0
Description = "Browser-level auto-detect (HKLM)"
}
)
# Backup HKLM keys
$backupData = @{}
foreach ($key in $hklmKeys) {
if (Test-Path $key.Path) {
$currentValue = (Get-ItemProperty -Path $key.Path -Name $key.Name -ErrorAction SilentlyContinue).($key.Name)
$backupData["$($key.Path)\$($key.Name)"] = $currentValue
}
}
# Apply HKLM keys
$setCount = 0
foreach ($key in $hklmKeys) {
if (-not (Test-Path $key.Path)) {
New-Item -Path $key.Path -Force | Out-Null
}
$existing = Get-ItemProperty -Path $key.Path -Name $key.Name -ErrorAction SilentlyContinue
if ($null -ne $existing -and $null -ne $existing.($key.Name)) {
Set-ItemProperty -Path $key.Path -Name $key.Name -Value $key.Value -Force | Out-Null
} else {
New-ItemProperty -Path $key.Path -Name $key.Name -Value $key.Value -PropertyType DWord -Force | Out-Null
}
$setCount++
}
# HKCU key - must be set for ALL user profiles, not just current elevated admin
# When running as admin, HKCU points to admin's profile, not the logged-in user
# Solution: Iterate through all user profiles via HKU (HKEY_USERS)
$hkuPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
$hkuName = "AutoDetect"
$hkuValue = 0
# Mount HKU if not already available
if (-not (Test-Path "HKU:")) {
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue | Out-Null
}
# Get all user SIDs (excluding system accounts)
$userSIDs = Get-ChildItem -Path "HKU:\" -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match '^S-1-5-21-' -and $_.PSChildName -notmatch '_Classes$' } |
Select-Object -ExpandProperty PSChildName
foreach ($sid in $userSIDs) {
$userKeyPath = "HKU:\$sid\$hkuPath"
try {
# Backup
if (Test-Path $userKeyPath) {
$currentValue = (Get-ItemProperty -Path $userKeyPath -Name $hkuName -ErrorAction SilentlyContinue).($hkuName)
$backupData["HKU\$sid\$hkuPath\$hkuName"] = $currentValue
}
# Create path if not exists
if (-not (Test-Path $userKeyPath)) {
New-Item -Path $userKeyPath -Force | Out-Null
}
# Always use Set-ItemProperty with -Type to ensure correct value type
# Remove existing value first to avoid type conflicts
Remove-ItemProperty -Path $userKeyPath -Name $hkuName -ErrorAction SilentlyContinue
New-ItemProperty -Path $userKeyPath -Name $hkuName -Value $hkuValue -PropertyType DWord -Force | Out-Null
# Verify the value was set correctly
$verifyVal = (Get-ItemProperty -Path $userKeyPath -Name $hkuName -ErrorAction SilentlyContinue).($hkuName)
if ($verifyVal -eq $hkuValue) {
$setCount++
Write-Log -Level DEBUG -Message "WPAD AutoDetect set for SID $sid (verified: $verifyVal)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level WARNING -Message "WPAD AutoDetect verification failed for SID $sid (expected $hkuValue, got $verifyVal)" -Module "AdvancedSecurity"
}
}
catch {
Write-Log -Level DEBUG -Message "Could not set WPAD for SID $sid : $_" -Module "AdvancedSecurity"
}
}
# Also set for .DEFAULT (applies to new users)
$defaultPath = "HKU:\.DEFAULT\$hkuPath"
try {
if (-not (Test-Path $defaultPath)) {
New-Item -Path $defaultPath -Force -ErrorAction SilentlyContinue | Out-Null
}
New-ItemProperty -Path $defaultPath -Name $hkuName -Value $hkuValue -PropertyType DWord -Force -ErrorAction SilentlyContinue | Out-Null
$setCount++
}
catch {
Write-Log -Level DEBUG -Message "Could not set WPAD for .DEFAULT: $_" -Module "AdvancedSecurity"
}
Register-Backup -Type "WPAD_Settings" -Data ($backupData | ConvertTo-Json) -Name "WPAD"
Write-Log -Level SUCCESS -Message "WPAD disabled ($setCount registry keys set across all user profiles)" -Module "AdvancedSecurity"
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to disable WPAD: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,172 @@
function Enable-RdpNLA {
<#
.SYNOPSIS
Enforce Network Level Authentication (NLA) for Remote Desktop Protocol
.DESCRIPTION
HYBRID ENFORCEMENT APPROACH (Best of Security + Usability):
LEVEL 1 - ENFORCED VIA POLICIES (admin cannot disable):
- NLA (Network Level Authentication) = REQUIRED
- SSL/TLS encryption = REQUIRED
Path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services
LEVEL 2 - USER CONTROL VIA SYSTEM (admin can change in Settings):
- RDP Enable/Disable = User choice
Path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server
Attack Prevention:
- Prevents brute-force attacks before login screen appears
- Forces SSL/TLS encryption for RDP traffic (cannot be disabled)
- Requires authentication at network level (cannot be disabled)
.PARAMETER DisableRDP
Optionally completely disable RDP (for air-gapped systems)
.PARAMETER Force
Force RDP disable even on domain-joined systems (NOT RECOMMENDED)
.EXAMPLE
Enable-RdpNLA
Enforces NLA and SSL/TLS for RDP
.EXAMPLE
Enable-RdpNLA -DisableRDP -Force
Completely disables RDP (air-gapped mode)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DisableRDP,
[Parameter(Mandatory = $false)]
[switch]$Force
)
try {
Write-Log -Level INFO -Message "Configuring RDP hardening (Hybrid Enforcement)..." -Module "AdvancedSecurity"
# POLICIES PATH (enforced - admin cannot change via GUI)
$policyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
# SYSTEM PATH (user control - admin can change via Settings)
$systemPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
# Backup current settings from BOTH paths
$backupData = @{
Policy_UserAuthentication = $null
Policy_SecurityLayer = $null
System_fDenyTSConnections = $null
}
# Backup Policies path (if exists)
if (Test-Path $policyPath) {
$backupData.Policy_UserAuthentication = (Get-ItemProperty -Path $policyPath -Name "UserAuthentication" -ErrorAction SilentlyContinue).UserAuthentication
$backupData.Policy_SecurityLayer = (Get-ItemProperty -Path $policyPath -Name "SecurityLayer" -ErrorAction SilentlyContinue).SecurityLayer
}
# Backup System path (if exists)
if (Test-Path $systemPath) {
$backupData.System_fDenyTSConnections = (Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue).fDenyTSConnections
}
# Register backup
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "RDP_Settings" -Data $backupJson -Name "RDP_Hardening"
# ========================================
# LEVEL 1: ENFORCE NLA + SSL/TLS (Policies)
# ========================================
Write-Log -Level INFO -Message "LEVEL 1: Enforcing NLA + SSL/TLS via Policies (admin cannot disable)..." -Module "AdvancedSecurity"
# Create Policies path if not exists
if (-not (Test-Path $policyPath)) {
New-Item -Path $policyPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created Policies registry path" -Module "AdvancedSecurity"
}
# ENFORCE NLA (cannot be disabled by admin via GUI)
$existing = Get-ItemProperty -Path $policyPath -Name "UserAuthentication" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $policyPath -Name "UserAuthentication" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $policyPath -Name "UserAuthentication" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "NLA ENFORCED via Policies (UserAuthentication = 1)" -Module "AdvancedSecurity"
# ENFORCE SSL/TLS (cannot be disabled by admin via GUI)
$existing = Get-ItemProperty -Path $policyPath -Name "SecurityLayer" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $policyPath -Name "SecurityLayer" -Value 2 -Force | Out-Null
} else {
New-ItemProperty -Path $policyPath -Name "SecurityLayer" -Value 2 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "SSL/TLS ENFORCED via Policies (SecurityLayer = 2)" -Module "AdvancedSecurity"
# ========================================
# LEVEL 2: RDP ENABLE/DISABLE (System - User Control)
# ========================================
Write-Log -Level INFO -Message "LEVEL 2: Setting RDP enable/disable (user CAN change in Settings)..." -Module "AdvancedSecurity"
# Create System path if not exists
if (-not (Test-Path $systemPath)) {
New-Item -Path $systemPath -Force | Out-Null
}
if ($DisableRDP) {
# Check if domain-joined
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
if ($computerSystem.PartOfDomain -and -not $Force) {
Write-Log -Level WARNING -Message "Domain-joined system detected. RDP disable skipped." -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Use -Force to override (NOT RECOMMENDED for enterprise!)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "WARNING: Domain-joined system detected!" -ForegroundColor Yellow
Write-Host "Skipping RDP complete disable (may be required for management)." -ForegroundColor Yellow
Write-Host "Use -DisableRDP -Force to override (NOT RECOMMENDED)." -ForegroundColor Yellow
Write-Host ""
}
else {
# Set RDP DISABLED as default (user CAN re-enable)
$existing = Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "RDP DISABLED by default (user CAN re-enable via Settings)" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "Windows will automatically adjust RDP firewall rules" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "RDP Status: DISABLED by default" -ForegroundColor Yellow
Write-Host " You CAN re-enable RDP anytime via:" -ForegroundColor White
Write-Host " -> Settings > System > Remote Desktop > Enable Remote Desktop" -ForegroundColor Gray
Write-Host " [OK] NLA + SSL/TLS will remain ENFORCED (secure!)" -ForegroundColor Green
Write-Host ""
}
}
else {
# Set RDP ENABLED (with NLA+SSL enforced)
$existing = Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "RDP ENABLED with enforced NLA+SSL (user CAN disable via Settings)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "RDP Status: ENABLED with enforced security" -ForegroundColor Green
Write-Host " [ENFORCED] NLA (Network Level Authentication)" -ForegroundColor Green
Write-Host " [ENFORCED] SSL/TLS encryption" -ForegroundColor Green
Write-Host " You CAN disable RDP anytime via Settings if not needed" -ForegroundColor White
Write-Host ""
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to configure RDP hardening: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,98 @@
function Remove-PowerShellV2 {
<#
.SYNOPSIS
Remove PowerShell v2 to prevent downgrade attacks
.DESCRIPTION
Removes the PowerShell v2 Windows Feature to prevent downgrade attacks.
PowerShell v2 bypasses logging, AMSI, and Constrained Language Mode.
Attack Prevention: Downgrade attacks, script logging bypass, AMSI bypass
Impact: Legacy scripts using -Version 2 will not work
.EXAMPLE
Remove-PowerShellV2
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Checking PowerShell v2 optional feature state..." -Module "AdvancedSecurity"
# Canonical detection: use Windows Optional Feature state
$psv2Feature = $null
try {
$psv2Feature = Get-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -ErrorAction SilentlyContinue
}
catch {
$psv2Feature = $null
}
if (-not $psv2Feature) {
# Feature is not available on this OS (e.g. removed in newer Windows 11 builds)
Write-Log -Level INFO -Message "PowerShell v2 optional feature not available on this OS (nothing to remove)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Success = $true
Changed = $false
}
}
if ($psv2Feature.State -ne 'Enabled') {
# Feature exists but is not enabled/installed
Write-Log -Level SUCCESS -Message "PowerShell v2 feature state: $($psv2Feature.State) - no removal required" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Success = $true
Changed = $false
}
}
# PSv2 feature is enabled - proceed with backup and removal
Write-Log -Level DEBUG -Message "PowerShell v2 feature is ENABLED - preparing backup and removal via DISM..." -Module "AdvancedSecurity"
# Backup current state
$backupData = @{
FeatureName = $psv2Feature.FeatureName
State = $psv2Feature.State
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
Register-Backup -Type "WindowsFeature" -Data ($backupData | ConvertTo-Json) -Name "PowerShellV2"
# Remove PowerShell v2
Write-Log -Level WARNING -Message "Removing PowerShell v2 (this may take a moment)..." -Module "AdvancedSecurity"
Write-Host ""
Write-Host "Removing PowerShell v2..." -ForegroundColor Yellow
Write-Host "This may take up to 60 seconds..." -ForegroundColor Gray
Write-Host ""
$result = Disable-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -NoRestart -ErrorAction Stop
if ($result.RestartNeeded) {
Write-Log -Level WARNING -Message "PowerShell v2 removed - REBOOT REQUIRED to complete" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "PowerShell v2 Removed!" -ForegroundColor Green
Write-Host ""
Write-Host "IMPORTANT: REBOOT REQUIRED" -ForegroundColor Yellow
Write-Host "Changes will take effect after reboot." -ForegroundColor Gray
Write-Host ""
}
else {
Write-Log -Level SUCCESS -Message "PowerShell v2 removed successfully" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "PowerShell v2 Removed!" -ForegroundColor Green
Write-Host ""
}
return [PSCustomObject]@{
Success = $true
Changed = $true
}
}
catch {
Write-Log -Level ERROR -Message "Failed to remove PowerShell v2: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Success = $false
Changed = $false
}
}
}

View file

@ -0,0 +1,150 @@
function Set-DiscoveryProtocolsSecurity {
<#
.SYNOPSIS
Completely hardens discovery protocols (WS-Discovery + mDNS) for air-gapped systems.
.DESCRIPTION
This function is designed for the AdvancedSecurity **Maximum** profile.
It applies the following changes:
- Disables OS-level mDNS client resolution
- Stops and disables WS-Discovery related services
- Adds explicit Windows Firewall BLOCK rules for WS-Discovery and mDNS ports
Protocols/ports affected:
- WS-Discovery: UDP 3702, TCP 5357/5358
- mDNS: UDP 5353
NOTE: Backup for services, registry and firewall rules is handled centrally by
Backup-AdvancedSecuritySettings and the Core rollback system.
.PARAMETER DisableCompletely
When present, applies full discovery protocol hardening. Currently this
function is only called with -DisableCompletely in Maximum profile.
.EXAMPLE
Set-DiscoveryProtocolsSecurity -DisableCompletely
# Completely disables WS-Discovery and mDNS on this host.
#>
[CmdletBinding()]
param(
[switch]$DisableCompletely
)
try {
Write-Log -Level INFO -Message "Applying discovery protocol security (WS-Discovery + mDNS)... DisableCompletely: $DisableCompletely" -Module "AdvancedSecurity"
if (-not $DisableCompletely) {
Write-Log -Level INFO -Message "Set-DiscoveryProtocolsSecurity called without -DisableCompletely. No changes applied." -Module "AdvancedSecurity"
return $true
}
$changesApplied = 0
# =============================
# 1) Disable mDNS via DNS Client parameters
# =============================
$dnsParamsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
if (-not (Test-Path $dnsParamsPath)) {
New-Item -Path $dnsParamsPath -Force | Out-Null
Write-Log -Level INFO -Message "Created registry key: $dnsParamsPath" -Module "AdvancedSecurity"
}
$mdnsProps = Get-ItemProperty -Path $dnsParamsPath -ErrorAction SilentlyContinue
$currentEnableMdns = if ($mdnsProps) { $mdnsProps.EnableMDNS } else { $null }
if ($currentEnableMdns -ne 0) {
New-ItemProperty -Path $dnsParamsPath -Name "EnableMDNS" -Value 0 -PropertyType DWord -Force | Out-Null
Write-Log -Level INFO -Message "Set EnableMDNS = 0 (Disable OS mDNS resolver)" -Module "AdvancedSecurity"
$changesApplied++
}
# =============================
# 2) Stop and disable WS-Discovery related services
# =============================
$wsdServices = @(
@{ Name = "FDResPub"; DisplayName = "Function Discovery Resource Publication" },
@{ Name = "fdPHost"; DisplayName = "Function Discovery Provider Host" }
)
foreach ($svc in $wsdServices) {
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if (-not $service) {
Write-Log -Level INFO -Message "Service $($svc.Name) not found (may not be installed)" -Module "AdvancedSecurity"
continue
}
if ($service.Status -eq 'Running') {
try {
Stop-Service -Name $svc.Name -Force -ErrorAction Stop
Write-Log -Level INFO -Message "Stopped service: $($svc.Name) ($($svc.DisplayName))" -Module "AdvancedSecurity"
$changesApplied++
}
catch {
Write-Log -Level WARNING -Message "Failed to stop service $($svc.Name): $_" -Module "AdvancedSecurity"
}
}
if ($service.StartType -ne 'Disabled') {
try {
Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop
Write-Log -Level INFO -Message "Set service $($svc.Name) StartupType = Disabled" -Module "AdvancedSecurity"
$changesApplied++
}
catch {
Write-Log -Level WARNING -Message "Failed to set StartupType=Disabled for $($svc.Name): $_" -Module "AdvancedSecurity"
}
}
}
# =============================
# 3) Add firewall BLOCK rules for WS-Discovery and mDNS
# =============================
$firewallRules = @(
@{ Name = "NoID-Block-WSD-UDP-3702"; DisplayName = "NoID Privacy - Block WS-Discovery UDP 3702"; Protocol = "UDP"; LocalPort = 3702 },
@{ Name = "NoID-Block-WSD-TCP-5357"; DisplayName = "NoID Privacy - Block WS-Discovery HTTP TCP 5357"; Protocol = "TCP"; LocalPort = 5357 },
@{ Name = "NoID-Block-WSD-TCP-5358"; DisplayName = "NoID Privacy - Block WS-Discovery HTTPS TCP 5358"; Protocol = "TCP"; LocalPort = 5358 },
@{ Name = "NoID-Block-mDNS-UDP-5353"; DisplayName = "NoID Privacy - Block mDNS UDP 5353"; Protocol = "UDP"; LocalPort = 5353 }
)
foreach ($rule in $firewallRules) {
try {
$existing = Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
if (-not $existing) {
New-NetFirewallRule -Name $rule.Name `
-DisplayName $rule.DisplayName `
-Direction Inbound `
-Protocol $rule.Protocol `
-LocalPort $rule.LocalPort `
-Action Block `
-Profile Any `
-Enabled True | Out-Null
Write-Log -Level INFO -Message "Created firewall rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$changesApplied++
}
else {
# Ensure rule is enabled and blocking
Set-NetFirewallRule -Name $rule.Name -Enabled True -Action Block -ErrorAction SilentlyContinue
Write-Log -Level DEBUG -Message "Firewall rule already exists and was enforced: $($rule.DisplayName)" -Module "AdvancedSecurity"
}
}
catch {
Write-Log -Level WARNING -Message "Failed to ensure firewall rule $($rule.DisplayName): $_" -Module "AdvancedSecurity"
}
}
if ($changesApplied -eq 0) {
Write-Log -Level SUCCESS -Message "Discovery protocol security already configured (no changes needed)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Discovery protocol security applied ($changesApplied changes)" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to apply discovery protocol security (WS-Discovery/mDNS): $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,66 @@
function Set-FirewallShieldsUp {
<#
.SYNOPSIS
Enable "Shields Up" mode - Block ALL incoming connections on Public network
.DESCRIPTION
Sets DoNotAllowExceptions=1 for PublicProfile firewall.
This blocks ALL incoming connections, even from allowed apps.
Goes BEYOND Microsoft Security Baseline.
.PARAMETER Enable
Enable Shields Up mode (block all incoming on Public)
.PARAMETER Disable
Disable Shields Up mode (allow configured exceptions)
#>
[CmdletBinding()]
param(
[switch]$Enable,
[switch]$Disable
)
$moduleName = "AdvancedSecurity"
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile"
$valueName = "DoNotAllowExceptions"
try {
if ($Enable) {
Write-Log -Level INFO -Message "Enabling Firewall Shields Up mode (Public profile)..." -Module $moduleName
# Ensure path exists
if (!(Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Set DoNotAllowExceptions = 1
Set-ItemProperty -Path $regPath -Name $valueName -Value 1 -Type DWord -Force
Write-Log -Level SUCCESS -Message "Firewall Shields Up ENABLED - All incoming connections blocked on Public network" -Module $moduleName
Write-Host ""
Write-Host " SHIELDS UP: Public network now blocks ALL incoming connections" -ForegroundColor Green
Write-Host " This includes allowed apps (Teams, Discord, etc. cannot receive calls)" -ForegroundColor Yellow
Write-Host ""
return $true
}
elseif ($Disable) {
Write-Log -Level INFO -Message "Disabling Firewall Shields Up mode..." -Module $moduleName
if (Test-Path $regPath) {
Set-ItemProperty -Path $regPath -Name $valueName -Value 0 -Type DWord -Force
}
Write-Log -Level SUCCESS -Message "Firewall Shields Up disabled - Normal firewall exceptions apply" -Module $moduleName
return $true
}
else {
Write-Log -Level WARNING -Message "No action specified for Set-FirewallShieldsUp" -Module $moduleName
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to set Firewall Shields Up: $_" -Module $moduleName
return $false
}
}

View file

@ -0,0 +1,102 @@
function Set-IPv6Security {
<#
.SYNOPSIS
Disable IPv6 to prevent DHCPv6/Router Solicitation attacks (mitm6)
.DESCRIPTION
Disables IPv6 via registry to prevent:
- mitm6 attacks (DHCPv6 spoofing DNS takeover NTLM relay)
- IPv6 Router Advertisement spoofing
- DHCPv6 poisoning attacks
This is the recommended mitigation per Fox-IT security research.
WARNING: May break Exchange Server and some Active Directory features.
Only recommended for air-gapped or high-security systems.
.PARAMETER DisableCompletely
If true, completely disables IPv6 (DisabledComponents = 0xFF)
.EXAMPLE
Set-IPv6Security -DisableCompletely
.NOTES
Registry: HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\DisabledComponents
Value 0xFF = Disable all IPv6 components
REBOOT REQUIRED for changes to take effect.
References:
- https://blog.fox-it.com/2018/01/11/mitm6-compromising-ipv4-networks-via-ipv6/
- https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows
#>
[CmdletBinding()]
param(
[switch]$DisableCompletely
)
try {
if (-not $DisableCompletely) {
Write-Log -Level INFO -Message "IPv6 disable not requested - keeping default configuration" -Module "AdvancedSecurity"
return $true
}
Write-Log -Level INFO -Message "Disabling IPv6 (mitm6 attack mitigation)..." -Module "AdvancedSecurity"
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters"
# Backup current value
$currentValue = Get-ItemProperty -Path $regPath -Name "DisabledComponents" -ErrorAction SilentlyContinue
$backupData = @{
Path = $regPath
Name = "DisabledComponents"
PreviousValue = if ($currentValue) { $currentValue.DisabledComponents } else { "_NOT_SET" }
NewValue = 255
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "Registry" -Data $backupJson -Name "IPv6_DisabledComponents"
# Ensure registry path exists
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Set DisabledComponents to 0xFF (255) = Disable all IPv6 components
Set-ItemProperty -Path $regPath -Name "DisabledComponents" -Value 255 -Type DWord -Force
Write-Log -Level SUCCESS -Message "IPv6 disabled (DisabledComponents = 0xFF)" -Module "AdvancedSecurity"
# Verify
$verifyValue = (Get-ItemProperty -Path $regPath -Name "DisabledComponents" -ErrorAction SilentlyContinue).DisabledComponents
if ($verifyValue -eq 255) {
Write-Log -Level SUCCESS -Message "IPv6 disable verified - REBOOT REQUIRED" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Yellow
Write-Host " IPv6 DISABLED (mitm6 Attack Mitigation)" -ForegroundColor Yellow
Write-Host "================================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Registry: DisabledComponents = 0xFF (255)" -ForegroundColor White
Write-Host ""
Write-Host "Protection against:" -ForegroundColor Cyan
Write-Host " - DHCPv6 spoofing (mitm6 tool)" -ForegroundColor Gray
Write-Host " - IPv6 Router Advertisement attacks" -ForegroundColor Gray
Write-Host " - DNS takeover via fake DHCPv6 server" -ForegroundColor Gray
Write-Host " - NTLM credential relay attacks" -ForegroundColor Gray
Write-Host ""
Write-Host "REBOOT REQUIRED for changes to take effect!" -ForegroundColor Red
Write-Host ""
return $true
}
else {
Write-Log -Level ERROR -Message "IPv6 disable verification failed" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to disable IPv6: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,192 @@
function Set-SRPRules {
<#
.SYNOPSIS
Configures Software Restriction Policies (SRP) to block .lnk execution from Temp/Downloads
.DESCRIPTION
Implements SRP rules to mitigate CVE-2025-9491 (Windows LNK Remote Code Execution).
CRITICAL ZERO-DAY MITIGATION:
- CVE-2025-9491: Actively exploited since 2017
- No patch available (Microsoft: "does not meet servicing threshold")
- ASR and SmartScreen do NOT protect against this
SRP Rules Created:
1. Block *.lnk from %LOCALAPPDATA%\Temp\* (Outlook attachments)
2. Block *.lnk from %USERPROFILE%\Downloads\* (Browser downloads)
Windows 11 Bug Fix:
- Removes buggy registry keys that disable SRP on Win11
.PARAMETER DryRun
Preview changes without applying them
.EXAMPLE
Set-SRPRules
Applies SRP rules to block malicious .lnk execution
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: Administrator privileges
REFERENCES:
- CVE-2025-9491: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-9491
- CISA KEV: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- SRP Documentation: https://docs.microsoft.com/windows/security/threat-protection/windows-defender-application-control/applocker/software-restriction-policies
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
try {
$configPath = Join-Path $PSScriptRoot "..\Config\SRP-Rules.json"
if (-not (Test-Path $configPath)) {
Write-Log -Level ERROR -Message "SRP-Rules.json not found: $configPath" -Module "AdvancedSecurity"
return $false
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
Write-Log -Level INFO -Message "Configuring Software Restriction Policies (SRP) for CVE-2025-9491..." -Module "AdvancedSecurity"
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would configure SRP with following rules:" -Module "AdvancedSecurity"
foreach ($rule in $config.PathRules) {
Write-Log -Level INFO -Message "[DRYRUN] - $($rule.Name): $($rule.Path)" -Module "AdvancedSecurity"
}
return $true
}
# Step 1: Create SRP Policy Root
$policyRoot = $config.RegistryPaths.PolicyRoot
if (-not (Test-Path $policyRoot)) {
Write-Log -Level INFO -Message "Creating SRP policy root: $policyRoot" -Module "AdvancedSecurity"
New-Item -Path $policyRoot -Force | Out-Null
}
# Step 2: Set Default Level (Unrestricted)
Write-Log -Level INFO -Message "Setting SRP default level to Unrestricted (262144)" -Module "AdvancedSecurity"
$existingDefaultLevel = Get-ItemProperty -Path $policyRoot -Name "DefaultLevel" -ErrorAction SilentlyContinue
if ($null -ne $existingDefaultLevel) {
Set-ItemProperty -Path $policyRoot -Name "DefaultLevel" -Value $config.SRPConfiguration.DefaultLevel -Force | Out-Null
}
else {
New-ItemProperty -Path $policyRoot -Name "DefaultLevel" -Value $config.SRPConfiguration.DefaultLevel -PropertyType DWord -Force | Out-Null
}
# Step 3: Enable Transparent Enforcement
$existingTransparent = Get-ItemProperty -Path $policyRoot -Name "TransparentEnabled" -ErrorAction SilentlyContinue
if ($null -ne $existingTransparent) {
Set-ItemProperty -Path $policyRoot -Name "TransparentEnabled" -Value $config.SRPConfiguration.TransparentEnabled -Force | Out-Null
}
else {
New-ItemProperty -Path $policyRoot -Name "TransparentEnabled" -Value $config.SRPConfiguration.TransparentEnabled -PropertyType DWord -Force | Out-Null
}
# Step 4: Create Path Rules
$pathRulesRoot = $config.RegistryPaths.PathRules
if (-not (Test-Path $pathRulesRoot)) {
Write-Log -Level INFO -Message "Creating SRP path rules root: $pathRulesRoot" -Module "AdvancedSecurity"
New-Item -Path $pathRulesRoot -Force | Out-Null
}
$rulesCreated = 0
foreach ($rule in $config.PathRules) {
if (-not $rule.Enabled) {
Write-Log -Level INFO -Message "Skipping disabled rule: $($rule.Name)" -Module "AdvancedSecurity"
continue
}
# Generate GUID for rule
$ruleGuid = "{$([guid]::NewGuid().ToString())}"
$rulePath = Join-Path $pathRulesRoot $ruleGuid
Write-Log -Level INFO -Message "Creating SRP rule: $($rule.Name)" -Module "AdvancedSecurity"
# Create rule key
if (-not (Test-Path $rulePath)) {
New-Item -Path $rulePath -Force | Out-Null
}
# Set ItemData (path pattern)
$existingItemData = Get-ItemProperty -Path $rulePath -Name "ItemData" -ErrorAction SilentlyContinue
if ($null -ne $existingItemData) {
Set-ItemProperty -Path $rulePath -Name "ItemData" -Value $rule.Path -Force | Out-Null
}
else {
New-ItemProperty -Path $rulePath -Name "ItemData" -Value $rule.Path -PropertyType ExpandString -Force | Out-Null
}
# Set Description
$existingDescription = Get-ItemProperty -Path $rulePath -Name "Description" -ErrorAction SilentlyContinue
if ($null -ne $existingDescription) {
Set-ItemProperty -Path $rulePath -Name "Description" -Value $rule.Description -Force | Out-Null
}
else {
New-ItemProperty -Path $rulePath -Name "Description" -Value $rule.Description -PropertyType String -Force | Out-Null
}
# Set SaferFlags
$existingSaferFlags = Get-ItemProperty -Path $rulePath -Name "SaferFlags" -ErrorAction SilentlyContinue
if ($null -ne $existingSaferFlags) {
Set-ItemProperty -Path $rulePath -Name "SaferFlags" -Value $rule.SaferFlags -Force | Out-Null
}
else {
New-ItemProperty -Path $rulePath -Name "SaferFlags" -Value $rule.SaferFlags -PropertyType DWord -Force | Out-Null
}
$rulesCreated++
Write-Log -Level SUCCESS -Message "SRP rule created: $($rule.Name) -> $($rule.Path)" -Module "AdvancedSecurity"
}
# Step 5: Windows 11 Bug Fix
$bugFixPath = $config.RegistryPaths.Win11BugFix
if (Test-Path $bugFixPath) {
Write-Log -Level INFO -Message "Applying Windows 11 SRP bug fix..." -Module "AdvancedSecurity"
foreach ($keyName in $config.Windows11BugFix.KeysToRemove) {
$keyExists = Get-ItemProperty -Path $bugFixPath -Name $keyName -ErrorAction SilentlyContinue
if ($null -ne $keyExists) {
Remove-ItemProperty -Path $bugFixPath -Name $keyName -Force -ErrorAction SilentlyContinue
Write-Log -Level SUCCESS -Message "Removed buggy key: $keyName (Windows 11 SRP fix)" -Module "AdvancedSecurity"
}
}
}
Write-Log -Level SUCCESS -Message "SRP configuration completed: $rulesCreated rules created" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "CVE-2025-9491 mitigation active - .lnk files from Temp/Downloads now blocked" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " SRP RULES CONFIGURED (CVE-2025-9491)" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Zero-Day Protection: Windows LNK RCE (ACTIVELY EXPLOITED)" -ForegroundColor Yellow
Write-Host ""
Write-Host "Rules Created: $rulesCreated" -ForegroundColor Cyan
Write-Host "Protected Paths:" -ForegroundColor White
Write-Host " - Outlook Temp (%LOCALAPPDATA%\Temp\*.lnk)" -ForegroundColor Gray
Write-Host " - Downloads (%USERPROFILE%\Downloads\*.lnk)" -ForegroundColor Gray
Write-Host ""
Write-Host "Status: ACTIVE (malicious .lnk files blocked)" -ForegroundColor Green
Write-Host "CVE-2025-9491: MITIGATED" -ForegroundColor Green
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to configure SRP rules: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,113 @@
function Set-WDigestProtection {
<#
.SYNOPSIS
Disable WDigest credential caching to prevent plaintext password storage in LSASS
.DESCRIPTION
Configures WDigest to NOT store plaintext credentials in LSASS memory.
Prevents Mimikatz, Windows Credential Editor (WCE), and other memory-dumping
tools from extracting plaintext passwords.
Status: This setting is DEPRECATED in Windows 11 24H2+ (default is already secure),
but we set it explicitly for:
- Backwards compatibility with older Windows versions
- Defense-in-depth (explicit is better than implicit)
- Mixed environments with Win7/8/Server 2008/2012
No negative impact on modern systems (setting is ignored on Win11 24H2+).
.EXAMPLE
Set-WDigestProtection
Sets UseLogonCredential = 0 to prevent plaintext credential storage
.NOTES
Microsoft Security Advisory: KB2871997 (May 2014)
Deprecated in Windows 11 24H2 Security Baseline (September 2024)
Default behavior:
- Windows 7/8/Server 2008/2012: UseLogonCredential = 1 (INSECURE!)
- Windows 8.1+: UseLogonCredential = 0 (Secure)
- Windows 11 24H2+: Setting ignored (hardcoded secure)
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Configuring WDigest credential protection..." -Module "AdvancedSecurity"
$wdigestRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest"
# Check Windows version for informational logging
$osVersion = [System.Environment]::OSVersion.Version
$isWin11 = $osVersion.Major -ge 10 -and $osVersion.Build -ge 22000
if ($isWin11 -and $osVersion.Build -ge 26100) {
# Windows 11 24H2+ (Build 26100+)
Write-Log -Level INFO -Message "Windows 11 24H2+ detected - WDigest setting is deprecated but will be set for backwards compatibility" -Module "AdvancedSecurity"
}
# Backup current setting
$currentValue = $null
if (Test-Path $wdigestRegPath) {
$currentValue = (Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -ErrorAction SilentlyContinue).UseLogonCredential
}
$backupData = @{
OriginalValue = $currentValue
RegistryPath = $wdigestRegPath
SettingName = "UseLogonCredential"
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
# Register backup
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "WDigest_Settings" -Data $backupJson -Name "WDigest_Protection"
# Create registry path if it doesn't exist
if (-not (Test-Path $wdigestRegPath)) {
Write-Log -Level INFO -Message "Creating WDigest registry path..." -Module "AdvancedSecurity"
New-Item -Path $wdigestRegPath -Force | Out-Null
}
# Set UseLogonCredential = 0 (Secure - no plaintext in memory)
$existing = Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -Value 0 -PropertyType DWord -Force | Out-Null
}
# Verify
$newValue = (Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential").UseLogonCredential
if ($newValue -eq 0) {
if ($currentValue -eq 1) {
Write-Log -Level SUCCESS -Message "WDigest credential protection enabled (UseLogonCredential = 0)" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Previous value was 1 (INSECURE) - system was vulnerable to plaintext credential dumps!" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "SECURITY IMPROVEMENT: WDigest was storing plaintext credentials!" -ForegroundColor Yellow
Write-Host "This has now been FIXED. Plaintext credential storage is disabled." -ForegroundColor Green
Write-Host ""
}
elseif ($null -eq $currentValue) {
Write-Log -Level SUCCESS -Message "WDigest credential protection configured (UseLogonCredential = 0)" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "WDigest setting was not previously configured (default varies by OS version)" -Module "AdvancedSecurity"
}
else {
# currentValue was already 0
Write-Log -Level SUCCESS -Message "WDigest credential protection verified (UseLogonCredential = 0)" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "Setting was already correct (no change needed)" -Module "AdvancedSecurity"
}
return $true
}
else {
Write-Log -Level ERROR -Message "Failed to verify WDigest setting (expected 0, got $newValue)" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to configure WDigest protection: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,110 @@
function Set-WindowsUpdate {
<#
.SYNOPSIS
Configures Windows Update using simple GUI-equivalent settings
.DESCRIPTION
Applies 3 simple Windows Update settings that align with the Windows Settings GUI:
1. Get the latest updates as soon as they're available (ON, enforced via policy)
2. Receive updates for other Microsoft products (ON, user-toggleable)
3. Delivery Optimization - Downloads from other devices (OFF, enforced via policy)
NO forced schedules and NO auto-reboot policies are configured.
Installation timing remains user-controlled via the Windows Update GUI; where
policies are used, Windows clearly indicates that "Some settings are managed
by your organization".
.PARAMETER DryRun
Preview changes without applying them
.EXAMPLE
Set-WindowsUpdate
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: Administrator privileges
Based on: Windows Settings > Windows Update > Advanced options
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
try {
$configPath = Join-Path $PSScriptRoot "..\Config\WindowsUpdate.json"
if (-not (Test-Path $configPath)) {
Write-Log -Level ERROR -Message "WindowsUpdate.json not found: $configPath" -Module "AdvancedSecurity"
return $false
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
Write-Log -Level INFO -Message "Configuring Windows Update (3 simple GUI settings)..." -Module "AdvancedSecurity"
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would configure 3 Windows Update settings" -Module "AdvancedSecurity"
return $true
}
$settingsApplied = 0
# Loop through all 3 settings from config
foreach ($settingKey in $config.Settings.PSObject.Properties.Name) {
$setting = $config.Settings.$settingKey
$regPath = $setting.RegistryPath
# Ensure registry path exists
if (-not (Test-Path $regPath)) {
Write-Log -Level DEBUG -Message "Creating registry path: $regPath" -Module "AdvancedSecurity"
New-Item -Path $regPath -Force | Out-Null
}
# Apply each value in this setting
foreach ($valueName in $setting.Values.PSObject.Properties.Name) {
$valueData = $setting.Values.$valueName
# Always use New-ItemProperty with -Force to ensure correct type and value
# -Force will overwrite existing keys
New-ItemProperty -Path $regPath -Name $valueName -Value $valueData.Value -PropertyType DWord -Force | Out-Null
Write-Log -Level SUCCESS -Message "$($setting.Name): $valueName = $($valueData.Value)" -Module "AdvancedSecurity"
$settingsApplied++
}
}
Write-Log -Level SUCCESS -Message "Windows Update configured: $settingsApplied registry keys set" -Module "AdvancedSecurity"
# Restart Windows Update service to apply changes immediately
Write-Log -Level INFO -Message "Restarting Windows Update service to apply changes..." -Module "AdvancedSecurity"
try {
Restart-Service -Name wuauserv -Force -ErrorAction Stop | Out-Null
Write-Log -Level SUCCESS -Message "Windows Update service restarted successfully" -Module "AdvancedSecurity"
}
catch {
Write-Log -Level WARNING -Message "Could not restart Windows Update service: $($_.Exception.Message)" -Module "AdvancedSecurity"
}
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " Windows Update Configured (3 Settings)" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "[1] Get latest updates immediately: ON (Policy)" -ForegroundColor Gray
Write-Host "[2] Microsoft Update (Office, etc.): ON (User can toggle)" -ForegroundColor Gray
Write-Host "[3] P2P Delivery Optimization: OFF (Policy)" -ForegroundColor Gray
Write-Host ""
Write-Host "Installation timing remains user-controlled (no forced schedules, no auto-reboot policies)." -ForegroundColor White
Write-Host "Windows will indicate where settings are managed by policy in the GUI." -ForegroundColor White
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to configure Windows Update: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,200 @@
function Set-WirelessDisplaySecurity {
<#
.SYNOPSIS
Hardens Wireless Display (Miracast) settings to prevent screen interception attacks.
.DESCRIPTION
Configures Windows Wireless Display policies to prevent attackers from:
- Setting up rogue Miracast receivers to capture your screen
- Using your PC as an unauthorized display receiver
- Intercepting screen content via mDNS spoofing
Default (always applied): Blocks receiving projections, requires PIN for pairing
Full disable: Also blocks sending projections and mDNS discovery
.PARAMETER DisableCompletely
If specified, completely disables all Wireless Display functionality.
Default: Only hardens (blocks receiving, requires PIN) but allows sending.
.EXAMPLE
Set-WirelessDisplaySecurity
# Applies default hardening (blocks receiving, requires PIN)
.EXAMPLE
Set-WirelessDisplaySecurity -DisableCompletely
# Completely disables all Wireless Display functionality
#>
[CmdletBinding()]
param(
[switch]$DisableCompletely
)
try {
Write-Log -Level INFO -Message "Applying Wireless Display security hardening (DisableCompletely: $DisableCompletely)..." -Module "AdvancedSecurity"
$changesApplied = 0
# Registry path for Wireless Display policies
$connectPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Connect"
# Create key if it doesn't exist
if (-not (Test-Path $connectPath)) {
New-Item -Path $connectPath -Force | Out-Null
Write-Log -Level INFO -Message "Created registry key: $connectPath" -Module "AdvancedSecurity"
}
# ============================================
# ALWAYS APPLIED (Default hardening for all profiles)
# ============================================
# 1. AllowProjectionToPC = 0 (Block receiving projections - prevents rogue receiver attacks)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionToPC" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionToPC -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionToPC" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionToPC = 0 (Block receiving)" -Module "AdvancedSecurity"
$changesApplied++
}
# 2. RequirePinForPairing = 2 (Always require PIN - prevents unauthorized pairing)
$currentValue = Get-ItemProperty -Path $connectPath -Name "RequirePinForPairing" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.RequirePinForPairing -ne 2) {
Set-ItemProperty -Path $connectPath -Name "RequirePinForPairing" -Value 2 -Type DWord -Force
Write-Log -Level INFO -Message "Set RequirePinForPairing = 2 (Always require PIN)" -Module "AdvancedSecurity"
$changesApplied++
}
# ============================================
# OPTIONAL: Complete disable (user choice)
# ============================================
if ($DisableCompletely) {
Write-Log -Level INFO -Message "Applying complete Wireless Display disable..." -Module "AdvancedSecurity"
# 3. AllowProjectionFromPC = 0 (Block sending projections)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionFromPC" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionFromPC -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionFromPC" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionFromPC = 0 (Block sending)" -Module "AdvancedSecurity"
$changesApplied++
}
# 4. AllowMdnsAdvertisement = 0 (Don't advertise as receiver)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowMdnsAdvertisement" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowMdnsAdvertisement -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowMdnsAdvertisement" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowMdnsAdvertisement = 0 (No mDNS ads)" -Module "AdvancedSecurity"
$changesApplied++
}
# 5. AllowMdnsDiscovery = 0 (Don't discover receivers via mDNS)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowMdnsDiscovery" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowMdnsDiscovery -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowMdnsDiscovery" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowMdnsDiscovery = 0 (No mDNS discovery)" -Module "AdvancedSecurity"
$changesApplied++
}
# 6. AllowProjectionFromPCOverInfrastructure = 0 (Block infrastructure projection)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionFromPCOverInfrastructure" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionFromPCOverInfrastructure -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionFromPCOverInfrastructure" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionFromPCOverInfrastructure = 0" -Module "AdvancedSecurity"
$changesApplied++
}
# 7. AllowProjectionToPCOverInfrastructure = 0 (Block infrastructure receiving)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionToPCOverInfrastructure" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionToPCOverInfrastructure -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionToPCOverInfrastructure" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionToPCOverInfrastructure = 0" -Module "AdvancedSecurity"
$changesApplied++
}
# 8. Block Miracast ports via Windows Firewall (7236, 7250)
$firewallRules = @(
@{
Name = "NoID-Block-Miracast-TCP-7236"
DisplayName = "NoID Privacy - Block Miracast TCP 7236"
Direction = "Inbound"
Protocol = "TCP"
LocalPort = 7236
},
@{
Name = "NoID-Block-Miracast-TCP-7250"
DisplayName = "NoID Privacy - Block Miracast TCP 7250"
Direction = "Inbound"
Protocol = "TCP"
LocalPort = 7250
},
@{
Name = "NoID-Block-Miracast-UDP-7236"
DisplayName = "NoID Privacy - Block Miracast UDP 7236"
Direction = "Inbound"
Protocol = "UDP"
LocalPort = 7236
},
@{
Name = "NoID-Block-Miracast-UDP-7250"
DisplayName = "NoID Privacy - Block Miracast UDP 7250"
Direction = "Inbound"
Protocol = "UDP"
LocalPort = 7250
}
)
foreach ($rule in $firewallRules) {
$existingRule = Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
if (-not $existingRule) {
New-NetFirewallRule -Name $rule.Name `
-DisplayName $rule.DisplayName `
-Direction $rule.Direction `
-Protocol $rule.Protocol `
-LocalPort $rule.LocalPort `
-Action Block `
-Profile Any `
-Enabled True | Out-Null
Write-Log -Level INFO -Message "Created firewall rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$changesApplied++
}
}
# 9. Disable WiFi Direct Service (WFDSConMgrSvc) - CRITICAL for complete Miracast block
# Registry policies alone don't block WiFi Direct P2P discovery!
$wfdService = Get-Service -Name "WFDSConMgrSvc" -ErrorAction SilentlyContinue
if ($wfdService) {
if ($wfdService.Status -eq 'Running') {
Stop-Service -Name "WFDSConMgrSvc" -Force -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Stopped WiFi Direct Service (WFDSConMgrSvc)" -Module "AdvancedSecurity"
}
if ($wfdService.StartType -ne 'Disabled') {
Set-Service -Name "WFDSConMgrSvc" -StartupType Disabled -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Disabled WiFi Direct Service (WFDSConMgrSvc) - survives reboot" -Module "AdvancedSecurity"
$changesApplied++
}
}
# 10. Disable WiFi Direct Virtual Adapters (immediate effect)
$wfdAdapters = Get-NetAdapter -InterfaceDescription "Microsoft Wi-Fi Direct Virtual*" -IncludeHidden -ErrorAction SilentlyContinue
if ($wfdAdapters) {
$wfdAdapters | Where-Object { $_.Status -ne 'Disabled' } | ForEach-Object {
Disable-NetAdapter -Name $_.Name -Confirm:$false -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Disabled WiFi Direct adapter: $($_.Name)" -Module "AdvancedSecurity"
$changesApplied++
}
}
}
if ($changesApplied -eq 0) {
Write-Log -Level SUCCESS -Message "Wireless Display security already configured (no changes needed)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Wireless Display security applied ($changesApplied changes)" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to apply Wireless Display security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,216 @@
function Stop-RiskyServices {
<#
.SYNOPSIS
Stop and disable risky network services
.DESCRIPTION
Stops and disables network services that pose security risks:
- SSDPSRV (SSDP Discovery) - Port 1900 UDP
- upnphost (UPnP Device Host) - Port 2869 TCP
- lmhosts (TCP/IP NetBIOS Helper) - Port 139 TCP
Defense in Depth: Firewall blocks external access, but services
still run and listen locally. Stopping services completely closes ports.
Service Dependencies:
upnphost depends on SSDPSRV, so upnphost must be stopped FIRST.
.EXAMPLE
Stop-RiskyServices
Stops all risky network services
.NOTES
Impact:
- Smart home device auto-discovery may not work
- DLNA/casting features may require manual configuration
- NetBIOS name resolution disabled (already disabled via registry)
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Stopping risky network services..." -Module "AdvancedSecurity"
$services = @(
@{
Name = "upnphost"
DisplayName = "UPnP Device Host"
Port = 2869
Protocol = "TCP"
Risk = "MEDIUM"
Impact = "DLNA/casting features may require manual configuration"
},
@{
Name = "SSDPSRV"
DisplayName = "SSDP Discovery"
Port = 1900
Protocol = "UDP"
Risk = "MEDIUM"
Impact = "Smart home device auto-discovery may not work"
},
@{
Name = "lmhosts"
DisplayName = "TCP/IP NetBIOS Helper"
Port = 139
Protocol = "TCP"
Risk = "MEDIUM"
Impact = "NetBIOS name resolution disabled"
}
# Note: Computer Browser (Browser) service is DEPRECATED in Win10/11
# It's tied to SMB1 which is not installed by default
# Removing from list to avoid errors on modern systems
)
# Backup service states
Write-Log -Level INFO -Message "Backing up service states..." -Module "AdvancedSecurity"
$serviceBackup = @{}
foreach ($svc in $services) {
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if ($service) {
$serviceBackup[$svc.Name] = @{
Status = $service.Status.ToString()
StartType = $service.StartType.ToString()
DisplayName = $service.DisplayName
}
}
}
$backupData = @{
Services = $serviceBackup
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "Services_State" -Data $backupJson -Name "RiskyServices"
Write-Log -Level SUCCESS -Message "Backed up state of $($serviceBackup.Count) services" -Module "AdvancedSecurity"
# Stop and disable services
$stoppedCount = 0
$errors = @()
# CRITICAL: Stop upnphost FIRST (it depends on SSDPSRV)
foreach ($svc in $services) {
Write-Log -Level INFO -Message "Processing service: $($svc.DisplayName) ($($svc.Name))..." -Module "AdvancedSecurity"
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if (-not $service) {
Write-Log -Level INFO -Message "Service $($svc.Name) not found (may not be installed)" -Module "AdvancedSecurity"
continue
}
try {
# Stop service if running
if ($service.Status -eq 'Running') {
Write-Log -Level INFO -Message "Stopping $($svc.Name)..." -Module "AdvancedSecurity"
Stop-Service -Name $svc.Name -Force -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Stopped $($svc.Name)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level INFO -Message "$($svc.Name) already stopped" -Module "AdvancedSecurity"
}
# Disable service
Write-Log -Level INFO -Message "Disabling $($svc.Name)..." -Module "AdvancedSecurity"
Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Disabled $($svc.Name) (StartupType = Disabled)" -Module "AdvancedSecurity"
$stoppedCount++
}
catch {
$errors += "$($svc.Name): $_"
Write-Log -Level WARNING -Message "Failed to stop/disable $($svc.Name): $_" -Module "AdvancedSecurity"
}
}
# Verify ports are closed
Write-Log -Level INFO -Message "Verifying ports are closed..." -Module "AdvancedSecurity"
Start-Sleep -Seconds 2 # Give services time to fully stop
$portsClosed = @()
$portsStillOpen = @()
# Check TCP ports
foreach ($port in @(139, 2869)) {
$listener = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
if (-not $listener) {
$portsClosed += "TCP $port"
Write-Log -Level SUCCESS -Message "Port TCP $port is CLOSED" -Module "AdvancedSecurity"
}
else {
$portsStillOpen += "TCP $port"
Write-Log -Level WARNING -Message "Port TCP $port is still LISTENING!" -Module "AdvancedSecurity"
}
}
# Check UDP port 1900
$udpListener = Get-NetUDPEndpoint -LocalPort 1900 -ErrorAction SilentlyContinue
if (-not $udpListener) {
$portsClosed += "UDP 1900"
Write-Log -Level SUCCESS -Message "Port UDP 1900 is CLOSED" -Module "AdvancedSecurity"
}
else {
$portsStillOpen += "UDP 1900"
Write-Log -Level WARNING -Message "Port UDP 1900 is still LISTENING locally. SSDP service is disabled and blocked by firewall; this listener is a known Windows behavior and not reachable from external networks." -Module "AdvancedSecurity"
}
# Summary
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " RISKY SERVICES STOPPED" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Services stopped: $stoppedCount" -ForegroundColor White
foreach ($svc in $services) {
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if ($service) {
$status = if ($service.Status -eq 'Stopped') { "STOPPED" } else { $service.Status }
$startType = $service.StartType
Write-Host " $($svc.DisplayName): $status (StartType: $startType)" -ForegroundColor Gray
}
}
Write-Host ""
Write-Host "Ports closed: $($portsClosed.Count)" -ForegroundColor White
foreach ($port in $portsClosed) {
Write-Host " $port" -ForegroundColor Green
}
if ($portsStillOpen.Count -gt 0) {
Write-Host ""
Write-Host "Ports still open: $($portsStillOpen.Count)" -ForegroundColor Yellow
foreach ($port in $portsStillOpen) {
Write-Host " $port" -ForegroundColor Yellow
}
if ($portsStillOpen -contains "UDP 1900") {
Write-Host ""
Write-Host "Note: UDP 1900 may still show a local listener, but SSDP is disabled and blocked by firewall. This is a known Windows behavior and not remotely reachable." -ForegroundColor Gray
}
}
if ($errors.Count -gt 0) {
Write-Host ""
Write-Host "Errors: $($errors.Count)" -ForegroundColor Red
foreach ($errorMsg in $errors) {
Write-Host " $errorMsg" -ForegroundColor Red
}
}
Write-Host ""
if ($errors.Count -eq 0) {
Write-Log -Level SUCCESS -Message "All risky services stopped and disabled successfully" -Module "AdvancedSecurity"
return $true
}
else {
Write-Log -Level WARNING -Message "Completed with $($errors.Count) errors" -Module "AdvancedSecurity"
return $true # Partial success
}
}
catch {
Write-Log -Level ERROR -Message "Failed to stop risky services: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,100 @@
function Test-AdminShares {
<#
.SYNOPSIS
Test administrative shares compliance
.DESCRIPTION
Checks if administrative shares (C$, ADMIN$, etc.) are disabled
.EXAMPLE
Test-AdminShares
#>
[CmdletBinding()]
param()
try {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
$result = [PSCustomObject]@{
Feature = "Admin Shares"
Status = "Unknown"
Details = @()
AutoShareWks = $null
AutoShareServer = $null
ActiveShares = @()
Compliant = $false
}
# Check registry settings
if (Test-Path $regPath) {
$result.AutoShareWks = (Get-ItemProperty -Path $regPath -Name "AutoShareWks" -ErrorAction SilentlyContinue).AutoShareWks
$result.AutoShareServer = (Get-ItemProperty -Path $regPath -Name "AutoShareServer" -ErrorAction SilentlyContinue).AutoShareServer
if ($result.AutoShareWks -eq 0 -and $result.AutoShareServer -eq 0) {
$result.Details += "Registry: AutoShareWks = 0, AutoShareServer = 0 (Disabled)"
}
else {
$result.Details += "Registry: AutoShareWks = $($result.AutoShareWks), AutoShareServer = $($result.AutoShareServer)"
}
}
# Check for active admin shares (requires LanmanServer service)
$serverService = Get-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
if (-not $serverService -or $serverService.Status -ne 'Running') {
# Server service is stopped/disabled - admin shares are effectively disabled
$result.Details += "LanmanServer service is not running (admin shares cannot exist)"
$adminShares = @()
}
else {
try {
$adminShares = Get-SmbShare | Where-Object { $_.Name -match '^[A-Z]\$$|^ADMIN\$$' }
}
catch {
# Get-SmbShare failed - treat as no shares
$result.Details += "Could not query SMB shares: $($_.Exception.Message)"
$adminShares = @()
}
}
$result.ActiveShares = $adminShares | Select-Object -ExpandProperty Name
if ($adminShares.Count -eq 0) {
$result.Details += "No administrative shares found (C$, ADMIN$ removed)"
if ($result.AutoShareWks -eq 0 -and $result.AutoShareServer -eq 0) {
$result.Status = "Secure"
$result.Compliant = $true
}
else {
$result.Status = "Partially Secure"
$result.Compliant = $false
$result.Details += "WARNING: Shares removed but AutoShare registry not set (will recreate on reboot!)"
}
}
else {
# Shares are present, check if Registry is configured to disable them
if ($result.AutoShareWks -eq 0 -and $result.AutoShareServer -eq 0) {
# Config is correct, just needs a reboot
$result.Status = "Pending Reboot"
$result.Compliant = $true
$result.Details += "Active admin shares: $($adminShares.Name -join ', ') (Will be removed after reboot)"
}
else {
# Config is NOT correct
$result.Status = "Insecure"
$result.Compliant = $false
$result.Details += "Active admin shares: $($adminShares.Name -join ', ')"
}
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test admin shares: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "Admin Shares"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,121 @@
function Test-DiscoveryProtocolsSecurity {
<#
.SYNOPSIS
Tests WS-Discovery and mDNS hardening state.
.DESCRIPTION
Verifies that the following conditions are met:
- OS-level mDNS resolver disabled (EnableMDNS = 0)
- FDResPub and fdPHost services disabled and not running
- NoID firewall BLOCK rules for WS-Discovery and mDNS exist and are enabled
Returns a PSCustomObject with detailed fields and an overall Compliant flag.
.EXAMPLE
Test-DiscoveryProtocolsSecurity
#>
[CmdletBinding()]
param()
$result = [PSCustomObject]@{
EnableMDNS = $null
FDResPubDisabled = $false
FdPHostDisabled = $false
FirewallRulesPresent = $false
FirewallRulesEnabled = $false
Udp3702ListenersClosed = $null
Udp5353ListenersClosed = $null
Tcp5357ListenersClosed = $null
Tcp5358ListenersClosed = $null
Compliant = $false
}
try {
# 1) Check mDNS registry flag
$dnsParamsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
if (Test-Path $dnsParamsPath) {
$props = Get-ItemProperty -Path $dnsParamsPath -ErrorAction SilentlyContinue
if ($props.PSObject.Properties.Name -contains 'EnableMDNS') {
$result.EnableMDNS = $props.EnableMDNS
}
}
# 2) Check services
$fdResPub = Get-Service -Name "FDResPub" -ErrorAction SilentlyContinue
if ($fdResPub) {
$result.FDResPubDisabled = ($fdResPub.StartType -eq 'Disabled' -and $fdResPub.Status -ne 'Running')
}
$fdPHost = Get-Service -Name "fdPHost" -ErrorAction SilentlyContinue
if ($fdPHost) {
$result.FdPHostDisabled = ($fdPHost.StartType -eq 'Disabled' -and $fdPHost.Status -ne 'Running')
}
# 3) Check firewall rules
$ruleNames = @(
"NoID-Block-WSD-UDP-3702",
"NoID-Block-WSD-TCP-5357",
"NoID-Block-WSD-TCP-5358",
"NoID-Block-mDNS-UDP-5353"
)
$rules = @()
foreach ($name in $ruleNames) {
$r = Get-NetFirewallRule -Name $name -ErrorAction SilentlyContinue
if ($r) {
$rules += $r
}
}
if ($rules.Count -gt 0) {
$result.FirewallRulesPresent = ($rules.Count -eq $ruleNames.Count)
$result.FirewallRulesEnabled = ($rules | Where-Object { $_.Enabled -eq 'True' -and $_.Action -eq 'Block' }).Count -eq $ruleNames.Count
}
# 4) Optional: check that ports are not listening
try {
$udp3702 = Get-NetUDPEndpoint -LocalPort 3702 -ErrorAction SilentlyContinue
$result.Udp3702ListenersClosed = (-not $udp3702)
}
catch {
$result.Udp3702ListenersClosed = $null
}
try {
$udp5353 = Get-NetUDPEndpoint -LocalPort 5353 -ErrorAction SilentlyContinue
$result.Udp5353ListenersClosed = (-not $udp5353)
}
catch {
$result.Udp5353ListenersClosed = $null
}
try {
$tcp5357 = Get-NetTCPConnection -LocalPort 5357 -State Listen -ErrorAction SilentlyContinue
$result.Tcp5357ListenersClosed = (-not $tcp5357)
}
catch {
$result.Tcp5357ListenersClosed = $null
}
try {
$tcp5358 = Get-NetTCPConnection -LocalPort 5358 -State Listen -ErrorAction SilentlyContinue
$result.Tcp5358ListenersClosed = (-not $tcp5358)
}
catch {
$result.Tcp5358ListenersClosed = $null
}
# Overall compliance: mDNS disabled, services disabled, firewall rules present+enabled
$mdnsOk = ($result.EnableMDNS -eq 0)
$servicesOk = $result.FDResPubDisabled -and $result.FdPHostDisabled
$firewallOk = $result.FirewallRulesPresent -and $result.FirewallRulesEnabled
$result.Compliant = $mdnsOk -and $servicesOk -and $firewallOk
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test discovery protocol security (WS-Discovery/mDNS): $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $result
}
}

View file

@ -0,0 +1,57 @@
function Test-FingerProtocol {
<#
.SYNOPSIS
Test if Finger Protocol (TCP 79) is blocked
.DESCRIPTION
Verifies that the Windows Firewall rule blocking outbound TCP port 79
is present and enabled. This prevents ClickFix malware attacks that
abuse finger.exe to retrieve commands from C2 servers.
.OUTPUTS
PSCustomObject with compliance result
#>
[CmdletBinding()]
param()
try {
$ruleName = "NoID Privacy - Block Finger Protocol (Port 79)"
# Check if firewall rule exists and is enabled
$rule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($rule) {
$isEnabled = $rule.Enabled -eq 'True'
$isBlocking = $rule.Action -eq 'Block'
$isOutbound = $rule.Direction -eq 'Outbound'
$compliant = $isEnabled -and $isBlocking -and $isOutbound
if ($compliant) {
$status = "Finger Protocol blocked (TCP 79 outbound)"
}
else {
$status = "Rule exists but misconfigured (Enabled: $isEnabled, Block: $isBlocking, Outbound: $isOutbound)"
}
}
else {
$compliant = $false
$status = "Firewall rule not found"
}
return [PSCustomObject]@{
Feature = "Finger Protocol Block"
Compliant = $compliant
Status = $status
Details = if ($rule) { "Rule: $ruleName" } else { "ClickFix malware protection not active" }
}
}
catch {
return [PSCustomObject]@{
Feature = "Finger Protocol Block"
Compliant = $false
Status = "Error checking: $($_.Exception.Message)"
Details = $null
}
}
}

View file

@ -0,0 +1,39 @@
function Test-FirewallShieldsUp {
<#
.SYNOPSIS
Test if Firewall Shields Up mode is enabled
.DESCRIPTION
Checks DoNotAllowExceptions value for PublicProfile firewall.
#>
[CmdletBinding()]
param()
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile"
$valueName = "DoNotAllowExceptions"
try {
$value = Get-ItemProperty -Path $regPath -Name $valueName -ErrorAction SilentlyContinue
if ($null -eq $value -or $value.$valueName -ne 1) {
return @{
Pass = $false
Message = "Shields Up NOT enabled (Public network allows configured exceptions)"
CurrentValue = if ($null -eq $value) { "Not Set" } else { $value.$valueName }
}
}
return @{
Pass = $true
Message = "Shields Up ENABLED (Public network blocks ALL incoming)"
CurrentValue = 1
}
}
catch {
return @{
Pass = $false
Message = "Error checking Shields Up: $_"
CurrentValue = "Error"
}
}
}

View file

@ -0,0 +1,61 @@
function Test-IPv6Security {
<#
.SYNOPSIS
Test IPv6 disable status (mitm6 attack mitigation)
.DESCRIPTION
Checks if IPv6 is completely disabled via DisabledComponents registry value.
This is an OPTIONAL setting only available in Maximum profile.
DisabledComponents = 0xFF (255) means IPv6 is completely disabled.
.EXAMPLE
Test-IPv6Security
#>
[CmdletBinding()]
param()
try {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters"
$value = Get-ItemProperty -Path $regPath -Name "DisabledComponents" -ErrorAction SilentlyContinue
if ($value -and $value.DisabledComponents -eq 255) {
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true
Compliant = $true
Message = "IPv6 DISABLED (DisabledComponents = 0xFF) - mitm6 protected"
Details = "IPv6 completely disabled - DHCPv6 spoofing attacks blocked"
}
}
elseif ($value -and $value.DisabledComponents -gt 0) {
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true
Compliant = $true
Message = "IPv6 PARTIALLY disabled (DisabledComponents = $($value.DisabledComponents))"
Details = "IPv6 partially disabled - some mitm6 protection"
}
}
else {
# IPv6 is enabled - this is OPTIONAL, so still "pass" but note it's not configured
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true # Optional feature - not a failure
Compliant = $true # Optional feature
Message = "IPv6 ENABLED (Optional - not configured)"
Details = "IPv6 enabled (default) - WPAD disabled provides partial mitm6 protection"
}
}
}
catch {
Write-Log -Level ERROR -Message "Failed to test IPv6 security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true # Don't fail on error for optional feature
Compliant = $true
Message = "Error checking IPv6 status"
Details = "Could not determine IPv6 status: $_"
}
}
}

View file

@ -0,0 +1,75 @@
function Test-LegacyTLS {
<#
.SYNOPSIS
Test Legacy TLS configuration compliance
.DESCRIPTION
Verifies that TLS 1.0 and TLS 1.1 are disabled for both Client and Server.
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "Legacy TLS (1.0/1.1)"
Status = "Unknown"
Details = @()
Compliant = $true
}
$tlsVersions = @("TLS 1.0", "TLS 1.1")
$components = @("Server", "Client")
$nonCompliantCount = 0
foreach ($version in $tlsVersions) {
foreach ($component in $components) {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$version\$component"
if (Test-Path $regPath) {
$enabled = (Get-ItemProperty -Path $regPath -Name "Enabled" -ErrorAction SilentlyContinue).Enabled
$disabledByDefault = (Get-ItemProperty -Path $regPath -Name "DisabledByDefault" -ErrorAction SilentlyContinue).DisabledByDefault
if ($enabled -eq 0) {
# Compliant
}
elseif ($null -eq $enabled -and $disabledByDefault -eq 1) {
# Compliant (implicitly disabled)
}
else {
$result.Details += "$version $component is NOT disabled (Enabled=$enabled)"
$nonCompliantCount++
}
}
else {
# Key missing usually means default (Enabled on old OS, Disabled on very new OS)
# For hardening, we expect explicit disable keys
$result.Details += "$version $component registry keys missing"
$nonCompliantCount++
}
}
}
if ($nonCompliantCount -eq 0) {
$result.Status = "Secure (Disabled)"
$result.Compliant = $true
}
else {
$result.Status = "Insecure ($nonCompliantCount issues)"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test Legacy TLS: $_" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "Legacy TLS (1.0/1.1)"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,61 @@
function Test-PowerShellV2 {
<#
.SYNOPSIS
Test PowerShell v2 status
.DESCRIPTION
Verifies that the PowerShell v2 feature is disabled or not present.
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "PowerShell v2 (Downgrade Attack)"
Status = "Unknown"
Details = @()
Compliant = $true
}
$psv2Feature = $null
try {
$psv2Feature = Get-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -ErrorAction SilentlyContinue
}
catch {
$psv2Feature = $null
}
if (-not $psv2Feature) {
# Feature not present on OS - Secure by default
$result.Status = "Secure (Not Present)"
$result.Compliant = $true
$result.Details += "Feature 'MicrosoftWindowsPowerShellV2Root' not found on this OS"
}
elseif ($psv2Feature.State -ne 'Enabled') {
# Feature present but disabled - Secure
$result.Status = "Secure (Disabled)"
$result.Compliant = $true
$result.Details += "Feature state: $($psv2Feature.State)"
}
else {
# Feature Enabled - Insecure
$result.Status = "Insecure (Enabled)"
$result.Compliant = $false
$result.Details += "PowerShell v2 is enabled (allows downgrade attacks)"
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test PowerShell v2: $_" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "PowerShell v2"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,96 @@
function Test-RdpSecurity {
<#
.SYNOPSIS
Test RDP security hardening compliance
.DESCRIPTION
Verifies that RDP is properly hardened:
- NLA (Network Level Authentication) is enforced
- SSL/TLS encryption is required
- Optionally checks if RDP is completely disabled
.EXAMPLE
Test-RdpSecurity
Returns compliance status for RDP hardening
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$rdpRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$rdpServerPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
$result = [PSCustomObject]@{
Feature = "RDP Security"
Status = "Unknown"
Details = @()
NLA_Enabled = $false
SSL_TLS_Enabled = $false
RDP_Disabled = $false
Compliant = $false
}
# Check NLA
if (Test-Path $rdpRegPath) {
$userAuth = (Get-ItemProperty -Path $rdpRegPath -Name "UserAuthentication" -ErrorAction SilentlyContinue).UserAuthentication
$secLayer = (Get-ItemProperty -Path $rdpRegPath -Name "SecurityLayer" -ErrorAction SilentlyContinue).SecurityLayer
if ($userAuth -eq 1) {
$result.NLA_Enabled = $true
$result.Details += "NLA enforced (UserAuthentication = 1)"
}
else {
$result.Details += "NLA NOT enforced (UserAuthentication = $userAuth)"
}
if ($secLayer -eq 2) {
$result.SSL_TLS_Enabled = $true
$result.Details += "SSL/TLS enforced (SecurityLayer = 2)"
}
else {
$result.Details += "SSL/TLS NOT enforced (SecurityLayer = $secLayer)"
}
}
else {
$result.Details += "RDP registry path not found"
}
# Check if RDP is completely disabled
if (Test-Path $rdpServerPath) {
$rdpDisabled = (Get-ItemProperty -Path $rdpServerPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue).fDenyTSConnections
if ($rdpDisabled -eq 1) {
$result.RDP_Disabled = $true
$result.Details += "RDP completely disabled (fDenyTSConnections = 1)"
}
}
# Determine compliance
if ($result.RDP_Disabled) {
$result.Status = "Secure (RDP Disabled)"
$result.Compliant = $true
}
elseif ($result.NLA_Enabled -and $result.SSL_TLS_Enabled) {
$result.Status = "Secure (NLA + SSL/TLS)"
$result.Compliant = $true
}
else {
$result.Status = "Insecure"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test RDP security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "RDP Security"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,134 @@
function Test-RiskyPorts {
<#
.SYNOPSIS
Test risky firewall ports compliance
.DESCRIPTION
Checks if risky firewall ports (LLMNR, NetBIOS, UPnP/SSDP) are closed
.EXAMPLE
Test-RiskyPorts
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "Risky Firewall Ports"
Status = "Unknown"
Details = @()
OpenPorts = @()
DisabledRules = 0
EnabledRules = 0
Compliant = $false
}
$riskyPorts = @(5355, 137, 138, 139, 1900, 2869)
# Detect NoID SSDP firewall block rule for UDP 1900
$ssdpRuleName = "NoID Privacy - Block SSDP (UDP 1900)"
$ssdpBlockRule = Get-NetFirewallRule -DisplayName $ssdpRuleName -ErrorAction SilentlyContinue
$ssdpBlockActive = $false
if ($ssdpBlockRule -and $ssdpBlockRule.Enabled -eq 'True' -and $ssdpBlockRule.Action -eq 'Block') {
$ssdpBlockActive = $true
}
# PERFORMANCE FIX: Batch query instead of per-rule queries
# Old approach: Get-NetFirewallRule | ForEach { Get-NetFirewallPortFilter } = 300 queries × 200ms = 60s!
# New approach: Get all port filters once, then filter = 2-3s total
# Get all inbound firewall rules (pre-filter by direction)
$inboundRules = Get-NetFirewallRule -Direction Inbound -ErrorAction SilentlyContinue
# Get all port filters in one batch query
$allPortFilters = @{}
Get-NetFirewallPortFilter -ErrorAction SilentlyContinue | ForEach-Object {
$allPortFilters[$_.InstanceID] = $_
}
# Now filter rules by risky ports (fast lookup)
$riskyRules = $inboundRules | Where-Object {
$portFilter = $allPortFilters[$_.InstanceID]
if ($portFilter) {
($portFilter.LocalPort -in $riskyPorts) -or ($portFilter.RemotePort -in $riskyPorts)
}
else {
$false
}
}
foreach ($rule in $riskyRules) {
if ($rule.Enabled -eq $true) {
$portFilter = $allPortFilters[$rule.InstanceID]
if ($rule.Action -eq 'Allow') {
$result.EnabledRules++
$result.Details += "WARNING: Allow rule '$($rule.DisplayName)' is ENABLED (Port: $($portFilter.LocalPort))"
}
else {
$result.Details += "INFO: Block rule '$($rule.DisplayName)' is ENABLED (Port: $($portFilter.LocalPort))"
}
}
else {
$result.DisabledRules++
}
}
# Check actual port listeners
foreach ($port in $riskyPorts) {
if ($port -in @(137, 138, 139, 2869)) {
# TCP ports
$listener = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
if ($listener) {
$result.OpenPorts += "TCP $port"
$result.Details += "OPEN: TCP port $port is LISTENING!"
}
}
else {
# UDP ports (5355, 1900)
$listener = Get-NetUDPEndpoint -LocalPort $port -ErrorAction SilentlyContinue
if ($listener) {
$result.OpenPorts += "UDP $port"
$result.Details += "OPEN: UDP port $port is LISTENING!"
}
}
}
# Determine compliance
$udp1900Open = $result.OpenPorts -contains "UDP 1900"
$otherOpenPorts = $result.OpenPorts | Where-Object { $_ -ne "UDP 1900" }
if ($result.OpenPorts.Count -eq 0 -and $result.EnabledRules -eq 0) {
# Ideal case: no listeners and no allow rules
$result.Status = "Secure"
$result.Compliant = $true
$result.Details += "All risky ports closed and firewall rules disabled"
}
elseif ($udp1900Open -and -not $otherOpenPorts -and $result.EnabledRules -eq 0 -and $ssdpBlockActive) {
# Only open endpoint is UDP 1900, but protected by NoID block rule (inbound)
$result.Status = "Secure (blocked by firewall)"
$result.Compliant = $true
$result.Details += "UDP 1900 is listening locally but inbound traffic is blocked by '$ssdpRuleName'"
}
elseif ($result.OpenPorts.Count -eq 0 -and $result.EnabledRules -gt 0) {
$result.Status = "Partially Secure"
$result.Compliant = $false
$result.Details += "Ports closed but $($result.EnabledRules) firewall rules still enabled"
}
else {
$result.Status = "Insecure"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test risky ports: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "Risky Firewall Ports"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,89 @@
function Test-RiskyServices {
<#
.SYNOPSIS
Test risky network services compliance
.DESCRIPTION
Checks if risky network services (SSDPSRV, upnphost, lmhosts) are stopped and disabled
.EXAMPLE
Test-RiskyServices
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "Risky Network Services"
Status = "Unknown"
Details = @()
RunningServices = @()
StoppedServices = @()
Compliant = $false
}
# Note: Computer Browser (Browser) is deprecated in Win10/11 - not included
$services = @("SSDPSRV", "upnphost", "lmhosts")
foreach ($svcName in $services) {
$service = Get-Service -Name $svcName -ErrorAction SilentlyContinue
if (-not $service) {
$result.Details += "$svcName - Not found (service may not be installed)"
continue
}
if ($service.Status -eq 'Running') {
$result.RunningServices += $svcName
$result.Details += "WARNING - $svcName is RUNNING (StartType: $($service.StartType))"
}
else {
$result.StoppedServices += $svcName
if ($service.StartType -eq 'Disabled') {
$result.Details += "${svcName}: Stopped and Disabled"
}
else {
$result.Details += "WARNING: ${svcName} is stopped but StartType is $($service.StartType) (should be Disabled)"
}
}
}
# Determine compliance
if ($result.RunningServices.Count -eq 0) {
$stoppedAndDisabled = $true
foreach ($svcName in $services) {
$service = Get-Service -Name $svcName -ErrorAction SilentlyContinue
if ($service -and $service.StartType -ne 'Disabled') {
$stoppedAndDisabled = $false
break
}
}
if ($stoppedAndDisabled) {
$result.Status = "Secure"
$result.Compliant = $true
}
else {
$result.Status = "Partially Secure"
$result.Compliant = $false
}
}
else {
$result.Status = "Insecure"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test risky services: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "Risky Network Services"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,126 @@
function Test-SRPCompliance {
<#
.SYNOPSIS
Verifies Software Restriction Policies (SRP) configuration for CVE-2025-9491
.DESCRIPTION
Tests whether SRP rules are correctly configured to block .lnk execution from Temp/Downloads.
Returns compliance status for CVE-2025-9491 mitigation.
.EXAMPLE
Test-SRPCompliance
.OUTPUTS
PSCustomObject with compliance results
#>
[CmdletBinding()]
param()
try {
$configPath = Join-Path $PSScriptRoot "..\Config\SRP-Rules.json"
if (-not (Test-Path $configPath)) {
return [PSCustomObject]@{
Feature = "SRP Configuration"
Status = "Not Configured"
Compliant = $false
Details = "SRP-Rules.json not found"
}
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$policyRoot = $config.RegistryPaths.PolicyRoot
# Check if SRP policy exists
if (-not (Test-Path $policyRoot)) {
Write-Log -Level WARNING -Message "SRP Check Failed: Policy root not found ($policyRoot)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Not Configured"
Compliant = $false
Details = "SRP policy root not found"
}
}
# Check Default Level
$defaultLevel = Get-ItemProperty -Path $policyRoot -Name "DefaultLevel" -ErrorAction SilentlyContinue
if ($null -eq $defaultLevel -or $defaultLevel.DefaultLevel -ne 262144) {
Write-Log -Level WARNING -Message "SRP Check Failed: DefaultLevel is not Unrestricted (262144)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Misconfigured"
Compliant = $false
Details = "Default level not set to Unrestricted (262144)"
}
}
# Check Path Rules
$pathRulesRoot = $config.RegistryPaths.PathRules
if (-not (Test-Path $pathRulesRoot)) {
Write-Log -Level WARNING -Message "SRP Check Failed: PathRules root not found ($pathRulesRoot)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Incomplete"
Compliant = $false
Details = "Path rules not configured"
}
}
# Count configured rules
$configuredRules = Get-ChildItem -Path $pathRulesRoot -ErrorAction SilentlyContinue
$ruleCount = if ($configuredRules) { $configuredRules.Count } else { 0 }
# Check for Windows 11 bug
$bugFixPath = $config.RegistryPaths.Win11BugFix
$hasBuggyKeys = $false
if (Test-Path $bugFixPath) {
foreach ($keyName in $config.Windows11BugFix.KeysToRemove) {
$keyExists = Get-ItemProperty -Path $bugFixPath -Name $keyName -ErrorAction SilentlyContinue
if ($null -ne $keyExists) {
$hasBuggyKeys = $true
break
}
}
}
if ($hasBuggyKeys) {
Write-Log -Level WARNING -Message "SRP Check Failed: Windows 11 buggy keys present (RuleCount/LastWriteTime)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Windows 11 Bug Detected"
Compliant = $false
Details = "Buggy registry keys present (RuleCount/LastWriteTime) - SRP may not work"
}
}
# All checks passed
if ($ruleCount -ge 2) {
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Protected"
Compliant = $true
Details = "$ruleCount path rules configured, Windows 11 bug fix applied"
}
}
else {
Write-Log -Level WARNING -Message "SRP Check Failed: Insufficient rules found ($ruleCount, expected 2+)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Incomplete"
Compliant = $false
Details = "Only $ruleCount path rules found (expected 2+)"
}
}
}
catch {
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Error"
Compliant = $false
Details = "Test failed: $_"
}
}
}

View file

@ -0,0 +1,130 @@
function Test-WDigest {
<#
.SYNOPSIS
Test WDigest credential protection compliance
.DESCRIPTION
Verifies that WDigest is configured to NOT store plaintext credentials in LSASS memory.
Checks the UseLogonCredential registry value.
Expected: UseLogonCredential = 0 (Secure)
Insecure: UseLogonCredential = 1 (Plaintext credentials in memory!)
.EXAMPLE
Test-WDigest
Returns compliance status for WDigest protection
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$wdigestRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest"
$result = [PSCustomObject]@{
Feature = "WDigest Protection"
Status = "Unknown"
Details = @()
UseLogonCredential = $null
Compliant = $false
Windows_Version = ""
Deprecated = $false
}
# Get Windows version
$osVersion = [System.Environment]::OSVersion.Version
$isWin11 = $osVersion.Major -ge 10 -and $osVersion.Build -ge 22000
$isWin11_24H2Plus = $isWin11 -and $osVersion.Build -ge 26100
if ($isWin11) {
$result.Windows_Version = "Windows 11 (Build $($osVersion.Build))"
if ($isWin11_24H2Plus) {
$result.Deprecated = $true
$result.Details += "Windows 11 24H2+ detected - WDigest setting is deprecated"
}
}
elseif ($osVersion.Major -eq 10) {
$result.Windows_Version = "Windows 10 (Build $($osVersion.Build))"
}
else {
$result.Windows_Version = "Windows $($osVersion.Major).$($osVersion.Minor) (Build $($osVersion.Build))"
}
# Check registry value
if (Test-Path $wdigestRegPath) {
$useLogonCred = (Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -ErrorAction SilentlyContinue).UseLogonCredential
if ($null -ne $useLogonCred) {
$result.UseLogonCredential = $useLogonCred
if ($useLogonCred -eq 0) {
$result.Status = "Secure"
$result.Compliant = $true
$result.Details += "UseLogonCredential = 0 (Plaintext credentials NOT stored)"
if ($result.Deprecated) {
$result.Details += "Note: Setting is deprecated but explicitly configured for backwards compatibility"
}
}
elseif ($useLogonCred -eq 1) {
$result.Status = "INSECURE!"
$result.Compliant = $false
$result.Details += "WARNING: UseLogonCredential = 1 (Plaintext credentials IN MEMORY!)"
$result.Details += "VULNERABLE to Mimikatz, WCE, and other credential dumping tools!"
}
else {
$result.Status = "Unknown Value"
$result.Compliant = $false
$result.Details += "UseLogonCredential = $useLogonCred (Unknown value)"
}
}
else {
# Value not set - default depends on OS version
if ($osVersion.Major -eq 6 -and $osVersion.Minor -le 2) {
# Windows 7/8 - default is 1 (INSECURE!)
$result.Status = "Insecure (Default)"
$result.Compliant = $false
$result.Details += "UseLogonCredential not set - Windows 7/8 default is 1 (INSECURE!)"
}
else {
# Windows 8.1+ - default is 0 (Secure)
$result.Status = "Secure (Default)"
$result.Compliant = $true
$result.Details += "UseLogonCredential not set - Windows 8.1+ default is 0 (Secure)"
if ($result.Deprecated) {
$result.Details += "Windows 11 24H2+: Setting is hardcoded secure (deprecated)"
}
}
}
}
else {
# Registry path doesn't exist
if ($osVersion.Major -eq 6 -and $osVersion.Minor -le 2) {
# Windows 7/8
$result.Status = "Insecure (No Config)"
$result.Compliant = $false
$result.Details += "WDigest registry path not found - Windows 7/8 default is INSECURE!"
}
else {
# Windows 8.1+
$result.Status = "Secure (Default)"
$result.Compliant = $true
$result.Details += "WDigest registry path not found - Windows 8.1+ default is secure"
}
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test WDigest protection: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "WDigest Protection"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,88 @@
function Test-WPAD {
<#
.SYNOPSIS
Test WPAD configuration compliance
.DESCRIPTION
Verifies that Web Proxy Auto-Discovery (WPAD) is disabled using the official
Microsoft-recommended key plus legacy keys for compatibility.
Reference: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/disable-http-proxy-auth-features
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "WPAD (Proxy Auto-Discovery)"
Status = "Unknown"
Details = @()
Compliant = $true
}
$wpadKeys = @(
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp"
Name = "DisableWpad"
Expected = 1
Description = "Official MS key (Win10 1809+)"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad"
Name = "WpadOverride"
Expected = 1
Description = "Legacy override key"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
Name = "AutoDetect"
Expected = 0
Description = "Browser-level HKLM"
}
)
$nonCompliantCount = 0
foreach ($key in $wpadKeys) {
if (Test-Path $key.Path) {
$val = (Get-ItemProperty -Path $key.Path -Name $key.Name -ErrorAction SilentlyContinue).($key.Name)
if ($val -eq $key.Expected) {
# Compliant
}
else {
$result.Details += "$($key.Name) is NOT set to $($key.Expected) (Current: $val)"
$nonCompliantCount++
}
}
else {
# Key missing
$result.Details += "Registry key missing: $($key.Path)"
$nonCompliantCount++
}
}
if ($nonCompliantCount -eq 0) {
$result.Status = "Secure (Disabled)"
$result.Compliant = $true
}
else {
$result.Status = "Insecure ($nonCompliantCount issues)"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test WPAD: $_" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "WPAD (Proxy Auto-Discovery)"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,91 @@
function Test-WindowsUpdate {
<#
.SYNOPSIS
Verifies Windows Update configuration (3 simple GUI settings)
.DESCRIPTION
Tests whether the 3 Windows Update GUI settings are properly configured:
1. Get latest updates immediately
2. Microsoft Update for other products
3. Delivery Optimization disabled
.EXAMPLE
Test-WindowsUpdate
.OUTPUTS
PSCustomObject with compliance results
#>
[CmdletBinding()]
param()
try {
$configPath = Join-Path $PSScriptRoot "..\Config\WindowsUpdate.json"
if (-not (Test-Path $configPath)) {
return [PSCustomObject]@{
Feature = "Windows Update"
Status = "Not Configured"
Compliant = $false
Details = "WindowsUpdate.json not found"
}
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$settingsConfigured = 0
$settingsTotal = 0
$details = @()
# Check all 3 settings from config
foreach ($settingKey in $config.Settings.PSObject.Properties.Name) {
$setting = $config.Settings.$settingKey
$regPath = $setting.RegistryPath
foreach ($valueName in $setting.Values.PSObject.Properties.Name) {
$valueData = $setting.Values.$valueName
$settingsTotal++
if (Test-Path $regPath) {
$actual = Get-ItemProperty -Path $regPath -Name $valueName -ErrorAction SilentlyContinue
if ($null -ne $actual -and $actual.$valueName -eq $valueData.Value) {
$settingsConfigured++
$details += "$($setting.Name): OK"
}
else {
$details += "$($setting.Name): NOT SET"
Write-Log -Level WARNING -Message "Windows Update Check Failed: $($setting.Name)" -Module "AdvancedSecurity"
if ($null -eq $actual) {
Write-Log -Level WARNING -Message " - Value '$valueName' not found in $regPath" -Module "AdvancedSecurity"
} else {
Write-Log -Level WARNING -Message " - Value '$valueName' mismatch. Expected: $($valueData.Value), Actual: $($actual.$valueName)" -Module "AdvancedSecurity"
}
}
}
else {
$details += "$($setting.Name): NOT SET (reg path missing)"
Write-Log -Level WARNING -Message "Windows Update Check Failed: $($setting.Name)" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - Registry Path Missing: $regPath" -Module "AdvancedSecurity"
}
}
}
$compliant = ($settingsConfigured -eq $settingsTotal)
return [PSCustomObject]@{
Feature = "Windows Update"
Status = if ($compliant) { "Configured" } else { "Incomplete" }
Compliant = $compliant
Details = "$settingsConfigured/$settingsTotal settings OK. $(if ($details) { $details -join ', ' })"
}
}
catch {
return [PSCustomObject]@{
Feature = "Windows Update"
Status = "Error"
Compliant = $false
Details = "Test failed: $_"
}
}
}

View file

@ -0,0 +1,70 @@
function Test-WirelessDisplaySecurity {
<#
.SYNOPSIS
Tests Wireless Display (Miracast) security configuration.
.DESCRIPTION
Verifies that Wireless Display policies are configured securely:
- AllowProjectionToPC = 0 (blocking receiving)
- RequirePinForPairing = 2 (always require PIN)
- Optionally: Complete disable of all Wireless Display
.EXAMPLE
Test-WirelessDisplaySecurity
#>
[CmdletBinding()]
param()
try {
$connectPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Connect"
$results = @{
AllowProjectionToPC = $null
RequirePinForPairing = $null
AllowProjectionFromPC = $null
AllowMdnsAdvertisement = $null
AllowMdnsDiscovery = $null
WiFiDirectServiceDisabled = $null
Compliant = $false
FullyDisabled = $false
}
if (Test-Path $connectPath) {
$props = Get-ItemProperty -Path $connectPath -ErrorAction SilentlyContinue
# Check basic hardening (always required)
$results.AllowProjectionToPC = $props.AllowProjectionToPC
$results.RequirePinForPairing = $props.RequirePinForPairing
# Check optional complete disable
$results.AllowProjectionFromPC = $props.AllowProjectionFromPC
$results.AllowMdnsAdvertisement = $props.AllowMdnsAdvertisement
$results.AllowMdnsDiscovery = $props.AllowMdnsDiscovery
# Check WiFi Direct Service status (CRITICAL for complete block)
$wfdService = Get-Service -Name "WFDSConMgrSvc" -ErrorAction SilentlyContinue
$results.WiFiDirectServiceDisabled = ($null -eq $wfdService) -or ($wfdService.StartType -eq 'Disabled')
# Basic compliance: receiving blocked + PIN required
$results.Compliant = ($results.AllowProjectionToPC -eq 0) -and ($results.RequirePinForPairing -eq 2)
# Fully disabled: all settings at 0/2 AND WiFi Direct service disabled
$results.FullyDisabled = $results.Compliant -and
($results.AllowProjectionFromPC -eq 0) -and
($results.AllowMdnsAdvertisement -eq 0) -and
($results.AllowMdnsDiscovery -eq 0) -and
$results.WiFiDirectServiceDisabled
}
else {
# Key doesn't exist = not hardened
$results.Compliant = $false
$results.FullyDisabled = $false
}
return [PSCustomObject]$results
}
catch {
Write-Log -Level ERROR -Message "Failed to test Wireless Display security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $null
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,312 @@
function Restore-AdvancedSecuritySettings {
<#
.SYNOPSIS
Restore Advanced Security settings from backup
.DESCRIPTION
Restores custom Advanced Security settings that are not handled by the generic
registry/service restore logic. This includes:
- Firewall Rules (Risky Ports)
- Windows Features (PowerShell v2)
- SMB Shares (Admin Shares)
.PARAMETER BackupFilePath
Path to the JSON backup file
.OUTPUTS
Boolean indicating success
#>
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory = $true)]
[string]$BackupFilePath
)
if (-not (Test-Path $BackupFilePath)) {
Write-Log -Level ERROR -Message "Backup file not found: $BackupFilePath" -Module "AdvancedSecurity"
return $false
}
try {
$filename = Split-Path $BackupFilePath -Leaf
Write-Log -Level INFO -Message "Processing Advanced Security backup: $filename" -Module "AdvancedSecurity"
# Skip Empty Marker files - these are already processed by generic Empty Marker logic in Core/Rollback.ps1
if ($filename -match "_EMPTY\.json$") {
Write-Log -Level DEBUG -Message "Skipping Empty Marker file (already processed): $filename" -Module "AdvancedSecurity"
return $true # Success - nothing to do here
}
# Load backup data
$backupData = Get-Content -Path $BackupFilePath -Raw | ConvertFrom-Json
# Determine backup type based on filename or content
if ($filename -match "RiskyPorts_Firewall") {
return Restore-FirewallRules -BackupData $backupData
}
elseif ($filename -match "PowerShellV2") {
return Restore-PowerShellV2 -BackupData $backupData
}
elseif ($filename -match "AdminShares") {
return Restore-AdminShares -BackupData $backupData
}
elseif ($filename -match "NetBIOS_Adapters") {
return Restore-NetBIOSAdapters -BackupData $backupData
}
elseif ($filename -match "RDP_Hardening") {
# RDP settings are already restored via the Smart JSON-Fallback mechanism in Rollback.ps1
# This JSON backup serves as a fallback and doesn't require separate restore logic
Write-Log -Level DEBUG -Message "RDP_Hardening.json acknowledged (already handled by Smart JSON-Fallback)" -Module "AdvancedSecurity"
return $true
}
else {
Write-Log -Level WARNING -Message "Unknown Advanced Security backup type: $filename" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to restore Advanced Security settings: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}
function Restore-FirewallRules {
param($BackupData)
Write-Log -Level INFO -Message "Restoring firewall rules..." -Module "AdvancedSecurity"
try {
# 1. Remove rules created by hardening (identified by Group or Name pattern)
# The hardening module creates rules with specific names/groups.
# Since we don't have the exact names of created rules stored in a "CreatedRules" list here,
# we rely on the fact that we are restoring the *previous* state.
# However, for firewall rules, "restoring" usually means:
# 1. Deleting the BLOCK rules we added
# 2. Re-enabling any rules we disabled (if any)
# The backup contains a SNAPSHOT of rules matching the risky ports.
# We should restore their state (Enabled/Disabled, Action).
if ($BackupData.Rules) {
foreach ($rule in $BackupData.Rules) {
# Check if rule exists
$currentRule = Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
if ($currentRule) {
# Restore state
Set-NetFirewallRule -Name $rule.Name `
-Enabled $rule.Enabled `
-Action $rule.Action `
-ErrorAction SilentlyContinue
Write-Log -Level DEBUG -Message "Restored rule state: $($rule.Name)" -Module "AdvancedSecurity"
}
}
}
# Also remove the specific block rules added by AdvancedSecurity
# These include:
# - Block Risky Port * (legacy patterns)
# - NoID Privacy - Block Finger Protocol (Port 79)
# - NoID Privacy - Block SSDP (UDP 1900)
# - Block Admin Shares - NoID Privacy (TCP 445 on Public profile)
$blockRules = Get-NetFirewallRule -DisplayName "Block Risky Port *" -ErrorAction SilentlyContinue
if ($blockRules) {
Remove-NetFirewallRule -InputObject $blockRules -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Removed $($blockRules.Count) hardening block rules" -Module "AdvancedSecurity"
}
# Remove Finger Protocol rule (corrected name with NoID prefix)
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Finger Protocol (Port 79)" -ErrorAction SilentlyContinue
# Remove SSDP block rule (UDP 1900)
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block SSDP (UDP 1900)" -ErrorAction SilentlyContinue
# Remove WS-Discovery and mDNS block rules (Maximum profile discovery hardening)
Remove-NetFirewallRule -Name "NoID-Block-WSD-UDP-3702" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -Name "NoID-Block-WSD-TCP-5357" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -Name "NoID-Block-WSD-TCP-5358" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -Name "NoID-Block-mDNS-UDP-5353" -ErrorAction SilentlyContinue
# Remove Admin Shares SMB block rule (TCP 445 on Public profile)
Remove-NetFirewallRule -DisplayName "Block Admin Shares - NoID Privacy" -ErrorAction SilentlyContinue
# Remove Miracast/Wireless Display block rules (Ports 7236, 7250)
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast TCP 7236" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast TCP 7250" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast UDP 7236" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast UDP 7250" -ErrorAction SilentlyContinue
# Re-enable WiFi Direct Service (WFDSConMgrSvc) for Miracast functionality
$wfdService = Get-Service -Name "WFDSConMgrSvc" -ErrorAction SilentlyContinue
if ($wfdService -and $wfdService.StartType -eq 'Disabled') {
Set-Service -Name "WFDSConMgrSvc" -StartupType Manual -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Re-enabled WiFi Direct Service (WFDSConMgrSvc)" -Module "AdvancedSecurity"
}
# Re-enable WiFi Direct Virtual Adapters
Get-NetAdapter -InterfaceDescription "Microsoft Wi-Fi Direct Virtual*" -IncludeHidden -ErrorAction SilentlyContinue |
Where-Object { $_.Status -eq 'Disabled' } |
ForEach-Object {
Enable-NetAdapter -Name $_.Name -Confirm:$false -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Re-enabled WiFi Direct adapter: $($_.Name)" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to restore firewall rules: $_" -Module "AdvancedSecurity"
return $false
}
}
function Restore-PowerShellV2 {
param($BackupData)
Write-Log -Level INFO -Message "Restoring PowerShell v2 state..." -Module "AdvancedSecurity"
try {
$shouldEnable = ($BackupData.State -eq "Enabled")
# Check current state
$psv2RegPath = "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine"
$psv2EngineVersion = (Get-ItemProperty -Path $psv2RegPath -Name "PowerShellVersion" -ErrorAction SilentlyContinue).PowerShellVersion
$isEnabled = ($null -ne $psv2EngineVersion -and $psv2EngineVersion -like "2.*")
if ($shouldEnable -and -not $isEnabled) {
Write-Log -Level INFO -Message "Re-enabling PowerShell v2 (via DISM)..." -Module "AdvancedSecurity"
Enable-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -NoRestart -ErrorAction Stop | Out-Null
}
elseif (-not $shouldEnable -and $isEnabled) {
Write-Log -Level INFO -Message "Disabling PowerShell v2 (via DISM)..." -Module "AdvancedSecurity"
Disable-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -NoRestart -ErrorAction Stop | Out-Null
}
else {
Write-Log -Level INFO -Message "PowerShell v2 state already matches backup ($($BackupData.State))" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to restore PowerShell v2: $_" -Module "AdvancedSecurity"
return $false
}
}
function Restore-AdminShares {
# Note: No parameters needed - registry restore happens separately via Core\Rollback.ps1
Write-Log -Level INFO -Message "Restoring Admin Shares..." -Module "AdvancedSecurity"
try {
# The backup contains a list of shares that existed.
# If we disabled them, they might be gone or the AutoShareServer/Wks registry keys were changed.
# Registry keys are handled by the generic Registry restore!
# So we mainly need to verify if we need to manually recreate shares or if registry restore + reboot is enough.
# Changing AutoShareServer/AutoShareWks requires a reboot to take effect.
# So simply restoring the registry keys (which happens before this) should be sufficient for the next boot.
# However, we can try to force re-creation if possible, but usually LanmanServer needs restart.
Write-Log -Level INFO -Message "Admin Shares settings restored via Registry. A reboot is required to fully restore shares." -Module "AdvancedSecurity"
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to restore Admin Shares: $_" -Module "AdvancedSecurity"
return $false
}
}
function Restore-NetBIOSAdapters {
<#
.SYNOPSIS
Restore NetBIOS over TCP/IP settings on network adapters
.DESCRIPTION
Restores the TcpipNetbiosOptions setting on each network adapter
to its pre-hardening state.
TcpipNetbiosOptions values:
- 0 = Default (use DHCP option)
- 1 = Enable NetBIOS over TCP/IP
- 2 = Disable NetBIOS over TCP/IP (set by hardening)
.PARAMETER BackupData
JSON backup data containing adapter descriptions and their original TcpipNetbiosOptions
#>
param($BackupData)
Write-Log -Level INFO -Message "Restoring NetBIOS over TCP/IP settings on network adapters..." -Module "AdvancedSecurity"
try {
# BackupData can be an array directly or have a nested structure
$adaptersToRestore = if ($BackupData -is [Array]) { $BackupData } else { @($BackupData) }
if ($adaptersToRestore.Count -eq 0) {
Write-Log -Level INFO -Message "No NetBIOS adapter settings to restore" -Module "AdvancedSecurity"
return $true
}
$restoredCount = 0
$failedCount = 0
# Get current adapters
$currentAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE" -ErrorAction SilentlyContinue
foreach ($backupAdapter in $adaptersToRestore) {
try {
# Find matching adapter by Index (most reliable) or Description
$targetAdapter = $currentAdapters | Where-Object {
$_.Index -eq $backupAdapter.Index -or
$_.Description -eq $backupAdapter.Description
} | Select-Object -First 1
if ($targetAdapter) {
$originalSetting = $backupAdapter.TcpipNetbiosOptions
# Only restore if different from current
if ($targetAdapter.TcpipNetbiosOptions -ne $originalSetting) {
$result = Invoke-CimMethod -InputObject $targetAdapter -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions = $originalSetting }
if ($result.ReturnValue -eq 0) {
Write-Log -Level DEBUG -Message "Restored NetBIOS setting on adapter '$($targetAdapter.Description)' to $originalSetting" -Module "AdvancedSecurity"
$restoredCount++
}
else {
Write-Log -Level WARNING -Message "SetTcpipNetbios returned $($result.ReturnValue) for adapter '$($targetAdapter.Description)'" -Module "AdvancedSecurity"
$failedCount++
}
}
else {
Write-Log -Level DEBUG -Message "NetBIOS setting on adapter '$($targetAdapter.Description)' already matches backup ($originalSetting)" -Module "AdvancedSecurity"
$restoredCount++
}
}
else {
Write-Log -Level WARNING -Message "Adapter not found for restore: Index=$($backupAdapter.Index), Description='$($backupAdapter.Description)'" -Module "AdvancedSecurity"
$failedCount++
}
}
catch {
Write-Log -Level WARNING -Message "Failed to restore NetBIOS on adapter '$($backupAdapter.Description)': $_" -Module "AdvancedSecurity"
$failedCount++
}
}
if ($failedCount -eq 0) {
Write-Log -Level SUCCESS -Message "NetBIOS settings restored on $restoredCount adapter(s)" -Module "AdvancedSecurity"
return $true
}
else {
Write-Log -Level WARNING -Message "NetBIOS restore completed with issues: $restoredCount succeeded, $failedCount failed" -Module "AdvancedSecurity"
return $true # Still return true - partial success is acceptable
}
}
catch {
Write-Log -Level ERROR -Message "Failed to restore NetBIOS adapter settings: $_" -Module "AdvancedSecurity"
return $false
}
}

View file

@ -0,0 +1,188 @@
function Test-AdvancedSecurity {
<#
.SYNOPSIS
Test Advanced Security compliance
.DESCRIPTION
Runs all compliance tests for Advanced Security hardening and returns
a comprehensive report of the current security posture.
Tests include:
- RDP Security (NLA enforcement, SSL/TLS, disable status)
- WDigest Protection (credential caching disabled)
- Administrative Shares (disabled and removed)
- Risky Firewall Ports (LLMNR, NetBIOS, UPnP/SSDP closed)
- Risky Network Services (SSDPSRV, upnphost, lmhosts stopped)
- Discovery Protocols (WS-Discovery, mDNS)
.EXAMPLE
Test-AdvancedSecurity
Runs all compliance tests and displays results
.EXAMPLE
$results = Test-AdvancedSecurity
$results | Format-Table
.OUTPUTS
Array of PSCustomObjects with compliance results
#>
[CmdletBinding()]
param()
try {
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " ADVANCED SECURITY COMPLIANCE TEST" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
$results = @()
# 1. RDP Security
Write-Host "Testing RDP Security..." -ForegroundColor Gray
$rdpTest = Test-RdpSecurity
$results += $rdpTest
# 2. WDigest Protection
Write-Host "Testing WDigest Protection..." -ForegroundColor Gray
$wdigestTest = Test-WDigest
$results += $wdigestTest
# 3. Admin Shares
Write-Host "Testing Administrative Shares..." -ForegroundColor Gray
$adminSharesTest = Test-AdminShares
$results += $adminSharesTest
# 4. Legacy TLS
Write-Host "Testing Legacy TLS (1.0/1.1)..." -ForegroundColor Gray
$tlsTest = Test-LegacyTLS
$results += $tlsTest
# 5. WPAD
Write-Host "Testing WPAD Configuration..." -ForegroundColor Gray
$wpadTest = Test-WPAD
$results += $wpadTest
# 6. PowerShell v2
Write-Host "Testing PowerShell v2 Status..." -ForegroundColor Gray
$psv2Test = Test-PowerShellV2
$results += $psv2Test
# 7. Risky Ports
Write-Host "Testing Risky Firewall Ports..." -ForegroundColor Gray
$riskyPortsTest = Test-RiskyPorts
$results += $riskyPortsTest
# 8. Risky Services
Write-Host "Testing Risky Network Services..." -ForegroundColor Gray
$riskyServicesTest = Test-RiskyServices
$results += $riskyServicesTest
# 9. SRP Configuration (CVE-2025-9491)
Write-Host "Testing SRP Configuration (CVE-2025-9491)..." -ForegroundColor Gray
$srpTest = Test-SRPCompliance
$results += $srpTest
# 10. Windows Update Configuration
Write-Host "Testing Windows Update Configuration..." -ForegroundColor Gray
$wuTest = Test-WindowsUpdate
$results += $wuTest
# 11. Finger Protocol Block
Write-Host "Testing Finger Protocol Block..." -ForegroundColor Gray
$fingerTest = Test-FingerProtocol
$results += $fingerTest
# 12. Wireless Display Security
Write-Host "Testing Wireless Display Security..." -ForegroundColor Gray
$wirelessDisplayTest = Test-WirelessDisplaySecurity
if ($wirelessDisplayTest) {
$results += [PSCustomObject]@{
Feature = "Wireless Display Security"
Compliant = $wirelessDisplayTest.Compliant
Details = if ($wirelessDisplayTest.FullyDisabled) { "Fully Disabled" }
elseif ($wirelessDisplayTest.Compliant) { "Hardened (receiving blocked, PIN required)" }
else { "NOT HARDENED - screen interception possible!" }
}
}
# 13. Discovery Protocols (WS-Discovery + mDNS) - Maximum profile only
Write-Host "Testing Discovery Protocols (WS-Discovery + mDNS)..." -ForegroundColor Gray
$discoveryTest = Test-DiscoveryProtocolsSecurity
if ($discoveryTest) {
$results += [PSCustomObject]@{
Feature = "Discovery Protocols (WS-Discovery + mDNS)"
Status = if ($discoveryTest.Compliant) { "Secure" } else { "Insecure" }
Details = "mDNS=" + $(if ($discoveryTest.EnableMDNS -eq 0) { "Disabled" } else { "Enabled/Not Set" }) +
"; Services: FDResPub=" + $discoveryTest.FDResPubDisabled + ", fdPHost=" + $discoveryTest.FdPHostDisabled +
"; FirewallRulesEnabled=" + $discoveryTest.FirewallRulesEnabled
Compliant = $discoveryTest.Compliant
}
}
# 14. Firewall Shields Up (optional - Maximum profile only)
Write-Host "Testing Firewall Shields Up (Public)..." -ForegroundColor Gray
$shieldsUpTest = Test-FirewallShieldsUp
# Always pass - this is an optional hardening only for the Maximum (air-gapped) profile
$results += [PSCustomObject]@{
Feature = "Firewall Shields Up (Public)"
Compliant = $shieldsUpTest.Pass
Details = $shieldsUpTest.Message
}
# 15. IPv6 Disable (optional - Maximum profile only, mitm6 mitigation)
Write-Host "Testing IPv6 Security (mitm6 mitigation)..." -ForegroundColor Gray
$ipv6Test = Test-IPv6Security
# Always pass - this is an optional hardening only for the Maximum profile
$results += [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Compliant = $ipv6Test.Pass
Details = $ipv6Test.Message
}
# Summary
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " COMPLIANCE SUMMARY" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
$compliantCount = ($results | Where-Object { $_.Compliant -eq $true }).Count
$totalTests = $results.Count
$compliancePercent = [math]::Round(($compliantCount / $totalTests) * 100, 1)
Write-Host "Total Tests: $totalTests" -ForegroundColor White
Write-Host "Compliant: $compliantCount" -ForegroundColor Green
Write-Host "Non-Compliant: $($totalTests - $compliantCount)" -ForegroundColor Red
Write-Host "Compliance: $compliancePercent%" -ForegroundColor $(if ($compliancePercent -ge 80) { 'Green' } elseif ($compliancePercent -ge 50) { 'Yellow' } else { 'Red' })
Write-Host ""
# Detailed results table
Write-Host "DETAILED RESULTS:" -ForegroundColor White
Write-Host ""
$tableFormat = @{Expression = { $_.Feature }; Label = "Feature"; Width = 30 },
@{Expression = { $_.Status }; Label = "Status"; Width = 20 },
@{Expression = { if ($_.Compliant) { "[X]" }else { "[ ]" } }; Label = "Compliant"; Width = 10 }
$results | Format-Table $tableFormat -AutoSize
Write-Host ""
# Return structured object with metadata for programmatic use
return [PSCustomObject]@{
Results = $results
TotalChecks = $totalTests
CompliantCount = $compliantCount
Compliance = $compliancePercent
}
}
catch {
Write-Log -Level ERROR -Message "Failed to run compliance tests: $_" -Module "AdvancedSecurity" -Exception $_.Exception
Write-Host ""
Write-Host "ERROR: Failed to run compliance tests" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Gray
Write-Host ""
return $null
}
}

View file

@ -0,0 +1,35 @@
@{
RootModule = 'AntiAI.psm1'
ModuleVersion = '2.2.0'
GUID = 'f8e9d7c6-5b4a-3c2d-1e0f-9a8b7c6d5e4f'
Author = 'NexusOne23'
CompanyName = 'Open Source Project'
Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.'
Description = 'Comprehensive Windows 11 AI deactivation - Disables all 8+ AI features using official Microsoft policies (Recall, Copilot, Paint AI, Notepad AI, Click to Do, Settings Agent). Maximum compliance mode with enterprise-grade Recall protection.'
PowerShellVersion = '5.1'
FunctionsToExport = @(
'Invoke-AntiAI'
)
PrivateData = @{
PSData = @{
Tags = @('Windows11', 'AI', 'Privacy', 'Security', 'Recall', 'Copilot', 'AntiAI')
ProjectUri = 'https://github.com/yourusername/NoIDPrivacy'
ReleaseNotes = @'
v1.0.0 - Initial Release
- Disables 8+ Windows 11 AI features using official Microsoft policies
- Master switch: Blocks all generative AI models (Paint, Notepad, Photos, Clipchamp, Snipping Tool)
- Windows Recall: Complete deactivation (component removal + snapshots + data providers)
- Windows Recall: Enterprise protection (app/URI deny lists, storage limits)
- Windows Copilot: System-wide deactivation + hardware key remapping
- Click to Do: Screenshot analysis disabled
- Paint AI: Cocreator, Generative Fill, Image Creator disabled
- Notepad AI: Write, Summarize, Rewrite features disabled
- Settings Agent: AI-powered search in Settings disabled
- Full backup/restore capability
- Comprehensive compliance verification
'@
}
}
}

View file

@ -0,0 +1,60 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
AntiAI Module Loader
.DESCRIPTION
Disables all Windows 11 AI features using official Microsoft policies.
Includes Recall, Copilot, Paint AI, Notepad AI, Click to Do, Settings Agent, and Explorer AI Actions.
.NOTES
Module: AntiAI
Version: 2.2.0
Author: NoID Privacy
#>
Set-StrictMode -Version Latest
# Get module root path
$script:ModuleRoot = $PSScriptRoot
# Import private functions
$privateFunctions = @(
'Backup-AntiAISettings'
'Restore-AntiAISettings'
'Test-AntiAICompliance'
'Set-SystemAIModels'
'Disable-Recall'
'Set-RecallProtection'
'Disable-Copilot'
'Disable-CopilotAdvanced' # NEW v2.2.0: URI handlers, Edge sidebar, Recall export
'Disable-ClickToDo'
'Disable-SettingsAgent'
'Disable-ExplorerAI' # NEW: File Explorer AI Actions menu
'Disable-NotepadAI'
'Disable-PaintAI'
)
foreach ($function in $privateFunctions) {
$functionPath = Join-Path $ModuleRoot "Private\$function.ps1"
if (Test-Path $functionPath) {
. $functionPath
}
}
# Import public functions
$publicFunctions = @(
'Invoke-AntiAI'
)
foreach ($function in $publicFunctions) {
$functionPath = Join-Path $ModuleRoot "Public\$function.ps1"
if (Test-Path $functionPath) {
. $functionPath
}
}
# Export public functions + Test-AntiAICompliance (needed for Invoke-AntiAI verification)
Export-ModuleMember -Function @($publicFunctions + 'Test-AntiAICompliance')

View file

@ -0,0 +1,419 @@
{
"ModuleName": "AntiAI",
"Version": "1.0.0",
"Description": "Maximum AI deactivation - Disables all 13 Windows 11 AI features using official Microsoft policies",
"Mode": "Maximum Compliance (Enterprise-Grade)",
"TotalFeatures": 13,
"TotalPolicies": 32,
"Features": {
"1_GenerativeAI_Master": {
"Name": "Generative AI Master Switch",
"Description": "Blocks ALL apps from using Windows on-device generative AI models AND app-level generative AI access",
"Impact": "Disables generative AI in Notepad, Paint, Photos, Clipchamp, Snipping Tool, and all future apps. Also blocks app access to generative AI features.",
"CloudBased": false,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\AppPrivacy": {
"LetAppsAccessSystemAIModels": {
"Type": "DWord",
"Value": 2,
"Description": "Force Deny - No app can access on-device generative AI models (0=User decides, 1=Force Allow, 2=Force Deny)"
},
"LetAppsAccessGenerativeAI": {
"Type": "DWord",
"Value": 2,
"Description": "Force Deny - Block app access to generative AI features (Text & Image Generation in Settings)"
}
},
"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\systemAIModels": {
"Value": {
"Type": "String",
"Value": "Deny",
"Description": "CapabilityAccessManager Workaround - Blocks Paint Generative Erase/Background Removal"
}
}
}
},
"2_Windows_Recall": {
"Name": "Windows Recall (Complete Deactivation + Enterprise Protection)",
"Description": "Takes continuous screenshots of EVERYTHING on your screen (passwords, banking, private messages) and stores them locally for AI-powered search. EXTREME privacy risk!",
"Impact": "Component completely removed from system, all snapshots deleted, background data providers disabled, apps/URLs protected",
"CloudBased": false,
"RequiresReboot": true,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"AllowRecallEnablement": {
"Type": "DWord",
"Value": 0,
"Description": "REMOVE Recall component from system + delete all existing snapshots (requires reboot)"
},
"DisableAIDataAnalysis": {
"Type": "DWord",
"Value": 1,
"Description": "PREVENT saving new snapshots for Recall (Device-scope)"
}
},
"HKCU:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"DisableAIDataAnalysis": {
"Type": "DWord",
"Value": 1,
"Description": "PREVENT saving new snapshots for Recall (User-scope)"
},
"DisableRecallDataProviders": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Recall background data providers (Enterprise/Education only)"
}
}
},
"EnterpriseProtection": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"SetDenyAppListForRecall": {
"Type": "MultiString",
"Value": [
"Microsoft.MicrosoftEdge_8wekyb3d8bbwe!App",
"Microsoft.WindowsTerminal_8wekyb3d8bbwe!App",
"KeePassXC_8wekyb3d8bbwe!KeePassXC",
"Microsoft.RemoteDesktop_8wekyb3d8bbwe!App"
],
"Description": "Apps NEVER captured in snapshots (Browser for Banking, Terminal for Passwords, KeePass, RDP)"
},
"SetDenyUriListForRecall": {
"Type": "MultiString",
"Value": [
"*.bank.*",
"*.paypal.*",
"*.bankofamerica.*",
"mail.*",
"webmail.*",
"*password*",
"*login*"
],
"Description": "Websites/URLs NEVER captured in snapshots (Banking, Email, Login pages)"
},
"SetMaximumStorageDurationForRecallSnapshots": {
"Type": "DWord",
"Value": 30,
"Description": "Maximum snapshot retention: 30 days (Choices: 30/60/90/180 days, 0=OS default)"
},
"SetMaximumStorageSpaceForRecallSnapshots": {
"Type": "DWord",
"Value": 10,
"Description": "Maximum snapshot storage: 10 GB (Choices: 10/25/50/75/100/150 GB, 0=OS default)"
}
}
}
},
"3_Windows_Copilot": {
"Name": "Windows Copilot (System-Wide AI Assistant - 4-Layer Defense)",
"Description": "Microsoft's AI assistant integrated into Windows (chat, suggestions, proactive recommendations)",
"Impact": "Copilot completely disabled in UI, taskbar, and search. Hardware Copilot key remapped to Notepad.",
"CloudBased": true,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"TurnOffWindowsCopilot": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Copilot Layer 1 (WindowsAI HKLM)"
}
},
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsCopilot": {
"TurnOffWindowsCopilot": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Copilot Layer 2 (WindowsCopilot HKLM)"
},
"ShowCopilotButton": {
"Type": "DWord",
"Value": 0,
"Description": "HIDE Copilot Layer 3 (Taskbar Button Hidden HKLM)"
}
},
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Explorer": {
"DisableWindowsCopilot": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Copilot Layer 4 (Explorer Integration HKLM)"
}
},
"HKCU:\\Software\\Policies\\Microsoft\\Windows\\WindowsCopilot": {
"TurnOffWindowsCopilot": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Windows Copilot User-scope (HKCU)"
},
"ShowCopilotButton": {
"Type": "DWord",
"Value": 0,
"Description": "HIDE Copilot Button User-scope (HKCU)"
}
},
"HKCU:\\Software\\Policies\\Microsoft\\Windows\\WindowsAI": {
"SetCopilotHardwareKey": {
"Type": "String",
"Value": "Microsoft.WindowsNotepad_8wekyb3d8bbwe!App",
"Description": "REMAP hardware Copilot key to open Notepad instead"
}
}
}
},
"4_Click_to_Do": {
"Name": "Click to Do (Screenshot AI Analysis)",
"Description": "Takes on-demand screenshots and analyzes them with AI to suggest actions (copy text, search, call numbers)",
"Impact": "Screenshot analysis feature completely disabled",
"CloudBased": false,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"DisableClickToDo": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Click to Do (no screenshot AI analysis, no action suggestions)"
}
},
"HKCU:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"DisableClickToDo": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Click to Do (User-scope)"
}
}
}
},
"5_Paint_Cocreator": {
"Name": "Paint Cocreator (Cloud-Based Image Generation)",
"Description": "Text-to-image generation using cloud AI (type description, AI creates artwork)",
"Impact": "Cocreator feature completely removed from Paint app",
"CloudBased": true,
"Registry": {
"HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Paint": {
"DisableCocreator": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Paint Cocreator (no AI-generated images from text prompts)"
}
}
}
},
"6_Paint_Generative_Fill": {
"Name": "Paint Generative Fill (Cloud-Based AI Editing)",
"Description": "AI-powered image editing (fill selected areas with AI-generated content)",
"Impact": "Generative Fill feature completely removed from Paint app",
"CloudBased": true,
"Registry": {
"HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Paint": {
"DisableGenerativeFill": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Paint Generative Fill (no AI-powered content-aware fill)"
}
}
}
},
"7_Paint_Image_Creator": {
"Name": "Paint Image Creator (Cloud-Based AI Art)",
"Description": "DALL-E powered AI art generator integrated into Paint",
"Impact": "Image Creator feature completely removed from Paint app",
"CloudBased": true,
"Registry": {
"HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Paint": {
"DisableImageCreator": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Paint Image Creator (no DALL-E AI art generation)"
}
}
}
},
"8_Notepad_AI": {
"Name": "Notepad AI (Copilot Integration - Write, Summarize, Rewrite)",
"Description": "GPT-powered AI features in Notepad (generate text, summarize content, rewrite paragraphs)",
"Impact": "All AI features completely removed from Notepad app",
"CloudBased": true,
"RequiresADMX": false,
"Note": "ADMX file (WindowsNotepad.admx) is NOT required - registry policy works without it. ADMX only provides GUI visibility in gpedit.msc.",
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\WindowsNotepad": {
"DisableAIFeatures": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE all AI features in Notepad (Write, Summarize, Rewrite, Explain) - Microsoft official registry value name"
}
}
}
},
"9_Settings_Agent": {
"Name": "Settings Agent (AI-Powered Settings Search)",
"Description": "AI-enhanced natural language search in Windows Settings (understands questions like 'How do I change wallpaper?')",
"Impact": "AI search disabled, only classic keyword search remains",
"CloudBased": false,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"DisableSettingsAgent": {
"Type": "DWord",
"Value": 1,
"Description": "DISABLE Settings AI agent (fallback to classic search without natural language understanding)"
}
}
}
},
"10_Recall_Export_Block": {
"Name": "Recall Export Prevention (NEW - KB5055627)",
"Description": "Prevents users from exporting Recall snapshots to share with apps/websites (EEA compliance feature)",
"Impact": "Export functionality completely disabled, prevents data exfiltration",
"CloudBased": false,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsAI": {
"AllowRecallExport": {
"Type": "DWord",
"Value": 0,
"Description": "PREVENT Recall snapshot export (0=Disabled, 1=Allowed)"
}
}
}
},
"11_Copilot_URI_Handlers": {
"Name": "Copilot URI Protocol Handlers (Deep Link Block)",
"Description": "Blocks ms-copilot: and ms-edge-copilot: URI handlers that bypass policy restrictions",
"Impact": "Prevents launching Copilot via deep links, Start menu search, or third-party apps",
"CloudBased": false,
"URIHandlers": [
"ms-copilot",
"ms-edge-copilot"
],
"Note": "These handlers are in HKEY_CLASSES_ROOT and route Copilot requests to Edge"
},
"12_Edge_Copilot_Sidebar": {
"Name": "Microsoft Edge Copilot Sidebar (Browser AI)",
"Description": "Disables Copilot integration in Edge browser sidebar",
"Impact": "Edge sidebar and Copilot features completely disabled",
"CloudBased": true,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Edge": {
"EdgeSidebarEnabled": {
"Type": "DWord",
"Value": 0,
"Description": "DISABLE Edge sidebar completely"
},
"ShowHubsSidebar": {
"Type": "DWord",
"Value": 0,
"Description": "HIDE sidebar panel"
},
"HubsSidebarEnabled": {
"Type": "DWord",
"Value": 0,
"Description": "DISABLE Hubs sidebar"
},
"CopilotPageContext": {
"Type": "DWord",
"Value": 0,
"Description": "PREVENT Copilot from accessing page content"
},
"CopilotCDPPageContext": {
"Type": "DWord",
"Value": 0,
"Description": "PREVENT Copilot CDP page context"
}
}
}
},
"13_Region_Policy_Override": {
"Name": "IntegratedServicesRegionPolicySet.json Override",
"Description": "Modifies Windows regional policy file to disable Copilot globally (bypasses region restrictions)",
"Impact": "Copilot disabled at OS level regardless of region setting",
"CloudBased": false,
"FilePath": "C:\\Windows\\System32\\IntegratedServicesRegionPolicySet.json",
"Note": "Requires TakeOwnership of system file - changes 'Windows CoPilot' policy to disabled"
},
"14_Copilot_Network_Block": {
"Name": "Copilot Network Block (Hosts File)",
"Description": "Blocks Copilot cloud endpoints via hosts file redirect",
"Impact": "Web-based Copilot completely unreachable",
"CloudBased": true,
"HostsEntries": [
"copilot.microsoft.com",
"www.bing.com/copilot",
"edgeservices.bing.com"
],
"Note": "Optional aggressive blocking - may affect legitimate Bing searches"
},
"15_File_Explorer_AI_Actions": {
"Name": "File Explorer AI Actions Menu",
"Description": "Hides 'AI Actions' entry from File Explorer right-click context menu (image editing, text summarization, etc.)",
"Impact": "AI Actions menu entry removed from Explorer context menu",
"CloudBased": false,
"Registry": {
"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Explorer": {
"HideAIActionsMenu": {
"Type": "DWord",
"Value": 1,
"Description": "HIDE AI Actions from File Explorer context menu"
}
}
}
}
},
"AutomaticallyBlockedByMasterSwitch": {
"Description": "These AI features are automatically blocked by the Generative AI Master Switch (no dedicated policies exist)",
"Features": [
{
"Name": "Photos Generative Erase",
"Description": "AI-powered object removal from photos"
},
{
"Name": "Photos Background Blur/Remove",
"Description": "AI background effects in Photos app"
},
{
"Name": "Photos Auto-Categorization",
"Description": "AI-powered photo organization (Receipts, IDs, Screenshots, Notes)"
},
{
"Name": "Snipping Tool AI-OCR",
"Description": "Text extraction and actions from screenshots"
},
{
"Name": "Snipping Tool Quick Redact",
"Description": "AI-powered sensitive data redaction"
},
{
"Name": "Clipchamp Auto Compose",
"Description": "AI-powered automatic video editing"
},
{
"Name": "All Future Generative AI Apps",
"Description": "Any app that uses Windows generative AI models"
}
]
},
"Summary": {
"TotalAIFeaturesDisabled": "13 features (10 dedicated + 3 advanced blocks + unlimited via master switch)",
"TotalPoliciesApplied": 32,
"RegistryKeysModified": 32,
"URIHandlersBlocked": 2,
"RequiresReboot": "Yes (for Recall component removal)",
"RequiresADMX": "No (all policies work via registry, no ADMX needed)",
"CloudAIBlocked": "All documented Windows 11 25H2 cloud-based AI features",
"OnDeviceAIBlocked": "All on-device generative AI models via systemAIModels API",
"EdgeAIBlocked": "Copilot sidebar, page context, all AI integrations",
"DeepLinksBlocked": "ms-copilot: and ms-edge-copilot: URI protocols",
"EnterpriseCompliance": "Maximum (Recall app/URI protection, storage limits, export block)",
"MSBestPractice": "Based on OFFICIAL Microsoft registry policies ONLY (no community workarounds)"
}
}

View file

@ -0,0 +1,97 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables Click to Do (screenshot AI analysis).
.DESCRIPTION
Applies DisableClickToDo = 1 policy (Device and User scope).
Click to Do takes on-demand screenshots and analyzes them with AI to suggest actions:
- Extract and copy text
- Search for selected content
- Call detected phone numbers
- Email detected addresses
Disabling prevents all screenshot AI analysis and action suggestions.
.EXAMPLE
Disable-ClickToDo
#>
function Disable-ClickToDo {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Click to Do (screenshot AI analysis)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Click to Do (DisableClickToDo=1)" -Module "AntiAI"
$result.Applied++
$result.Success = $true
return $result
}
# Device-scope (HKLM)
$devicePath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $devicePath)) {
New-Item -Path $devicePath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $devicePath" -Module "AntiAI"
}
$existing = Get-ItemProperty -Path $devicePath -Name "DisableClickToDo" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $devicePath -Name "DisableClickToDo" -Value 1 -Force
} else {
New-ItemProperty -Path $devicePath -Name "DisableClickToDo" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableClickToDo = 1 (Device-scope)" -Module "AntiAI"
$result.Applied++
# User-scope (HKCU)
$userPath = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $userPath)) {
New-Item -Path $userPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $userPath" -Module "AntiAI"
}
$existing = Get-ItemProperty -Path $userPath -Name "DisableClickToDo" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $userPath -Name "DisableClickToDo" -Value 1 -Force
} else {
New-ItemProperty -Path $userPath -Name "DisableClickToDo" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableClickToDo = 1 (User-scope)" -Module "AntiAI"
$result.Applied++
# Verify
$deviceValues = Get-ItemProperty -Path $devicePath -ErrorAction SilentlyContinue
$userValues = Get-ItemProperty -Path $userPath -ErrorAction SilentlyContinue
$verified = ($deviceValues.DisableClickToDo -eq 1) -and
($userValues.DisableClickToDo -eq 1)
if ($verified) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: Click to Do disabled" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Click to Do policy not applied correctly"
}
}
catch {
$result.Errors += "Failed to disable Click to Do: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,250 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables Windows Copilot system-wide (App Removal + Multi-Layer Policies).
.DESCRIPTION
Complete Copilot removal for Windows 11 24H2/25H2+:
LAYER 0: APP REMOVAL (NEW - Windows 11 24H2/25H2+)
- Removes Copilot AppX packages (current user, all users, provisioned)
- Prevents Copilot integration in Paint, Office, and other apps
- Microsoft Official: TurnOffWindowsCopilot policy is DEPRECATED in 24H2+
- Reference: https://learn.microsoft.com/en-us/windows/client-management/manage-windows-copilot
LEGACY LAYERS (for older Windows 11 versions):
- Layer 1-4: Registry policies (WindowsAI, WindowsCopilot, Explorer)
- Layer 5: Hardware key remap to Notepad
Multi-layer approach ensures maximum compatibility across all Windows 11 versions.
.PARAMETER DryRun
Simulates the operation without making changes.
.EXAMPLE
Disable-Copilot
.NOTES
Requires Administrator privileges.
Best Practice: Run with -Verbose to see detailed operation log.
#>
function Disable-Copilot {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Windows Copilot (multi-layer defense + app removal)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
CopilotAppRemoved = $false
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Copilot (app removal + policies + hardware key)" -Module "AntiAI"
$result.Applied += 8 # 1 app removal + 2 HKLM policies + 1 ShowButton + 1 Explorer + 2 HKCU + 1 HW key
$result.CopilotAppRemoved = $true
$result.Success = $true
return $result
}
# ============================================================================
# LAYER 0: REMOVE COPILOT APP (Windows 11 24H2/25H2+)
# ============================================================================
# Microsoft official: TurnOffWindowsCopilot policy is DEPRECATED in 24H2+
# New method: Uninstall Copilot app completely (prevents in-app integration)
# Reference: https://learn.microsoft.com/en-us/windows/client-management/manage-windows-copilot
Write-Log -Level DEBUG -Message "Layer 0: Removing Copilot app packages..." -Module "AntiAI"
# Step 1: Remove for current user
$copilotPackages = Get-AppxPackage -Name "*Copilot*" -ErrorAction SilentlyContinue
if ($copilotPackages) {
foreach ($package in $copilotPackages) {
try {
Remove-AppxPackage -Package $package.PackageFullName -ErrorAction Stop
Write-Log -Level DEBUG -Message "Removed Copilot package: $($package.Name)" -Module "AntiAI"
$result.CopilotAppRemoved = $true
}
catch {
Write-Log -Level DEBUG -Message "Could not remove package $($package.Name): $($_.Exception.Message)" -Module "AntiAI"
}
}
}
# Step 2: Remove for all users (requires admin)
$copilotAllUsers = Get-AppxPackage -AllUsers -Name "*Copilot*" -ErrorAction SilentlyContinue
if ($copilotAllUsers) {
foreach ($package in $copilotAllUsers) {
try {
Remove-AppxPackage -Package $package.PackageFullName -AllUsers -ErrorAction Stop
Write-Log -Level DEBUG -Message "Removed Copilot package (all users): $($package.Name)" -Module "AntiAI"
$result.CopilotAppRemoved = $true
}
catch {
Write-Log -Level DEBUG -Message "Could not remove package for all users: $($_.Exception.Message)" -Module "AntiAI"
}
}
}
# Step 3: Remove provisioned packages (prevents reinstall for new users)
$provisionedCopilot = Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
Where-Object { $_.PackageName -like "*Copilot*" }
if ($provisionedCopilot) {
foreach ($package in $provisionedCopilot) {
try {
Remove-AppxProvisionedPackage -Online -PackageName $package.PackageName -ErrorAction Stop | Out-Null
Write-Log -Level DEBUG -Message "Removed provisioned Copilot package: $($package.PackageName)" -Module "AntiAI"
$result.CopilotAppRemoved = $true
}
catch {
Write-Log -Level DEBUG -Message "Could not remove provisioned package: $($_.Exception.Message)" -Module "AntiAI"
}
}
}
if ($result.CopilotAppRemoved) {
Write-Log -Level DEBUG -Message "Layer 0: Copilot app packages removed successfully" -Module "AntiAI"
$result.Applied++
}
else {
Write-Log -Level DEBUG -Message "Layer 0: No Copilot app packages found (already removed or not installed)" -Module "AntiAI"
}
# ============================================================================
# LEGACY LAYERS: Registry policies (still needed for older Windows 11 versions)
# ============================================================================
# MULTI-LAYER COPILOT BLOCKING (SecurityBaseline Best Practice)
# Layer 1: WindowsAI\TurnOffWindowsCopilot (HKLM - machine-wide)
$aiPolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $aiPolicyPath)) {
New-Item -Path $aiPolicyPath -Force | Out-Null
}
$existing = Get-ItemProperty -Path $aiPolicyPath -Name "TurnOffWindowsCopilot" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $aiPolicyPath -Name "TurnOffWindowsCopilot" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $aiPolicyPath -Name "TurnOffWindowsCopilot" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Layer 1: WindowsAI\TurnOffWindowsCopilot (HKLM) = 1" -Module "AntiAI"
$result.Applied++
# Layer 2: WindowsCopilot\TurnOffWindowsCopilot (HKLM - legacy path)
$copilotPathHKLM = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot"
if (-not (Test-Path $copilotPathHKLM)) {
New-Item -Path $copilotPathHKLM -Force | Out-Null
}
$existing = Get-ItemProperty -Path $copilotPathHKLM -Name "TurnOffWindowsCopilot" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $copilotPathHKLM -Name "TurnOffWindowsCopilot" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $copilotPathHKLM -Name "TurnOffWindowsCopilot" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Layer 2: WindowsCopilot\TurnOffWindowsCopilot (HKLM) = 1" -Module "AntiAI"
$result.Applied++
# Layer 3: ShowCopilotButton = 0 (Hide taskbar button)
$existing = Get-ItemProperty -Path $copilotPathHKLM -Name "ShowCopilotButton" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $copilotPathHKLM -Name "ShowCopilotButton" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $copilotPathHKLM -Name "ShowCopilotButton" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Layer 3: ShowCopilotButton (HKLM) = 0" -Module "AntiAI"
$result.Applied++
# Layer 4: Explorer\DisableWindowsCopilot (Block Explorer integration)
$explorerPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer"
if (-not (Test-Path $explorerPath)) {
New-Item -Path $explorerPath -Force | Out-Null
}
$existing = Get-ItemProperty -Path $explorerPath -Name "DisableWindowsCopilot" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $explorerPath -Name "DisableWindowsCopilot" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $explorerPath -Name "DisableWindowsCopilot" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Layer 4: Explorer\DisableWindowsCopilot (HKLM) = 1" -Module "AntiAI"
$result.Applied++
# User-scope policies (HKCU - additional protection)
$copilotPathHKCU = "HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot"
if (-not (Test-Path $copilotPathHKCU)) {
New-Item -Path $copilotPathHKCU -Force | Out-Null
}
$existing = Get-ItemProperty -Path $copilotPathHKCU -Name "TurnOffWindowsCopilot" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $copilotPathHKCU -Name "TurnOffWindowsCopilot" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $copilotPathHKCU -Name "TurnOffWindowsCopilot" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "User-scope: WindowsCopilot\TurnOffWindowsCopilot (HKCU) = 1" -Module "AntiAI"
$result.Applied++
$existing = Get-ItemProperty -Path $copilotPathHKCU -Name "ShowCopilotButton" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $copilotPathHKCU -Name "ShowCopilotButton" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $copilotPathHKCU -Name "ShowCopilotButton" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "User-scope: ShowCopilotButton (HKCU) = 0" -Module "AntiAI"
$result.Applied++
# Layer 5: Remap hardware Copilot key to Notepad (neutralize dedicated key)
$aiPathHKCU = "HKCU:\Software\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $aiPathHKCU)) {
New-Item -Path $aiPathHKCU -Force | Out-Null
}
$notepadAUMID = "Microsoft.WindowsNotepad_8wekyb3d8bbwe!App"
$existing = Get-ItemProperty -Path $aiPathHKCU -Name "SetCopilotHardwareKey" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $aiPathHKCU -Name "SetCopilotHardwareKey" -Value $notepadAUMID -Force | Out-Null
} else {
New-ItemProperty -Path $aiPathHKCU -Name "SetCopilotHardwareKey" -Value $notepadAUMID -PropertyType String -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Layer 5: Hardware Copilot key remapped to Notepad" -Module "AntiAI"
$result.Applied++
# Verify all layers
$aiHKLM = Get-ItemProperty -Path $aiPolicyPath -ErrorAction SilentlyContinue
$copilotHKLM = Get-ItemProperty -Path $copilotPathHKLM -ErrorAction SilentlyContinue
$explorerHKLM = Get-ItemProperty -Path $explorerPath -ErrorAction SilentlyContinue
$copilotHKCU = Get-ItemProperty -Path $copilotPathHKCU -ErrorAction SilentlyContinue
$aiHKCU = Get-ItemProperty -Path $aiPathHKCU -ErrorAction SilentlyContinue
$verified = ($aiHKLM.TurnOffWindowsCopilot -eq 1) -and
($copilotHKLM.TurnOffWindowsCopilot -eq 1) -and
($copilotHKLM.ShowCopilotButton -eq 0) -and
($explorerHKLM.DisableWindowsCopilot -eq 1) -and
($copilotHKCU.TurnOffWindowsCopilot -eq 1) -and
($copilotHKCU.ShowCopilotButton -eq 0) -and
($aiHKCU.SetCopilotHardwareKey -eq $notepadAUMID)
if ($verified) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: All Copilot policies configured (MS official keys only)" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Not all Copilot policies applied correctly"
}
}
catch {
$result.Errors += "Failed to disable Copilot: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,206 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Advanced Copilot blocking - URI handlers, Edge sidebar, region policy, network block.
.DESCRIPTION
Multi-layer advanced Copilot blocking for Windows 11 24H2/25H2+:
LAYER 1: RECALL EXPORT BLOCK (KB5055627)
- AllowRecallExport = 0 (prevents snapshot export)
LAYER 2: URI PROTOCOL HANDLERS
- Blocks ms-copilot: and ms-edge-copilot: deep links
- Prevents Start menu search and third-party app launching
LAYER 3: EDGE COPILOT SIDEBAR
- Disables sidebar completely
- Blocks page context access
- 5 registry policies
LAYER 4: REGION POLICY OVERRIDE (Optional)
- Modifies IntegratedServicesRegionPolicySet.json
- Disables Copilot at OS level regardless of region
LAYER 5: NETWORK BLOCK (Optional)
- Hosts file redirect for copilot endpoints
.PARAMETER DryRun
Simulates the operation without making changes.
.PARAMETER SkipNetworkBlock
Skip hosts file modification (less aggressive).
.PARAMETER SkipRegionPolicy
Skip IntegratedServicesRegionPolicySet.json modification.
.EXAMPLE
Disable-CopilotAdvanced
.NOTES
Requires Administrator privileges.
Part of NoID Privacy AntiAI Module v2.2.0
#>
function Disable-CopilotAdvanced {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Copilot (Advanced Layers)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
RecallExportBlocked = $false
URIHandlersBlocked = $false
EdgeSidebarDisabled = $false
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would apply advanced Copilot blocks" -Module "AntiAI"
$result.Applied = 3 # 3 official MS features: RecallExport, URIHandlers, EdgeSidebar
$result.Success = $true
return $result
}
# ============================================================================
# LAYER 1: RECALL EXPORT BLOCK (KB5055627 - NEW)
# ============================================================================
Write-Log -Level DEBUG -Message "Layer 1: Blocking Recall Export..." -Module "AntiAI"
$aiPolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $aiPolicyPath)) {
New-Item -Path $aiPolicyPath -Force | Out-Null
}
try {
$existing = Get-ItemProperty -Path $aiPolicyPath -Name "AllowRecallExport" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $aiPolicyPath -Name "AllowRecallExport" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $aiPolicyPath -Name "AllowRecallExport" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "AllowRecallExport = 0 (export disabled)" -Module "AntiAI"
$result.RecallExportBlocked = $true
$result.Applied++
}
catch {
Write-Log -Level WARNING -Message "Failed to set AllowRecallExport: $_" -Module "AntiAI"
$result.Errors += "AllowRecallExport: $_"
}
# ============================================================================
# LAYER 2: URI PROTOCOL HANDLERS (ms-copilot:, ms-edge-copilot:)
# ============================================================================
Write-Log -Level DEBUG -Message "Layer 2: Blocking URI handlers..." -Module "AntiAI"
$uriHandlers = @("ms-copilot", "ms-edge-copilot")
$uriBlocked = 0
foreach ($handler in $uriHandlers) {
$handlerPath = "Registry::HKEY_CLASSES_ROOT\$handler"
try {
if (Test-Path $handlerPath) {
# Rename the key to disable it (preserves for restore)
$backupPath = "Registry::HKEY_CLASSES_ROOT\${handler}_DISABLED_BY_NOID"
# Check if already disabled
if (-not (Test-Path $backupPath)) {
# Delete the original handler (blocks the protocol)
Remove-Item -Path $handlerPath -Recurse -Force -ErrorAction Stop
# Create marker for restore
New-Item -Path $backupPath -Force | Out-Null
New-ItemProperty -Path $backupPath -Name "OriginallyExisted" -Value 1 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $backupPath -Name "DisabledBy" -Value "NoID Privacy AntiAI" -PropertyType String -Force | Out-Null
New-ItemProperty -Path $backupPath -Name "DisabledAt" -Value (Get-Date -Format "o") -PropertyType String -Force | Out-Null
Write-Log -Level DEBUG -Message "Blocked URI handler: $handler" -Module "AntiAI"
$uriBlocked++
}
else {
Write-Log -Level DEBUG -Message "URI handler already blocked: $handler" -Module "AntiAI"
$uriBlocked++
}
}
else {
Write-Log -Level DEBUG -Message "URI handler not found (already removed): $handler" -Module "AntiAI"
$uriBlocked++
}
}
catch {
Write-Log -Level WARNING -Message "Failed to block URI handler $handler : $_" -Module "AntiAI"
$result.Errors += "URI $handler : $_"
}
}
if ($uriBlocked -gt 0) {
$result.URIHandlersBlocked = $true
$result.Applied++
}
# ============================================================================
# LAYER 3: EDGE COPILOT SIDEBAR
# ============================================================================
Write-Log -Level DEBUG -Message "Layer 3: Disabling Edge Copilot Sidebar..." -Module "AntiAI"
$edgePolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
if (-not (Test-Path $edgePolicyPath)) {
New-Item -Path $edgePolicyPath -Force | Out-Null
}
$edgePolicies = @(
@{ Name = "EdgeSidebarEnabled"; Value = 0; Desc = "Edge sidebar" },
@{ Name = "ShowHubsSidebar"; Value = 0; Desc = "Hubs sidebar visibility" },
@{ Name = "HubsSidebarEnabled"; Value = 0; Desc = "Hubs sidebar" },
@{ Name = "CopilotPageContext"; Value = 0; Desc = "Copilot page context" },
@{ Name = "CopilotCDPPageContext"; Value = 0; Desc = "Copilot CDP context" }
)
$edgeApplied = 0
foreach ($policy in $edgePolicies) {
try {
$existing = Get-ItemProperty -Path $edgePolicyPath -Name $policy.Name -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $edgePolicyPath -Name $policy.Name -Value $policy.Value -Force | Out-Null
} else {
New-ItemProperty -Path $edgePolicyPath -Name $policy.Name -Value $policy.Value -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Edge: $($policy.Name) = $($policy.Value)" -Module "AntiAI"
$edgeApplied++
}
catch {
Write-Log -Level WARNING -Message "Failed to set Edge policy $($policy.Name): $_" -Module "AntiAI"
}
}
if ($edgeApplied -eq $edgePolicies.Count) {
$result.EdgeSidebarDisabled = $true
$result.Applied++
}
# NOTE: Layer 4 (RegionPolicy) and Layer 5 (NetworkBlock) REMOVED
# Reason: NOT Microsoft Best Practice
# - IntegratedServicesRegionPolicySet.json: Community workaround, can break with updates
# - Hosts file blocking: "Not officially supported" per Microsoft Q&A
# We only use official Registry Policies as per MS documentation
# Determine overall success
$result.Success = ($result.RecallExportBlocked -or $result.URIHandlersBlocked -or
$result.EdgeSidebarDisabled) -and ($result.Errors.Count -eq 0)
Write-Log -Level DEBUG -Message "Advanced Copilot blocks applied: $($result.Applied)" -Module "AntiAI"
}
catch {
$result.Errors += "Critical error: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,76 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables AI Actions in File Explorer context menu.
.DESCRIPTION
Applies HideAIActionsMenu = 1 policy.
File Explorer AI Actions provides AI-powered features in the right-click menu:
- Image editing with AI (background removal, effects)
- Text summarization
- AI-powered file actions
Disabling removes the "AI Actions" entry from the File Explorer context menu.
.EXAMPLE
Disable-ExplorerAI
#>
function Disable-ExplorerAI {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling File Explorer AI Actions Menu" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Explorer AI Actions (HideAIActionsMenu=1)" -Module "AntiAI"
$result.Applied++
$result.Success = $true
return $result
}
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $regPath" -Module "AntiAI"
}
$existing = Get-ItemProperty -Path $regPath -Name "HideAIActionsMenu" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "HideAIActionsMenu" -Value 1 -Force
} else {
New-ItemProperty -Path $regPath -Name "HideAIActionsMenu" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set HideAIActionsMenu = 1 (AI Actions hidden from Explorer context menu)" -Module "AntiAI"
$result.Applied++
# Verify
$values = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
if ($values.HideAIActionsMenu -eq 1) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: Explorer AI Actions disabled" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Explorer AI Actions policy not applied"
}
}
catch {
$result.Errors += "Failed to disable Explorer AI Actions: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,87 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables all AI features in Notepad (Write, Summarize, Rewrite, Explain).
.DESCRIPTION
Applies DisableAIFeatures = 1 policy (Microsoft official registry value name).
Notepad AI features (GPT-powered):
- Write: Generate text from prompts
- Summarize: Condense long text into key points
- Rewrite: Rephrase text in different styles (formal, casual, professional)
- Explain: Clarify complex text
All features are cloud-based and require Copilot integration.
WARNING: Requires WindowsNotepad.admx for Group Policy (not required for direct registry).
ADMX Download: https://download.microsoft.com/download/72ea16a9-4cc9-4032-945d-3a56a483d034/WindowsNotepadAdminTemplates.cab
.EXAMPLE
Disable-NotepadAI
#>
function Disable-NotepadAI {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Notepad AI features (Write, Summarize, Rewrite, Explain)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
Warnings = @()
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Notepad AI (DisableAIFeatures=1)" -Module "AntiAI"
$result.Applied++
$result.Success = $true
return $result
}
$regPath = "HKLM:\SOFTWARE\Policies\WindowsNotepad"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $regPath" -Module "AntiAI"
}
# CRITICAL: Value name is "DisableAIFeatures" (NOT "DisableAIFeaturesInNotepad")
# Microsoft official registry value name from WindowsNotepad ADMX
$existing = Get-ItemProperty -Path $regPath -Name "DisableAIFeatures" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisableAIFeatures" -Value 1 -Force
} else {
New-ItemProperty -Path $regPath -Name "DisableAIFeatures" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableAIFeatures = 1 (All AI features disabled - Write/Summarize/Rewrite/Explain)" -Module "AntiAI"
$result.Applied++
# Note: WindowsNotepad.admx is NOT required - registry policy is fully effective without it
# ADMX only provides GUI visibility in gpedit.msc, which is irrelevant for scripted deployment
Write-Log -Level DEBUG -Message "Notepad AI disabled via registry policy (no ADMX required)" -Module "AntiAI"
# Verify with correct value name
$values = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
if ($values.DisableAIFeatures -eq 1) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: Notepad AI disabled (DisableAIFeatures=1)" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Notepad AI policy not applied (DisableAIFeatures not set)"
}
}
catch {
$result.Errors += "Failed to disable Notepad AI: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,104 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables all AI features in Paint (Cocreator, Generative Fill, Image Creator).
.DESCRIPTION
Applies 3 Paint AI policies:
1. DisableCocreator = 1 (Text-to-image generation)
2. DisableGenerativeFill = 1 (AI-powered content-aware fill)
3. DisableImageCreator = 1 (DALL-E art generator)
Paint AI features (cloud-based):
- Cocreator: Type description, AI generates artwork (e.g., "sunset over mountains")
- Generative Fill: Select area, AI fills with contextual content
- Image Creator: DALL-E powered AI art generation
All features require internet connection and send data to Microsoft cloud.
.EXAMPLE
Disable-PaintAI
#>
function Disable-PaintAI {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Paint AI features (Cocreator, Generative Fill, Image Creator)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Paint AI (Cocreator, GenerativeFill, ImageCreator)" -Module "AntiAI"
$result.Applied += 3
$result.Success = $true
return $result
}
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Paint"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $regPath" -Module "AntiAI"
}
# 1. Disable Cocreator (text-to-image)
$existing = Get-ItemProperty -Path $regPath -Name "DisableCocreator" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisableCocreator" -Value 1 -Force
} else {
New-ItemProperty -Path $regPath -Name "DisableCocreator" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableCocreator = 1 (Text-to-image generation disabled)" -Module "AntiAI"
$result.Applied++
# 2. Disable Generative Fill (AI content-aware fill)
$existing = Get-ItemProperty -Path $regPath -Name "DisableGenerativeFill" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisableGenerativeFill" -Value 1 -Force
} else {
New-ItemProperty -Path $regPath -Name "DisableGenerativeFill" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableGenerativeFill = 1 (AI content-aware fill disabled)" -Module "AntiAI"
$result.Applied++
# 3. Disable Image Creator (DALL-E art generator)
$existing = Get-ItemProperty -Path $regPath -Name "DisableImageCreator" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisableImageCreator" -Value 1 -Force
} else {
New-ItemProperty -Path $regPath -Name "DisableImageCreator" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableImageCreator = 1 (DALL-E art generation disabled)" -Module "AntiAI"
$result.Applied++
# Verify
$values = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
$verified = ($values.DisableCocreator -eq 1) -and
($values.DisableGenerativeFill -eq 1) -and
($values.DisableImageCreator -eq 1)
if ($verified) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: All Paint AI features disabled" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Not all Paint AI policies were applied"
}
}
catch {
$result.Errors += "Failed to disable Paint AI: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,120 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables Windows Recall completely (component removal + snapshots + data providers).
.DESCRIPTION
Applies 3 core Recall policies:
1. AllowRecallEnablement = 0 (Removes Recall component, deletes existing snapshots, requires reboot)
2. DisableAIDataAnalysis = 1 (Prevents new snapshots - Device and User scope)
3. DisableRecallDataProviders = 1 (Disables background data providers - Enterprise/Education)
WARNING: Requires system reboot for Recall component removal to take effect!
.EXAMPLE
Disable-Recall
#>
function Disable-Recall {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Windows Recall (component + snapshots + providers)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
RequiresReboot = $true
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Recall (AllowRecallEnablement=0, DisableAIDataAnalysis=1)" -Module "AntiAI"
$result.Success = $true
return $result
}
# Device-scope policies (HKLM)
$devicePath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $devicePath)) {
New-Item -Path $devicePath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $devicePath" -Module "AntiAI"
}
# 1. Remove Recall component (deletes bits + existing snapshots)
$existing = Get-ItemProperty -Path $devicePath -Name "AllowRecallEnablement" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $devicePath -Name "AllowRecallEnablement" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $devicePath -Name "AllowRecallEnablement" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set AllowRecallEnablement = 0 (Recall component will be removed on reboot)" -Module "AntiAI"
$result.Applied++
# 2. Disable AI data analysis (Device-scope)
$existing = Get-ItemProperty -Path $devicePath -Name "DisableAIDataAnalysis" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $devicePath -Name "DisableAIDataAnalysis" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $devicePath -Name "DisableAIDataAnalysis" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableAIDataAnalysis = 1 (Device-scope - no new snapshots)" -Module "AntiAI"
$result.Applied++
# User-scope policies (HKCU)
$userPath = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $userPath)) {
New-Item -Path $userPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $userPath" -Module "AntiAI"
}
# 3. Disable AI data analysis (User-scope)
$existing = Get-ItemProperty -Path $userPath -Name "DisableAIDataAnalysis" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $userPath -Name "DisableAIDataAnalysis" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $userPath -Name "DisableAIDataAnalysis" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableAIDataAnalysis = 1 (User-scope - no new snapshots)" -Module "AntiAI"
$result.Applied++
# 4. Disable Recall data providers (Enterprise/Education only, User-scope)
$existing = Get-ItemProperty -Path $userPath -Name "DisableRecallDataProviders" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $userPath -Name "DisableRecallDataProviders" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $userPath -Name "DisableRecallDataProviders" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableRecallDataProviders = 1 (Background data providers disabled)" -Module "AntiAI"
$result.Applied++
# Verify
$deviceValues = Get-ItemProperty -Path $devicePath -ErrorAction SilentlyContinue
$userValues = Get-ItemProperty -Path $userPath -ErrorAction SilentlyContinue
$verified = ($deviceValues.AllowRecallEnablement -eq 0) -and
($deviceValues.DisableAIDataAnalysis -eq 1) -and
($userValues.DisableAIDataAnalysis -eq 1) -and
($userValues.DisableRecallDataProviders -eq 1)
if ($verified) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: All Recall policies applied" -Module "AntiAI"
Write-Host "" # Ensure warning appears on new line
Write-Warning "REBOOT REQUIRED to remove Recall component and delete existing snapshots!"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Not all Recall policies were applied correctly"
}
}
catch {
$result.Errors += "Failed to disable Recall: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,77 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Disables Settings Agent (AI-powered Settings search).
.DESCRIPTION
Applies DisableSettingsAgent = 1 policy.
Settings Agent provides AI-enhanced natural language search in Windows Settings.
Examples of AI features:
- Understanding questions: "How do I change my wallpaper?"
- Contextual suggestions: "Change background" -> Desktop personalization
- Intelligent search results with natural language processing
Disabling falls back to classic keyword search without AI understanding.
.EXAMPLE
Disable-SettingsAgent
#>
function Disable-SettingsAgent {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Disabling Settings Agent (AI-powered search)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would disable Settings Agent (DisableAISettingsAgent=1)" -Module "AntiAI"
$result.Applied++
$result.Success = $true
return $result
}
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $regPath" -Module "AntiAI"
}
$existing = Get-ItemProperty -Path $regPath -Name "DisableSettingsAgent" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisableSettingsAgent" -Value 1 -Force
} else {
New-ItemProperty -Path $regPath -Name "DisableSettingsAgent" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set DisableSettingsAgent = 1 (AI search disabled, fallback to classic)" -Module "AntiAI"
$result.Applied++
# Verify
$values = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
if ($values.DisableSettingsAgent -eq 1) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: Settings Agent disabled" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Settings Agent policy not applied"
}
}
catch {
$result.Errors += "Failed to disable Settings Agent: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,130 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Applies enterprise-grade Recall protection (app/URI deny lists, storage limits).
.DESCRIPTION
Configures 4 additional Recall policies for maximum data protection:
1. SetDenyAppListForRecall - Apps never captured in snapshots (Browser, Terminal, Password managers, RDP)
2. SetDenyUriListForRecall - Websites/URLs never captured (Banking, Email, Login pages)
3. SetMaximumStorageDurationForRecallSnapshots - Max retention: 30 days
4. SetMaximumStorageSpaceForRecallSnapshots - Max storage: 10 GB
Note: These are additional protection layers BEYOND core Recall disable policies.
Even though Recall is disabled, these provide defense-in-depth.
.EXAMPLE
Set-RecallProtection
#>
function Set-RecallProtection {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Applying Recall enterprise protection (deny lists + storage limits)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
}
try {
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would set Recall protection (Deny lists + Storage limits)" -Module "AntiAI"
$result.Success = $true
return $result
}
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $regPath" -Module "AntiAI"
}
# 1. App Deny List - Critical apps never captured in snapshots
$denyApps = @(
"Microsoft.MicrosoftEdge_8wekyb3d8bbwe!App", # Edge Browser (Banking, passwords)
"Microsoft.WindowsTerminal_8wekyb3d8bbwe!App", # Terminal (CLI passwords, keys)
"KeePassXC_8wekyb3d8bbwe!KeePassXC", # Password Manager
"Microsoft.RemoteDesktop_8wekyb3d8bbwe!App" # RDP (remote system access)
)
# Store as proper MultiString (string array) so policies are visible to compliance checks
$existing = Get-ItemProperty -Path $regPath -Name "SetDenyAppListForRecall" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "SetDenyAppListForRecall" -Value $denyApps -Force
} else {
New-ItemProperty -Path $regPath -Name "SetDenyAppListForRecall" -Value $denyApps -PropertyType MultiString -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set App Deny List: $($denyApps.Count) critical apps protected" -Module "AntiAI"
$result.Applied++
# 2. URI Deny List - Critical websites never captured in snapshots
$denyUris = @(
"*.bank.*", # All banking sites
"*.paypal.*", # Payment processor
"*.bankofamerica.*", # Major bank
"mail.*", # Email sites
"webmail.*", # Webmail sites
"*password*", # Any password-related pages
"*login*" # Any login pages
)
# Store as MultiString using string array
$existing = Get-ItemProperty -Path $regPath -Name "SetDenyUriListForRecall" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "SetDenyUriListForRecall" -Value $denyUris -Force
} else {
New-ItemProperty -Path $regPath -Name "SetDenyUriListForRecall" -Value $denyUris -PropertyType MultiString -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set URI Deny List: $($denyUris.Count) URL patterns protected" -Module "AntiAI"
$result.Applied++
# 3. Storage Duration Limit - Max 30 days retention
$existing = Get-ItemProperty -Path $regPath -Name "SetMaximumStorageDurationForRecallSnapshots" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "SetMaximumStorageDurationForRecallSnapshots" -Value 30 -Force
} else {
New-ItemProperty -Path $regPath -Name "SetMaximumStorageDurationForRecallSnapshots" -Value 30 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set max snapshot retention: 30 days" -Module "AntiAI"
$result.Applied++
# 4. Storage Space Limit - Max 10 GB
$existing = Get-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -Value 10 -Force
} else {
New-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -Value 10 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set max snapshot storage: 10 GB" -Module "AntiAI"
$result.Applied++
# Verify
$values = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
$verified = ($null -ne $values.SetDenyAppListForRecall) -and
($null -ne $values.SetDenyUriListForRecall) -and
($values.SetMaximumStorageDurationForRecallSnapshots -eq 30) -and
($values.SetMaximumStorageSpaceForRecallSnapshots -eq 10)
if ($verified) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: All Recall protection policies applied" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: Not all Recall protection policies were applied"
}
}
catch {
$result.Errors += "Failed to apply Recall protection: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,118 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Sets the Generative AI Master Switch to block all apps from using AI models.
.DESCRIPTION
Configures LetAppsAccessSystemAIModels = 2 (Force Deny) to prevent ALL apps from
accessing Windows on-device generative AI models (text and image generation).
This master switch automatically blocks:
- Notepad AI (Write, Summarize, Rewrite)
- Paint AI (Cocreator, Generative Fill unless specifically disabled)
- Photos AI (Generative Erase, Background effects, Auto-categorization)
- Clipchamp AI (Auto Compose)
- Snipping Tool AI (OCR, Quick Redact)
- All future apps that use generative AI
.EXAMPLE
Set-SystemAIModels
#>
function Set-SystemAIModels {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
Write-Log -Level DEBUG -Message "Setting Generative AI Master Switch (Force Deny all apps)" -Module "AntiAI"
$result = [PSCustomObject]@{
Success = $false
Applied = 0
Errors = @()
}
try {
# 1. Set AppPrivacy Master Switch
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy"
if ($DryRun) {
Write-Log -Level DEBUG -Message "[DRYRUN] Would set $regPath\LetAppsAccessSystemAIModels = 2" -Module "AntiAI"
Write-Log -Level DEBUG -Message "[DRYRUN] Would set $regPath\LetAppsAccessGenerativeAI = 2" -Module "AntiAI"
Write-Log -Level DEBUG -Message "[DRYRUN] Would set CapabilityAccessManager\systemAIModels = Deny" -Module "AntiAI"
$result.Success = $true
return $result
}
# Ensure registry path exists
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $regPath" -Module "AntiAI"
}
# Set master switch: 2 = Force Deny (no app can access generative AI)
$existing = Get-ItemProperty -Path $regPath -Name "LetAppsAccessSystemAIModels" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "LetAppsAccessSystemAIModels" -Value 2 -Force
} else {
New-ItemProperty -Path $regPath -Name "LetAppsAccessSystemAIModels" -Value 2 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set LetAppsAccessSystemAIModels = 2 (Force Deny)" -Module "AntiAI"
$result.Applied++
# Set app-level Generative AI access: 2 = Force Deny (Text & Image Generation in Settings)
$existing2 = Get-ItemProperty -Path $regPath -Name "LetAppsAccessGenerativeAI" -ErrorAction SilentlyContinue
if ($null -ne $existing2) {
Set-ItemProperty -Path $regPath -Name "LetAppsAccessGenerativeAI" -Value 2 -Force
} else {
New-ItemProperty -Path $regPath -Name "LetAppsAccessGenerativeAI" -Value 2 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set LetAppsAccessGenerativeAI = 2 (Force Deny)" -Module "AntiAI"
$result.Applied++
# Verify AppPrivacy switches
$value = Get-ItemProperty -Path $regPath -Name "LetAppsAccessSystemAIModels" -ErrorAction SilentlyContinue
$value2 = Get-ItemProperty -Path $regPath -Name "LetAppsAccessGenerativeAI" -ErrorAction SilentlyContinue
if ($value.LetAppsAccessSystemAIModels -eq 2 -and $value2.LetAppsAccessGenerativeAI -eq 2) {
Write-Log -Level DEBUG -Message "Verification SUCCESS: Both AppPrivacy AI switches are Force Deny" -Module "AntiAI"
}
else {
$result.Errors += "Verification FAILED: AppPrivacy AI switches not set correctly"
}
# 2. Set CapabilityAccessManager Deny (additional workaround for Paint Generative Erase/Background Removal)
$capabilityPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\systemAIModels"
if (-not (Test-Path $capabilityPath)) {
New-Item -Path $capabilityPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $capabilityPath" -Module "AntiAI"
}
$existing = Get-ItemProperty -Path $capabilityPath -Name "Value" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $capabilityPath -Name "Value" -Value "Deny" -Force
} else {
New-ItemProperty -Path $capabilityPath -Name "Value" -Value "Deny" -PropertyType String -Force | Out-Null
}
Write-Log -Level DEBUG -Message "Set CapabilityAccessManager\systemAIModels = Deny (workaround for undocumented AI features)" -Module "AntiAI"
$result.Applied++
# Verify CapabilityAccessManager
$capValue = Get-ItemProperty -Path $capabilityPath -Name "Value" -ErrorAction SilentlyContinue
if ($capValue.Value -eq "Deny") {
Write-Log -Level DEBUG -Message "Verification SUCCESS: CapabilityAccessManager is Deny" -Module "AntiAI"
$result.Success = $true
}
else {
$result.Errors += "Verification FAILED: CapabilityAccessManager not set correctly"
}
}
catch {
$result.Errors += "Failed to set Generative AI Master Switch: $($_.Exception.Message)"
Write-Error $result.Errors[-1]
}
return $result
}

View file

@ -0,0 +1,652 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Verifies that all AntiAI policies are correctly applied.
.DESCRIPTION
REGISTRY COMPLIANCE VERIFICATION (Self-Check + MS Policy Validation)
This script performs TWO types of checks:
A) SELF-CHECK (Primary):
Verifies that AntiAI module successfully set all intended registry keys:
- Generative AI Master Switch (LetAppsAccessSystemAIModels)
- Recall Core (AllowRecallEnablement, DisableAIDataAnalysis x2, DisableRecallDataProviders)
- Recall Protection (App/URI Deny Lists, Storage Duration/Space)
- Copilot (4-layer defense: WindowsAI, WindowsCopilot, ShowCopilotButton, Explorer, User-scope, Hardware Key)
- Click to Do (DisableClickToDo x2)
- Paint AI (DisableCocreator, DisableGenerativeFill, DisableImageCreator)
- Notepad AI (DisableAIFeatures)
- Settings Agent (DisableSettingsAgent)
B) MS POLICY VALIDATION (Secondary):
Checks for additional Microsoft-official registry keys that AntiAI module does NOT set,
but which could indicate incomplete deactivation or MS policy changes:
- PolicyManager paths (alternative policy enforcement)
- Additional WindowsAI keys introduced in newer Windows builds
- Alternative Copilot/Recall paths
IMPORTANT LIMITATIONS:
- This is a REGISTRY-ONLY check. It does NOT verify if AI features are functionally disabled.
- "PASS" means "registry keys are set correctly" NOT "AI features are 100% inactive".
- Microsoft may add new AI features or change registry paths in future Windows updates.
- Some AI features may still work via cloud APIs even with correct registry settings.
For functional verification, test AI features manually after applying policies.
.EXAMPLE
.\Test-AntiAICompliance.ps1
Runs full compliance check and displays results.
.NOTES
Author: NoID Privacy
Version: 2.2.0 (Extended validation)
Requires: Windows 11 24H2+, Administrator privileges
#>
# Helper function to check registry value (must be outside main function)
function Test-RegistryValue {
param(
[string]$Path,
[string]$Name,
$ExpectedValue,
[string]$Description
)
$check = @{
Description = $Description
Path = "$Path\$Name"
Expected = $ExpectedValue
Actual = $null
Status = "FAIL"
}
try {
if (Test-Path $Path) {
$value = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue
if ($null -ne $value) {
$check.Actual = $value.$Name
# Handle different value types
if ($ExpectedValue -is [array]) {
# MultiString comparison - verify arrays match
if ($check.Actual -is [array]) {
# Check if arrays have same length and all items match
if ($check.Actual.Count -eq $ExpectedValue.Count) {
$allMatch = $true
foreach ($expectedItem in $ExpectedValue) {
if ($check.Actual -notcontains $expectedItem) {
$allMatch = $false
break
}
}
$check.Status = if ($allMatch) { "PASS" } else { "FAIL" }
}
else {
# Different array lengths - still OK if all expected items are present
# (allows for extra items set by policy)
$allPresent = $true
foreach ($expectedItem in $ExpectedValue) {
if ($check.Actual -notcontains $expectedItem) {
$allPresent = $false
break
}
}
$check.Status = if ($allPresent) { "PASS" } else { "FAIL" }
}
}
else {
# Expected array but got single value or nothing
$check.Status = "FAIL"
}
}
else {
# Exact value comparison
$check.Status = if ($check.Actual -eq $ExpectedValue) { "PASS" } else { "FAIL" }
}
}
else {
$check.Actual = "NOT SET"
}
}
else {
$check.Actual = "PATH MISSING"
}
}
catch {
$check.Actual = "ERROR: $($_.Exception.Message)"
}
return $check
}
function Test-AntiAICompliance {
[CmdletBinding()]
param()
$startTime = Get-Date
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " ANTIAI COMPLIANCE VERIFICATION v2.2" -ForegroundColor Cyan
Write-Host " Registry-Based Policy Check" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Checking 32 AI Deactivation Policies" -ForegroundColor Cyan
Write-Host " + Advanced Copilot Blocks + MS Validation" -ForegroundColor DarkGray
Write-Host "========================================`n" -ForegroundColor Cyan
# Initialize results (TotalPolicies calculated dynamically)
$results = @{
Passed = 0
Failed = 0
Warnings = 0
Details = @()
MSConflicts = 0
MSAligned = 0
}
Write-Host "[1/13] Checking Generative AI Master Switch..." -ForegroundColor Yellow
$check = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy" `
-Name "LetAppsAccessSystemAIModels" `
-ExpectedValue 2 `
-Description "Generative AI Master (Force Deny all apps)"
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: Master switch blocks all generative AI" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: Expected 2 (Force Deny), got $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
# Additional check for LetAppsAccessGenerativeAI (Text & Image Generation in Settings)
$genAICheck = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy" `
-Name "LetAppsAccessGenerativeAI" `
-ExpectedValue 2 `
-Description "Generative AI App Access (Force Deny)"
$results.Details += $genAICheck
if ($genAICheck.Status -eq "PASS") {
Write-Host " PASS: App access to generative AI blocked" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: LetAppsAccessGenerativeAI not set (may allow AI features)" -ForegroundColor Red
$results.Failed++
}
# Additional CapabilityAccessManager check (workaround for Paint Generative Erase/Background Removal)
$capCheck = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\systemAIModels" `
-Name "Value" `
-ExpectedValue "Deny" `
-Description "CapabilityAccessManager systemAIModels (Workaround)"
$results.Details += $capCheck
if ($capCheck.Status -eq "PASS") {
Write-Host " PASS: CapabilityAccessManager blocks AI capabilities" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: CapabilityAccessManager not set (may allow Paint Generative Erase/Background Removal)" -ForegroundColor Red
$results.Failed++
}
Write-Host "`n[2/13] Checking Recall Core Policies..." -ForegroundColor Yellow
$recallChecks = @(
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "AllowRecallEnablement" -ExpectedValue 0 -Description "Recall Component Removal"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "DisableAIDataAnalysis" -ExpectedValue 1 -Description "Recall Snapshots Disabled (Device)"),
(Test-RegistryValue -Path "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "DisableAIDataAnalysis" -ExpectedValue 1 -Description "Recall Snapshots Disabled (User)"),
(Test-RegistryValue -Path "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "DisableRecallDataProviders" -ExpectedValue 1 -Description "Recall Data Providers Disabled")
)
foreach ($check in $recallChecks) {
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: $($check.Description)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: $($check.Description) - $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
}
Write-Host "`n[3/13] Checking Recall Enterprise Protection..." -ForegroundColor Yellow
# Expected deny lists (must match Set-RecallProtection.ps1)
$expectedDenyApps = @(
"Microsoft.MicrosoftEdge_8wekyb3d8bbwe!App",
"Microsoft.WindowsTerminal_8wekyb3d8bbwe!App",
"KeePassXC_8wekyb3d8bbwe!KeePassXC",
"Microsoft.RemoteDesktop_8wekyb3d8bbwe!App"
)
$expectedDenyUris = @(
"*.bank.*",
"*.paypal.*",
"*.bankofamerica.*",
"mail.*",
"webmail.*",
"*password*",
"*login*"
)
$protectionChecks = @(
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetDenyAppListForRecall" -ExpectedValue $expectedDenyApps -Description "App Deny List"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetDenyUriListForRecall" -ExpectedValue $expectedDenyUris -Description "URI Deny List"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetMaximumStorageDurationForRecallSnapshots" -ExpectedValue 30 -Description "Max Retention: 30 days"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetMaximumStorageSpaceForRecallSnapshots" -ExpectedValue 10 -Description "Max Storage: 10 GB")
)
foreach ($check in $protectionChecks) {
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: $($check.Description)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " WARN: $($check.Description) - $($check.Actual)" -ForegroundColor Yellow
$results.Warnings++
}
}
Write-Host "`n[4/13] Checking Windows Copilot (4-layer defense)..." -ForegroundColor Yellow
$copilotChecks = @(
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "TurnOffWindowsCopilot" -ExpectedValue 1 -Description "Copilot Layer 1 (WindowsAI HKLM)"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot" -Name "TurnOffWindowsCopilot" -ExpectedValue 1 -Description "Copilot Layer 2 (WindowsCopilot HKLM)"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot" -Name "ShowCopilotButton" -ExpectedValue 0 -Description "Copilot Layer 3 (Taskbar Button Hidden)"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" -Name "DisableWindowsCopilot" -ExpectedValue 1 -Description "Copilot Layer 4 (Explorer Integration)"),
(Test-RegistryValue -Path "HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot" -Name "TurnOffWindowsCopilot" -ExpectedValue 1 -Description "Copilot User-scope (HKCU)"),
(Test-RegistryValue -Path "HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot" -Name "ShowCopilotButton" -ExpectedValue 0 -Description "Copilot Button Hidden (User)"),
(Test-RegistryValue -Path "HKCU:\Software\Policies\Microsoft\Windows\WindowsAI" -Name "SetCopilotHardwareKey" -ExpectedValue "Microsoft.WindowsNotepad_8wekyb3d8bbwe!App" -Description "Hardware Key Remapped to Notepad")
)
foreach ($check in $copilotChecks) {
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: $($check.Description)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: $($check.Description) - $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
}
Write-Host "`n[5/13] Checking Click to Do..." -ForegroundColor Yellow
$clickChecks = @(
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "DisableClickToDo" -ExpectedValue 1 -Description "Click to Do Disabled (Device)"),
(Test-RegistryValue -Path "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "DisableClickToDo" -ExpectedValue 1 -Description "Click to Do Disabled (User)")
)
foreach ($check in $clickChecks) {
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: $($check.Description)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: $($check.Description) - $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
}
Write-Host "`n[6/13] Checking Paint AI..." -ForegroundColor Yellow
$paintChecks = @(
(Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint" -Name "DisableCocreator" -ExpectedValue 1 -Description "Paint Cocreator Disabled"),
(Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint" -Name "DisableGenerativeFill" -ExpectedValue 1 -Description "Paint Generative Fill Disabled"),
(Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint" -Name "DisableImageCreator" -ExpectedValue 1 -Description "Paint Image Creator Disabled")
)
foreach ($check in $paintChecks) {
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: $($check.Description)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: $($check.Description) - $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
}
Write-Host "`n[7/13] Checking Notepad AI..." -ForegroundColor Yellow
$check = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Policies\WindowsNotepad" `
-Name "DisableAIFeatures" `
-ExpectedValue 1 `
-Description "Notepad AI Disabled (Write/Summarize/Rewrite)"
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: Notepad AI completely disabled" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: Expected 1, got $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
Write-Host "`n[8/13] Checking Settings Agent..." -ForegroundColor Yellow
$check = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" `
-Name "DisableSettingsAgent" `
-ExpectedValue 1 `
-Description "Settings AI Agent Disabled"
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: Settings Agent disabled (classic search only)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: Expected 1, got $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
Write-Host "`n[9/13] Checking Explorer AI Actions..." -ForegroundColor Yellow
$check = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" `
-Name "HideAIActionsMenu" `
-ExpectedValue 1 `
-Description "Explorer AI Actions Hidden"
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: Explorer AI Actions menu hidden" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " FAIL: Expected 1, got $($check.Actual)" -ForegroundColor Red
$results.Failed++
}
Write-Host "`n[10/13] Checking Recall Export Block (NEW)..." -ForegroundColor Yellow
$check = Test-RegistryValue `
-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" `
-Name "AllowRecallExport" `
-ExpectedValue 0 `
-Description "Recall Export Disabled"
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: Recall snapshot export blocked" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " WARN: Recall export may be allowed (optional policy)" -ForegroundColor Yellow
$results.Warnings++
}
Write-Host "`n[11/13] Checking Edge Copilot Sidebar..." -ForegroundColor Yellow
$edgeCopilotChecks = @(
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Name "EdgeSidebarEnabled" -ExpectedValue 0 -Description "Edge Sidebar Disabled"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Name "ShowHubsSidebar" -ExpectedValue 0 -Description "Hubs Sidebar Hidden"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Name "HubsSidebarEnabled" -ExpectedValue 0 -Description "Hubs Sidebar Disabled"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Name "CopilotPageContext" -ExpectedValue 0 -Description "Copilot Page Context Blocked"),
(Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Name "CopilotCDPPageContext" -ExpectedValue 0 -Description "Copilot CDP Context Blocked")
)
foreach ($check in $edgeCopilotChecks) {
$results.Details += $check
if ($check.Status -eq "PASS") {
Write-Host " PASS: $($check.Description)" -ForegroundColor Green
$results.Passed++
}
else {
Write-Host " WARN: $($check.Description) - $($check.Actual)" -ForegroundColor Yellow
$results.Warnings++
}
}
Write-Host "`n[12/13] Checking Recall Component Status..." -ForegroundColor Yellow
# Check for Recall component status (Windows Optional Feature)
try {
$recallFeature = Get-WindowsOptionalFeature -Online -FeatureName "Recall" -ErrorAction SilentlyContinue
if ($null -ne $recallFeature) {
if ($recallFeature.State -eq "Disabled") {
Write-Host " PASS: Recall component is disabled" -ForegroundColor Green
}
else {
Write-Host " INFO: Recall component present but configured to be removed (reboot required)" -ForegroundColor Yellow
}
}
else {
Write-Host " PASS: Recall component not present on this system" -ForegroundColor Green
}
}
catch {
Write-Host " INFO: Cannot query Recall feature status" -ForegroundColor DarkGray
}
Write-Host "`n[13/13] Checking MS Policy Validation (Conflict Scanner)..." -ForegroundColor Yellow
$msConflicts = 0
$msInfo = 0
try {
# PolicyManager paths (alternative policy enforcement used by Intune/MDM)
$policyManagerChecks = @(
@{ Path = "HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\WindowsAI"; Name = "DisableAIDataAnalysis"; Desc = "Recall PolicyManager (MDM Current)" },
@{ Path = "HKLM:\SOFTWARE\Microsoft\PolicyManager\default\WindowsAI"; Name = "DisableAIDataAnalysis"; Desc = "Recall PolicyManager (MDM Default)" }
)
foreach ($check in $policyManagerChecks) {
try {
if (Test-Path $check.Path) {
$prop = Get-ItemProperty -Path $check.Path -ErrorAction SilentlyContinue
if ($prop -and ($prop.PSObject.Properties.Name -contains $check.Name)) {
$value = $prop.($check.Name)
if ($null -ne $value) {
if ($value -eq 1) {
Write-Host " INFO: $($check.Desc) = 1 (aligned with AntiAI)" -ForegroundColor DarkGray
$msInfo++
}
else {
Write-Host " WARN: $($check.Desc) = $value (may conflict with AntiAI!)" -ForegroundColor Yellow
$msConflicts++
}
}
}
}
}
catch {
# Silently ignore if property doesn't exist or path is inaccessible
$null = $null
}
}
# Check for alternative Copilot/Explorer keys (conflict detection)
$additionalMSKeys = @(
@{ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"; Name = "NoCopilotButton"; ExpectedValue = 1; Desc = "Explorer Copilot Button" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "CopilotPageEnabled"; ExpectedValue = 0; Desc = "Edge Copilot Integration" }
)
foreach ($check in $additionalMSKeys) {
try {
if (Test-Path $check.Path) {
$prop = Get-ItemProperty -Path $check.Path -ErrorAction SilentlyContinue
if ($prop -and ($prop.PSObject.Properties.Name -contains $check.Name)) {
$value = $prop.($check.Name)
if ($null -ne $value) {
if ($value -eq $check.ExpectedValue) {
Write-Host " INFO: $($check.Desc) = $value (aligned with AntiAI)" -ForegroundColor DarkGray
$msInfo++
}
else {
Write-Host " WARN: $($check.Desc) = $value (may conflict with AntiAI, expected $($check.ExpectedValue))" -ForegroundColor Yellow
$msConflicts++
}
}
}
}
}
catch {
# Silently ignore if property doesn't exist or path is inaccessible
$null = $null
}
}
if ($msConflicts -eq 0 -and $msInfo -eq 0) {
Write-Host " No alternative MS policies detected (clean configuration)" -ForegroundColor DarkGray
}
elseif ($msConflicts -gt 0) {
Write-Host " CONFLICTS DETECTED: $msConflicts MS policy conflict(s) found!" -ForegroundColor Yellow
}
else {
Write-Host " $msInfo additional MS policy/policies aligned with AntiAI" -ForegroundColor DarkGray
}
Write-Host " NOTE: MS Policy Validation scans for conflicts with AntiAI configuration." -ForegroundColor DarkGray
Write-Host " Missing keys are OK - conflicts are reported as warnings." -ForegroundColor DarkGray
}
catch {
Write-Host " WARNING: MS Policy Validation encountered an error: $($_.Exception.Message)" -ForegroundColor Yellow
Write-Host " Continuing with self-check results only..." -ForegroundColor DarkGray
}
# Store MS validation results
$results.MSConflicts = $msConflicts
$results.MSAligned = $msInfo
# Calculate final results
$endTime = Get-Date
$duration = ($endTime - $startTime).TotalSeconds
# TotalPolicies = Passed + Failed (Warnings are informational only)
$totalPolicies = $results.Passed + $results.Failed
if ($totalPolicies -gt 0) {
$successRate = [math]::Round(($results.Passed / $totalPolicies) * 100, 1)
}
else {
$successRate = 0
}
# Derive overall status for programmatic use
if ($results.Failed -eq 0 -and $results.Passed -gt 0) {
# All checks passed (warnings are OK)
$overallStatus = "PASS"
}
elseif ($results.Passed -eq 0 -and $results.Failed -gt 0) {
# All checks failed - likely AntiAI module was never run
$overallStatus = "NOT_APPLIED"
}
elseif ($results.Failed -gt 0) {
# Some checks failed
$overallStatus = "FAIL"
}
else {
# Edge case: no checks run
$overallStatus = "NOT_APPLIED"
}
$results["OverallStatus"] = $overallStatus
$results["TotalPolicies"] = $totalPolicies
$results["TotalChecks"] = $totalPolicies
$results["FailedChecks"] = $results.Failed
$results["DurationSeconds"] = [math]::Round($duration, 2)
# Set exit code for programmatic use
# 0 = All checks passed, no MS conflicts
# 1 = Self-check failed (AntiAI policies not set correctly)
# 2 = Self-check passed but MS conflicts detected
$exitCode = 0
if ($results.Failed -gt 0) {
$exitCode = 1
}
elseif ($results.MSConflicts -gt 0) {
$exitCode = 2
}
$results["ExitCode"] = $exitCode
# Display summary
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " COMPLIANCE SUMMARY" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan
# Self-Check Results
Write-Host "Self-Check (AntiAI Policies):" -ForegroundColor Cyan
Write-Host " Total Policies: $totalPolicies" -ForegroundColor White
Write-Host " Passed: " -NoNewline
Write-Host "$($results.Passed)" -ForegroundColor Green
Write-Host " Failed: " -NoNewline
if ($results.Failed -eq 0) {
Write-Host "$($results.Failed)" -ForegroundColor Green
}
else {
Write-Host "$($results.Failed)" -ForegroundColor Red
}
Write-Host " Warnings: " -NoNewline
if ($results.Warnings -eq 0) {
Write-Host "$($results.Warnings)" -ForegroundColor Green
}
else {
Write-Host "$($results.Warnings)" -ForegroundColor Yellow
}
Write-Host " Success Rate: " -NoNewline
if ($successRate -eq 100) {
Write-Host "$successRate%" -ForegroundColor Green
}
elseif ($successRate -ge 80) {
Write-Host "$successRate%" -ForegroundColor Yellow
}
else {
Write-Host "$successRate%" -ForegroundColor Red
}
# MS Policy Validation Results
Write-Host "`nMS Policy Validation:" -ForegroundColor Cyan
Write-Host " Conflicts: " -NoNewline
if ($results.MSConflicts -eq 0) {
Write-Host "$($results.MSConflicts)" -ForegroundColor Green
}
else {
Write-Host "$($results.MSConflicts)" -ForegroundColor Yellow
}
Write-Host " Aligned: $($results.MSAligned)" -ForegroundColor White
Write-Host " Status: " -NoNewline
if ($results.MSConflicts -eq 0) {
Write-Host "NO CONFLICTS" -ForegroundColor Green
}
else {
Write-Host "CONFLICTS DETECTED" -ForegroundColor Yellow
}
Write-Host "`nExecution:" -ForegroundColor Cyan
Write-Host " Duration: $([math]::Round($duration, 2)) seconds" -ForegroundColor White
Write-Host "`nOverall Status: " -NoNewline
switch ($overallStatus) {
"PASS" {
if ($results.MSConflicts -eq 0) {
Write-Host "COMPLIANT - All checks passed, no conflicts (Exit Code: 0)" -ForegroundColor Green
}
else {
Write-Host "COMPLIANT - Registry OK, but MS conflicts detected (Exit Code: 2)" -ForegroundColor Yellow
}
}
"NOT_APPLIED" {
Write-Host "NOT APPLIED - AntiAI module has not been run yet (Exit Code: 1)" -ForegroundColor Yellow
}
default {
Write-Host "NON-COMPLIANT - Action required (Exit Code: 1)" -ForegroundColor Red
}
}
Write-Host "`n========================================`n" -ForegroundColor Cyan
# IMPORTANT DISCLAIMER
if ($overallStatus -eq "PASS") {
Write-Host " IMPORTANT: This check verifies REGISTRY COMPLIANCE ONLY." -ForegroundColor Yellow
Write-Host " It does NOT guarantee that AI features are functionally disabled." -ForegroundColor Yellow
Write-Host "" -ForegroundColor Yellow
Write-Host " Reasons why AI features might still work:" -ForegroundColor DarkGray
Write-Host " - Microsoft may use alternative/undocumented registry paths" -ForegroundColor DarkGray
Write-Host " - Cloud-based AI features bypass local policies" -ForegroundColor DarkGray
Write-Host " - Newer Windows builds may introduce new AI keys/features" -ForegroundColor DarkGray
Write-Host " - Apps may have hardcoded AI functionality" -ForegroundColor DarkGray
Write-Host "" -ForegroundColor Yellow
Write-Host " RECOMMENDATION: Manually test AI features after applying policies:" -ForegroundColor Yellow
Write-Host " - Open Notepad -> Check for AI/Copilot button" -ForegroundColor DarkGray
Write-Host " - Open Paint -> Check for Cocreator/Generative Fill" -ForegroundColor DarkGray
Write-Host " - Press Win+C -> Should NOT open Copilot" -ForegroundColor DarkGray
Write-Host " - Snipping Tool -> Check for AI OCR/Redact features" -ForegroundColor DarkGray
Write-Host "`n========================================`n" -ForegroundColor Cyan
}
# Return results object
return $results
}

View file

@ -0,0 +1,508 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Disables all Windows 11 AI features using official Microsoft policies.
.DESCRIPTION
Maximum AI deactivation module - Disables 8+ Windows 11 AI features:
DEACTIVATED AI FEATURES:
1. Generative AI Master Switch - Blocks ALL apps from using on-device AI models
2. Windows Recall - Screenshots everything (EXTREME privacy risk!) - Component removed
3. Windows Copilot - System AI assistant (chat, proactive suggestions)
4. Click to Do - Screenshot AI analysis with action suggestions
5. Paint Cocreator - Cloud-based text-to-image generation
6. Paint Generative Fill - AI-powered image editing
7. Paint Image Creator - DALL-E art generator
8. Notepad AI - Write, Summarize, Rewrite features (GPT)
9. Settings Agent - AI-powered Settings search
AUTOMATICALLY BLOCKED (by Master Switch):
- Photos Generative Erase / Background effects
- Clipchamp Auto Compose
- Snipping Tool AI-OCR / Quick Redact
- All future generative AI apps
RECALL ENTERPRISE PROTECTION (Maximum Compliance):
- App Deny List: Browser, Terminal, Password managers, RDP never captured
- URI Deny List: Banking, Email, Login pages never captured
- Storage Duration: Maximum 30 days retention
- Storage Space: Maximum 10 GB allocated
Uses only official Microsoft policies (WindowsAI CSP, AppPrivacy, Paint, Notepad).
No registry hacks, 100% MS Best Practice compliant.
WARNING: Recall component removal requires reboot!
.PARAMETER SkipBackup
Skip backup creation (NOT RECOMMENDED - use only for testing)
.PARAMETER DryRun
Preview actions without applying changes
.EXAMPLE
Invoke-AntiAI
Disables all AI features with automatic backup.
.EXAMPLE
Invoke-AntiAI -DryRun
Preview actions without applying changes.
.NOTES
Author: NoID Privacy
Version: 2.2.0
Requires: Windows 11 24H2 or later, Administrator privileges
Impact: All AI features completely disabled, reboot required
#>
function Invoke-AntiAI {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun,
[Parameter(Mandatory = $false)]
[switch]$SkipBackup
)
$startTime = Get-Date
Write-Host "" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " ANTI-AI MODULE v2.2.0" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Disables 13 AI features (32 policies):" -ForegroundColor White
Write-Host " - Generative AI Master Switch (blocks ALL AI models)" -ForegroundColor Gray
Write-Host " - Windows Recall + Export Block" -ForegroundColor Gray
Write-Host " - Windows Copilot (app + URI handlers + Edge sidebar)" -ForegroundColor Gray
Write-Host " - Click to Do, Paint AI (3), Notepad AI, Settings Agent" -ForegroundColor Gray
Write-Host " - Explorer AI Actions Menu" -ForegroundColor Gray
Write-Host ""
Write-Host "Uses 32 registry policies (+ URI handlers blocked separately)" -ForegroundColor Gray
Write-Host "REBOOT REQUIRED for Recall component removal" -ForegroundColor Yellow
Write-Host ""
if ($DryRun) {
Write-Host "[DRY RUN MODE - Preview only, no changes]" -ForegroundColor Yellow
Write-Host ""
}
# Initialize result tracking (PSCustomObject for Framework compatibility)
$result = [PSCustomObject]@{
Success = $false
TotalFeatures = 13 # 10 Original + 3 Advanced (RecallExport, URIHandlers, EdgeSidebar)
Applied = 0
Failed = 0
Warnings = @()
Errors = @()
RequiresReboot = $false
VerificationPassed = $null
StartTime = $startTime
EndTime = $null
Duration = $null
}
# BAVR Pattern: Backup, Apply (9 features), Verify, Complete
# No step counting during apply - clean sequential output
try {
# Core/Rollback.ps1 is loaded by Framework.ps1 - DO NOT load again here
# Loading it twice would reset $script:BackupBasePath and break the backup system!
# Initialize Session-based backup system
$moduleBackupPath = $null
# PHASE 1: BACKUP
Write-Host "[1/4] BACKUP - Creating restore point..." -ForegroundColor Cyan
if (-not $SkipBackup -and -not $DryRun) {
try {
Initialize-BackupSystem
$moduleBackupPath = Start-ModuleBackup -ModuleName "AntiAI"
Write-Host " Backup initialized: $moduleBackupPath" -ForegroundColor Green
Write-Host ""
}
catch {
Write-Host " WARNING: Backup failed - continuing without backup (RISKY!)" -ForegroundColor Yellow
Write-Host ""
$result.Warnings += "Backup initialization failed: $_"
}
}
else {
if ($DryRun) {
Write-Host " Skipped (DryRun mode)" -ForegroundColor Gray
}
else {
Write-Host " Skipped (SkipBackup flag)" -ForegroundColor Yellow
}
Write-Host ""
}
# Capture AntiAI pre-state for precise restore (32 policies)
if ($moduleBackupPath -and -not $DryRun) {
try {
$antiAIPreState = @()
$antiAIPreTargets = @(
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy"; Name = "LetAppsAccessSystemAIModels"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy"; Name = "LetAppsAccessGenerativeAI"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\systemAIModels"; Name = "Value"; Type = "String" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "AllowRecallEnablement"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableAIDataAnalysis"; Type = "DWord" },
@{ Path = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableAIDataAnalysis"; Type = "DWord" },
@{ Path = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableRecallDataProviders"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "SetDenyAppListForRecall"; Type = "MultiString" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "SetDenyUriListForRecall"; Type = "MultiString" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "SetMaximumStorageDurationForRecallSnapshots"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "SetMaximumStorageSpaceForRecallSnapshots"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "TurnOffWindowsCopilot"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot"; Name = "TurnOffWindowsCopilot"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot"; Name = "ShowCopilotButton"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer"; Name = "DisableWindowsCopilot"; Type = "DWord" },
@{ Path = "HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot"; Name = "TurnOffWindowsCopilot"; Type = "DWord" },
@{ Path = "HKCU:\Software\Policies\Microsoft\Windows\WindowsCopilot"; Name = "ShowCopilotButton"; Type = "DWord" },
@{ Path = "HKCU:\Software\Policies\Microsoft\Windows\WindowsAI"; Name = "SetCopilotHardwareKey"; Type = "String" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableClickToDo"; Type = "DWord" },
@{ Path = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableClickToDo"; Type = "DWord" },
@{ Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint"; Name = "DisableCocreator"; Type = "DWord" },
@{ Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint"; Name = "DisableGenerativeFill"; Type = "DWord" },
@{ Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint"; Name = "DisableImageCreator"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\WindowsNotepad"; Name = "DisableAIFeatures"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableSettingsAgent"; Type = "DWord" },
# NEW v2.2.0: Advanced Copilot Blocking
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "AllowRecallExport"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "EdgeSidebarEnabled"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "ShowHubsSidebar"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "HubsSidebarEnabled"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "CopilotPageContext"; Type = "DWord" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "CopilotCDPPageContext"; Type = "DWord" },
# NEW: File Explorer AI Actions Menu
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer"; Name = "HideAIActionsMenu"; Type = "DWord" }
)
foreach ($t in $antiAIPreTargets) {
$entry = [PSCustomObject]@{
Path = $t.Path
Name = $t.Name
Type = $t.Type
Exists = $false
Value = $null
}
try {
if (Test-Path $t.Path) {
$prop = Get-ItemProperty -Path $t.Path -Name $t.Name -ErrorAction SilentlyContinue
if ($null -ne $prop -and $prop.PSObject.Properties.Name -contains $t.Name) {
$entry.Exists = $true
$entry.Value = $prop.$($t.Name)
}
}
}
catch {
# Ignore read errors, entry remains Exists = $false
$null = $null
}
$antiAIPreState += $entry
}
$preStatePath = Join-Path $moduleBackupPath "AntiAI_PreState.json"
$antiAIPreState | ConvertTo-Json -Depth 5 | Out-File -FilePath $preStatePath -Encoding UTF8 -Force
Write-Log -Level DEBUG -Message "AntiAI pre-state snapshot saved: $preStatePath" -Module "AntiAI"
}
catch {
Write-Log -Level WARNING -Message "Failed to capture AntiAI pre-state snapshot: $_" -Module "AntiAI"
}
}
# PHASE 2: APPLY
Write-Host "[2/4] APPLY - Disabling AI features..." -ForegroundColor Cyan
Write-Host ""
# Feature 1: Generative AI Master Switch
Write-Host " Generative AI Master Switch..." -ForegroundColor White -NoNewline
$masterResult = Set-SystemAIModels -DryRun:$DryRun
if ($masterResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $masterResult.Errors
}
# Feature 2: Windows Recall (Core + Protection)
Write-Host " Windows Recall (component removal)..." -ForegroundColor White -NoNewline
$recallResult = Disable-Recall -DryRun:$DryRun
if ($recallResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
$result.RequiresReboot = $true
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $recallResult.Errors
}
Write-Host " Recall Enterprise Protection..." -ForegroundColor White -NoNewline
$protectionResult = Set-RecallProtection -DryRun:$DryRun
if ($protectionResult.Success) {
Write-Host " OK" -ForegroundColor Green
}
else {
Write-Host " WARNING" -ForegroundColor Yellow
$result.Warnings += "Recall protection incomplete but core disable succeeded"
}
# Feature 3: Windows Copilot
if ($moduleBackupPath -and -not $DryRun) {
# CRITICAL: Create JSON backup for Explorer Advanced HKLM (Protected Key)
# .reg import often fails for this key due to permissions/ownership
try {
$expPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
if (Test-Path $expPath) {
$expVal = Get-ItemProperty -Path $expPath -Name "ShowCopilotButton" -ErrorAction SilentlyContinue
if ($expVal) {
$expData = @{ "ShowCopilotButton" = $expVal.ShowCopilotButton }
$expJson = $expData | ConvertTo-Json
Register-Backup -Type "AntiAI" -Data $expJson -Name "Explorer_Advanced_Device_JSON" | Out-Null
}
}
} catch {
Write-Host " WARNING: Failed to create JSON backup for Explorer Advanced: $_" -ForegroundColor Yellow
}
}
Write-Host " Windows Copilot..." -ForegroundColor White -NoNewline
$copilotResult = Disable-Copilot -DryRun:$DryRun
if ($copilotResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $copilotResult.Errors
}
# Feature 4: Click to Do
Write-Host " Click to Do..." -ForegroundColor White -NoNewline
$clickResult = Disable-ClickToDo -DryRun:$DryRun
if ($clickResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $clickResult.Errors
}
# Feature 5-7: Paint AI (3 features)
Write-Host " Paint AI (Cocreator, Fill, Creator)..." -ForegroundColor White -NoNewline
$paintResult = Disable-PaintAI -DryRun:$DryRun
if ($paintResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied += 3
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed += 3
$result.Errors += $paintResult.Errors
}
# Feature 8: Notepad AI
Write-Host " Notepad AI..." -ForegroundColor White -NoNewline
$notepadResult = Disable-NotepadAI -DryRun:$DryRun
if ($notepadResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $notepadResult.Errors
}
# Feature 9: Settings Agent
Write-Host " Settings Agent..." -ForegroundColor White -NoNewline
$settingsResult = Disable-SettingsAgent -DryRun:$DryRun
if ($settingsResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $settingsResult.Errors
}
# Feature 10: Explorer AI Actions Menu
Write-Host " Explorer AI Actions..." -ForegroundColor White -NoNewline
$explorerResult = Disable-ExplorerAI -DryRun:$DryRun
if ($explorerResult.Success) {
Write-Host " OK" -ForegroundColor Green
$result.Applied++
}
else {
Write-Host " FAILED" -ForegroundColor Red
$result.Failed++
$result.Errors += $explorerResult.Errors
}
# ============================================================================
# ADVANCED COPILOT BLOCKING (NEW v2.2.0)
# ============================================================================
Write-Host ""
Write-Host " [Advanced Copilot Blocks]" -ForegroundColor Cyan
# Feature 11-15: Advanced Copilot Blocking
Write-Host " Advanced Copilot Blocks..." -ForegroundColor White -NoNewline
$advancedResult = Disable-CopilotAdvanced -DryRun:$DryRun
if ($advancedResult.Success) {
Write-Host " OK" -ForegroundColor Green
# Count actual features applied (not layers)
$advancedFeaturesApplied = 0
if ($advancedResult.RecallExportBlocked) {
Write-Host " - Recall Export blocked" -ForegroundColor Gray
$advancedFeaturesApplied++
}
if ($advancedResult.URIHandlersBlocked) {
Write-Host " - URI handlers (ms-copilot:) blocked" -ForegroundColor Gray
$advancedFeaturesApplied++
}
if ($advancedResult.EdgeSidebarDisabled) {
Write-Host " - Edge Copilot sidebar disabled" -ForegroundColor Gray
$advancedFeaturesApplied++
}
$result.Applied += $advancedFeaturesApplied
}
else {
Write-Host " PARTIAL" -ForegroundColor Yellow
$result.Warnings += "Some advanced Copilot blocks may have failed"
if ($advancedResult.Errors.Count -gt 0) {
$result.Errors += $advancedResult.Errors
}
}
Write-Host ""
# Register backup in session manifest
# Note: Only Explorer Advanced JSON backup + PreState snapshot are created
# PreState snapshot covers all 32 policies precisely
if ($moduleBackupPath) {
Complete-ModuleBackup -ItemsBackedUp 2 -Status "Success"
}
# PHASE 3: VERIFY
Write-Host "[3/4] VERIFY - Checking compliance..." -ForegroundColor Cyan
if (-not $DryRun -and $result.Failed -eq 0) {
try {
$complianceResult = Test-AntiAICompliance
if ($complianceResult.OverallStatus -eq "PASS") {
Write-Host " All $($complianceResult.TotalChecks) compliance checks passed" -ForegroundColor Green
$result.VerificationPassed = $true
}
else {
Write-Host " WARNING: $($complianceResult.FailedChecks)/$($complianceResult.TotalChecks) checks failed" -ForegroundColor Yellow
$result.VerificationPassed = $false
$result.Warnings += "Some compliance checks failed - policies may not be fully effective"
}
}
catch {
Write-Host " WARNING: Verification failed - $($_.Exception.Message)" -ForegroundColor Yellow
$result.Warnings += "Compliance verification skipped due to error"
$result.VerificationPassed = $null
}
}
else {
if ($DryRun) {
Write-Host " Skipped (DryRun mode)" -ForegroundColor Gray
}
else {
Write-Host " Skipped (errors occurred)" -ForegroundColor Yellow
}
$result.VerificationPassed = $null
}
Write-Host ""
# Calculate final status
$result.Success = ($result.Failed -eq 0)
$result.EndTime = Get-Date
$result.Duration = ($result.EndTime - $result.StartTime).TotalSeconds
# PHASE 4: COMPLETE
Write-Host "[4/4] COMPLETE - AI hardening finished!" -ForegroundColor Green
Write-Host ""
Write-Host "Status: " -NoNewline
if ($result.Success) {
Write-Host "SUCCESS - All AI features disabled!" -ForegroundColor Green
}
else {
Write-Host "COMPLETED WITH ERRORS" -ForegroundColor Yellow
}
Write-Host "Features: $($result.Applied)/$($result.TotalFeatures) disabled" -ForegroundColor $(if ($result.Failed -eq 0) { 'Green' } else { 'Yellow' })
Write-Host "Errors: $($result.Failed)" -ForegroundColor $(if ($result.Failed -eq 0) { 'Green' } else { 'Red' })
Write-Host "Warnings: $($result.Warnings.Count)" -ForegroundColor $(if ($result.Warnings.Count -eq 0) { 'Green' } else { 'Yellow' })
if ($null -ne $result.VerificationPassed) {
Write-Host "Verification: " -NoNewline
if ($result.VerificationPassed) {
Write-Host "PASSED - All policies verified" -ForegroundColor Green
}
else {
Write-Host "FAILED - Some policies not verified" -ForegroundColor Yellow
}
}
Write-Host "Duration: $([math]::Round($result.Duration, 2)) seconds" -ForegroundColor Cyan
if ($moduleBackupPath) {
Write-Host "Backup: $moduleBackupPath" -ForegroundColor Cyan
Write-Host "Items Backed: 2 items (PreState snapshot + Explorer JSON)" -ForegroundColor Cyan
}
elseif (-not $SkipBackup -and -not $DryRun) {
Write-Host "Backup: FAILED" -ForegroundColor Red
}
else {
Write-Host "Backup: SKIPPED" -ForegroundColor Yellow
}
if ($result.RequiresReboot) {
Write-Host "`nREBOOT REQUIRED: " -NoNewline -ForegroundColor Red
Write-Host "Recall component removal needs system restart!" -ForegroundColor Yellow
}
if ($result.Errors.Count -gt 0) {
Write-Host ""
Write-Host "Errors:" -ForegroundColor Red
foreach ($err in $result.Errors) {
Write-Host " - $err" -ForegroundColor Red
}
}
Write-Host ""
# GUI parsing marker for settings count (32 registry policies)
Write-Log -Level SUCCESS -Message "Applied 32 settings" -Module "AntiAI"
# Return result object as PSCustomObject (Framework expects this type)
return [PSCustomObject]$result
}
catch {
$result.Success = $false
$result.Errors += "Critical error: $($_.Exception.Message)"
Write-Error "AntiAI module failed: $($_.Exception.Message)"
return [PSCustomObject]$result
}
}

View file

@ -0,0 +1,38 @@
{
"DoHPolicy": {
"Mode": "REQUIRE",
"Description": "DNS-over-HTTPS enforcement mode",
"Options": {
"REQUIRE": {
"Value": 3,
"Description": "REQUIRE DoH (no unencrypted fallback) - Maximum security",
"BestFor": "Home users, single-network systems, maximum privacy",
"Warning": "May cause connectivity issues in corporate networks, captive portals, or mobile hotspots"
},
"ALLOW": {
"Value": 2,
"Description": "ALLOW DoH (fallback to UDP if DoH fails) - Balanced",
"BestFor": "VPN users, mobile devices, multi-network systems, enterprise environments",
"Warning": "Less secure - DNS queries may fall back to unencrypted UDP"
},
"PROHIBIT": {
"Value": 1,
"Description": "PROHIBIT DoH (disable encrypted DNS) - Not recommended",
"BestFor": "Legacy systems, specific enterprise requirements only",
"Warning": "All DNS queries will be unencrypted"
}
},
"Recommendation": "Use REQUIRE for home networks, ALLOW for VPN/mobile/enterprise"
},
"AllowFallbackToUdp": {
"REQUIRE": false,
"ALLOW": true,
"PROHIBIT": true
},
"Notes": [
"REQUIRE mode (default): Best privacy, but may break in corporate/captive portal networks or with VPNs",
"ALLOW mode: Good balance for VPN users, mobile users and enterprise environments",
"Users with VPNs or on multiple networks should choose ALLOW mode",
"PROHIBIT mode is NOT recommended - only for specific legacy requirements"
]
}

View file

@ -0,0 +1,140 @@
{
"version": "1.0.0",
"providers": {
"cloudflare": {
"name": "Cloudflare",
"description": "Fastest global DNS resolver with strong privacy",
"ipv4": {
"primary": "1.1.1.1",
"secondary": "1.0.0.1"
},
"ipv6": {
"primary": "2606:4700:4700::1111",
"secondary": "2606:4700:4700::1001"
},
"doh": {
"template": "https://cloudflare-dns.com/dns-query",
"supported": true
},
"ratings": {
"speed": 5,
"privacy": 4,
"security": 4,
"filtering": 2
},
"features": [
"Minimal logging (25h retention, anonymized IPs)",
"DNSSEC validation (server-side)",
"Fastest anycast network globally (150+ locations)",
"KPMG-audited privacy practices",
"Malware blocking available (1.1.1.2 variant)",
"Adult content blocking available (1.1.1.3 variant)"
],
"jurisdiction": "USA (GDPR-compliant, strong privacy commitments)",
"best_for": "Maximum speed with audited privacy",
"documentation": "https://developers.cloudflare.com/1.1.1.1/"
},
"quad9": {
"name": "Quad9",
"description": "Non-profit DNS with Swiss privacy and threat blocking",
"ipv4": {
"primary": "9.9.9.9",
"secondary": "149.112.112.112"
},
"ipv6": {
"primary": "2620:fe::fe",
"secondary": "2620:fe::9"
},
"doh": {
"template": "https://dns.quad9.net/dns-query",
"supported": true
},
"ratings": {
"speed": 4,
"privacy": 5,
"security": 5,
"filtering": 4
},
"features": [
"Zero logging (no IP or query data stored)",
"Swiss Data Protection Act enforcement",
"Threat intelligence from 20+ sources (97% blocking rate)",
"DNSSEC validation (server-side)",
"Non-profit, no data monetization",
"150+ locations in 90 countries"
],
"jurisdiction": "Switzerland (Zuerich) - Strongest privacy laws",
"best_for": "Maximum privacy and security under Swiss law",
"documentation": "https://quad9.net/"
},
"adguard": {
"name": "AdGuard DNS",
"description": "EU-based DNS with comprehensive ad and tracker blocking",
"ipv4": {
"primary": "94.140.14.14",
"secondary": "94.140.15.15"
},
"ipv6": {
"primary": "2a10:50c0::ad1:ff",
"secondary": "2a10:50c0::ad2:ff"
},
"doh": {
"template": "https://dns.adguard-dns.com/dns-query",
"supported": true
},
"ratings": {
"speed": 4,
"privacy": 4,
"security": 4,
"filtering": 5
},
"features": [
"Comprehensive ad and tracker blocking",
"Analytics and telemetry blocking",
"DNSSEC validation (server-side)",
"GDPR compliant (EU jurisdiction)",
"No-logging policy (not independently audited)",
"Family-friendly filtering options available"
],
"jurisdiction": "Cyprus (Limassol) - EU, GDPR compliance",
"best_for": "Maximum ad/tracker blocking with EU privacy",
"documentation": "https://adguard-dns.io/"
}
},
"default_provider": "quad9",
"rating_descriptions": {
"speed": {
"5": "Exceptional - Fastest global performance",
"4": "Excellent - Very fast response times",
"3": "Good - Adequate performance",
"2": "Fair - Slower than average",
"1": "Poor - Slow response times"
},
"privacy": {
"5": "Exceptional - Zero logging, strong jurisdiction, independently audited",
"4": "Excellent - Minimal logging, good privacy policies",
"3": "Good - Some logging, reasonable policies",
"2": "Fair - Extensive logging",
"1": "Poor - Privacy concerns"
},
"security": {
"5": "Exceptional - Multiple threat feeds, DNSSEC, automatic blocking",
"4": "Excellent - DNSSEC validation, basic threat protection",
"3": "Good - DNSSEC only",
"2": "Fair - Limited security features",
"1": "Poor - No security features"
},
"filtering": {
"5": "Exceptional - Comprehensive ad, tracker, and malware blocking",
"4": "Excellent - Malware and phishing blocking",
"3": "Good - Basic filtering available",
"2": "Fair - Limited filtering options",
"1": "Poor - No filtering"
}
},
"notes": {
"dnssec": "All providers perform server-side DNSSEC validation. No client-side NRPT configuration required.",
"doh": "DNS over HTTPS (DoH) encrypts DNS queries for privacy. Fallback to unencrypted DNS is disabled for security.",
"ipv6": "IPv6 addresses are always configured alongside IPv4. Windows will use IPv6 when available."
}
}

38
Modules/DNS/DNS.psd1 Normal file
View file

@ -0,0 +1,38 @@
@{
# Module manifest for DNS module
RootModule = 'DNS.psm1'
ModuleVersion = '2.2.0'
GUID = 'a8f7b3c9-4e5d-4a2b-9c1d-8f3e5a7b9c2d'
Author = 'NexusOne23'
CompanyName = 'Open Source Project'
Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.'
Description = 'Secure DNS configuration module with DoH support for Cloudflare, Quad9, and AdGuard DNS providers'
PowerShellVersion = '5.1'
# Functions to export from this module
FunctionsToExport = @(
'Invoke-DNSConfiguration',
'Get-DNSStatus',
'Restore-DNSSettings'
)
# Cmdlets to export from this module
CmdletsToExport = @()
# Variables to export from this module
VariablesToExport = @()
# Aliases to export from this module
AliasesToExport = @()
PrivateData = @{
PSData = @{
Tags = @('DNS', 'DoH', 'Security', 'Privacy', 'Cloudflare', 'Quad9', 'AdGuard')
LicenseUri = ''
ProjectUri = ''
ReleaseNotes = 'Initial release with DoH support for 3 major DNS providers'
}
}
}

43
Modules/DNS/DNS.psm1 Normal file
View file

@ -0,0 +1,43 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
DNS Configuration Module for NoID Privacy
.DESCRIPTION
Provides secure DNS configuration with DNS over HTTPS (DoH) support.
Supports Cloudflare, Quad9, and AdGuard DNS providers with automatic
backup and restore capabilities.
.NOTES
Author: NoID Privacy
Version: 2.2.0
Requires: PowerShell 5.1+, Administrator privileges
#>
# Module-level variables
$script:ModuleName = "DNS"
$script:ModuleRoot = $PSScriptRoot
$PrivatePath = "$PSScriptRoot\Private"
# Get module functions
$Private = @(Get-ChildItem -Path $PrivatePath -Filter "*.ps1" -ErrorAction SilentlyContinue)
$Public = @(Get-ChildItem -Path "$PSScriptRoot\Public" -Filter "*.ps1" -ErrorAction SilentlyContinue)
# Dot source the functions
foreach ($import in @($Private + $Public)) {
try {
. $import.FullName
}
catch {
Write-Host "ERROR: Failed to import function $($import.FullName): $_" -ForegroundColor Red
}
}
# Export public functions
Export-ModuleMember -Function $Public.BaseName
# Alias for naming consistency (non-breaking change)
New-Alias -Name 'Invoke-DNS' -Value 'Invoke-DNSConfiguration' -Force
Export-ModuleMember -Alias 'Invoke-DNS'

View file

@ -0,0 +1,300 @@
function Backup-DNSSettings {
<#
.SYNOPSIS
Backup current DNS settings for all physical network adapters
.DESCRIPTION
Creates a comprehensive backup of DNS configuration including:
- Current DNS server addresses (IPv4 and IPv6)
- DHCP status (was DNS obtained from DHCP?)
- DoH configuration
- Adapter interface information
Backup is stored using the framework's rollback system.
.PARAMETER DryRun
Show what would be backed up without actually creating backup
.EXAMPLE
Backup-DNSSettings
Creates backup of current DNS settings
.OUTPUTS
System.String - Path to backup file or $null if failed
.NOTES
DHCP awareness is critical for correct rollback behavior
#>
[CmdletBinding()]
param(
[Parameter()]
[switch]$DryRun
)
try {
Write-Log -Level INFO -Message "Backing up DNS settings..." -Module $script:ModuleName
# Get all physical adapters
$adapters = @(Get-PhysicalAdapters) # Force array
if ($adapters.Count -eq 0) {
Write-Log -Level WARNING -Message "No physical adapters found to backup" -Module $script:ModuleName
return $null
}
Write-Log -Level DEBUG -Message "Found $($adapters.Count) adapter(s) to backup" -Module $script:ModuleName
# Get netsh global DoH state
$netshGlobalDoh = $null
try {
$netshResult = netsh dnsclient show global 2>&1 | Out-String
if ($netshResult -match "DoH\s*:\s*(\w+)") {
$netshGlobalDoh = $matches[1]
Write-Log -Level DEBUG -Message "netsh global DoH state: $netshGlobalDoh" -Module $script:ModuleName
}
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve netsh global DoH state: $_" -Module $script:ModuleName
}
# Get all netsh DoH encryption entries
$netshDohEntries = @()
try {
$netshEncryption = netsh dnsclient show encryption 2>&1 | Out-String
# Parse netsh output for DoH servers
# Format: "Server: X.X.X.X | Template: https://... | Auto-upgrade: yes | UDP fallback: no"
$lines = $netshEncryption -split "`n"
foreach ($line in $lines) {
if ($line -match "Server:\s*(\S+)") {
$server = $matches[1]
$template = $null
$autoupgrade = $null
$udpfallback = $null
if ($netshEncryption -match "Server:\s*$([regex]::Escape($server)).*?Template:\s*(\S+)") {
$template = $matches[1]
}
if ($netshEncryption -match "Server:\s*$([regex]::Escape($server)).*?Auto-upgrade:\s*(\w+)") {
$autoupgrade = $matches[1]
}
if ($netshEncryption -match "Server:\s*$([regex]::Escape($server)).*?UDP fallback:\s*(\w+)") {
$udpfallback = $matches[1]
}
if ($template) {
$netshDohEntries += @{
Server = $server
Template = $template
AutoUpgrade = $autoupgrade
UdpFallback = $udpfallback
}
Write-Log -Level DEBUG -Message "Found netsh DoH entry: $server" -Module $script:ModuleName
}
}
}
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve netsh DoH entries: $_" -Module $script:ModuleName
}
$dohEntries = @()
try {
$allDoh = Get-DnsClientDohServerAddress -ErrorAction SilentlyContinue
if ($allDoh) {
foreach ($entry in $allDoh) {
if ($entry.ServerAddress -and $entry.DohTemplate) {
$dohEntries += @{
ServerAddress = $entry.ServerAddress
DohTemplate = $entry.DohTemplate
AllowFallbackToUdp = $entry.AllowFallbackToUdp
AutoUpgrade = $entry.AutoUpgrade
}
}
}
Write-Log -Level DEBUG -Message "Backed up $($dohEntries.Count) DoH entries from Get-DnsClientDohServerAddress" -Module $script:ModuleName
}
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve DoH entries via Get-DnsClientDohServerAddress: $_" -Module $script:ModuleName
}
# Get DoH Policy Registry settings
$dohPolicySettings = @{}
try {
$dnsClientPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient"
if (Test-Path $dnsClientPath) {
$dohPolicy = (Get-ItemProperty -Path $dnsClientPath -Name "DoHPolicy" -ErrorAction SilentlyContinue).DoHPolicy
if ($null -ne $dohPolicy) {
$dohPolicySettings['DoHPolicy'] = $dohPolicy
}
}
$dnsParamsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
if (Test-Path $dnsParamsPath) {
$enableAutoDoh = (Get-ItemProperty -Path $dnsParamsPath -Name "EnableAutoDoh" -ErrorAction SilentlyContinue).EnableAutoDoh
if ($null -ne $enableAutoDoh) {
$dohPolicySettings['EnableAutoDoh'] = $enableAutoDoh
}
# NOTE: Global DohFlags no longer used (we use per-adapter DohFlags instead)
# Kept for backward compatibility with old backups, but not written anymore
}
Write-Log -Level DEBUG -Message "Backed up DoH policy settings: $($dohPolicySettings.Count) keys" -Module $script:ModuleName
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve DoH policy settings: $_" -Module $script:ModuleName
}
$backupData = @{
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
ComputerName = $env:COMPUTERNAME
NetshGlobalDoh = $netshGlobalDoh
NetshDohEntries = $netshDohEntries
DohEntries = $dohEntries
DohPolicySettings = $dohPolicySettings
Adapters = @()
}
foreach ($adapter in $adapters) {
Write-Log -Level DEBUG -Message "Backing up adapter: $($adapter.Name)" -Module $script:ModuleName
# Get current DNS configuration
$dnsConfig = Get-DnsClientServerAddress -InterfaceIndex $adapter.InterfaceIndex -ErrorAction SilentlyContinue
# Collect DNS addresses first
$ipv4Addresses = @()
$ipv6Addresses = @()
foreach ($config in $dnsConfig) {
if ($config.AddressFamily -eq 2) { # IPv4
if ($config.ServerAddresses.Count -gt 0) {
$ipv4Addresses = $config.ServerAddresses
}
}
elseif ($config.AddressFamily -eq 23) { # IPv6
if ($config.ServerAddresses.Count -gt 0 -and
$config.ServerAddresses -notcontains "fec0:0:0:ffff::1" -and
$config.ServerAddresses -notcontains "fec0:0:0:ffff::2" -and
$config.ServerAddresses -notcontains "fec0:0:0:ffff::3") {
# Only if not DHCP placeholder addresses
$ipv6Addresses = $config.ServerAddresses
}
}
}
# CRITICAL FIX: Determine DHCP status AFTER collecting all addresses
# DNS is from DHCP only if NO addresses are configured (neither IPv4 nor IPv6)
$isDHCP = ($ipv4Addresses.Count -eq 0) -and ($ipv6Addresses.Count -eq 0)
# Get DoH configuration for this adapter's DNS servers
$dohConfig = @()
try {
$allDohServers = Get-DnsClientDohServerAddress -ErrorAction SilentlyContinue
if ($allDohServers) {
foreach ($dohServer in $allDohServers) {
if ($ipv4Addresses -contains $dohServer.ServerAddress -or
$ipv6Addresses -contains $dohServer.ServerAddress) {
$dohConfig += @{
ServerAddress = $dohServer.ServerAddress
DohTemplate = $dohServer.DohTemplate
AllowFallbackToUdp = $dohServer.AllowFallbackToUdp
AutoUpgrade = $dohServer.AutoUpgrade
}
}
}
}
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve DoH configuration: $_" -Module $script:ModuleName
}
# Get DohFlags registry settings for this adapter
$dohFlags = @{}
try {
$interfaceGuid = $adapter.InterfaceGuid
$dohFlagsBasePath = "HKLM:\System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\$interfaceGuid\DohInterfaceSettings\Doh"
if (Test-Path $dohFlagsBasePath) {
# Check each DNS server IP
$allDnsIPs = $ipv4Addresses + $ipv6Addresses
foreach ($dnsIP in $allDnsIPs) {
$dohFlagsPath = "$dohFlagsBasePath\$dnsIP"
if (Test-Path $dohFlagsPath) {
$flagValue = (Get-ItemProperty -Path $dohFlagsPath -Name "DohFlags" -ErrorAction SilentlyContinue).DohFlags
if ($null -ne $flagValue) {
$dohFlags[$dnsIP] = $flagValue
Write-Log -Level DEBUG -Message "Found DohFlags for $dnsIP : $flagValue" -Module $script:ModuleName
}
}
}
}
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve DohFlags: $_" -Module $script:ModuleName
}
# Get DHCP DNS Override setting for this adapter
$dhcpOverrideDisabled = $null
try {
$dnsClient = Get-DnsClient -InterfaceIndex $adapter.InterfaceIndex -ErrorAction SilentlyContinue
if ($dnsClient) {
$dhcpOverrideDisabled = (-not $dnsClient.RegisterThisConnectionsAddress)
Write-Log -Level DEBUG -Message "DHCP Override disabled: $dhcpOverrideDisabled" -Module $script:ModuleName
}
}
catch {
Write-Log -Level DEBUG -Message "Could not retrieve DHCP override setting: $_" -Module $script:ModuleName
}
$adapterBackup = @{
InterfaceIndex = $adapter.InterfaceIndex
InterfaceAlias = $adapter.Name
InterfaceDescription = $adapter.InterfaceDescription
InterfaceGuid = $adapter.InterfaceGuid
Status = $adapter.Status
IsDHCP = $isDHCP
IPv4Addresses = $ipv4Addresses
IPv6Addresses = $ipv6Addresses
DoHConfiguration = $dohConfig
DohFlags = $dohFlags
DhcpOverrideDisabled = $dhcpOverrideDisabled
}
$backupData.Adapters += $adapterBackup
$statusText = if ($isDHCP) { "DHCP" } else { "Static" }
$dnsText = if ($isDHCP) { "from DHCP" } else { "$($ipv4Addresses.Count) IPv4, $($ipv6Addresses.Count) IPv6" }
Write-Log -Level INFO -Message " - $($adapter.Name): $statusText ($dnsText)" -Module $script:ModuleName
}
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would backup DNS settings for $($adapters.Count) adapter(s)" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] - netsh global DoH state" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] - netsh DoH encryption entries" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] - DoH Policy Registry (DoHPolicy, EnableAutoDoh, DohFlags)" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] - Per-adapter DohFlags registry" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] - DHCP DNS override settings" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] - DNS server addresses" -Module $script:ModuleName
return "DRYRUN"
}
# Convert to JSON and save using rollback system
$backupJson = $backupData | ConvertTo-Json -Depth 10
$backupFile = Register-Backup -Type "DNS" -Data $backupJson
if ($backupFile) {
Write-Log -Level SUCCESS -Message "DNS settings backed up successfully" -Module $script:ModuleName
return $backupFile
}
else {
Write-Log -Level ERROR -Message "Failed to register DNS backup" -Module $script:ModuleName
return $null
}
}
catch {
Write-ErrorLog -Message "Failed to backup DNS settings" -Module $script:ModuleName -ErrorRecord $_
return $null
}
}

View file

@ -0,0 +1,65 @@
function Disable-DHCPDnsOverride {
<#
.SYNOPSIS
Prevent DHCP from overriding manually configured DNS servers
.DESCRIPTION
Sets adapter to NOT register its DNS address and ignore DHCP-provided DNS servers.
This ensures your static DNS configuration (e.g., Cloudflare with DoH) cannot be overridden.
.PARAMETER InterfaceIndex
Network adapter interface index
.PARAMETER DryRun
Show what would be configured without applying changes
.EXAMPLE
Disable-DHCPDnsOverride -InterfaceIndex 12
.NOTES
Uses Set-DnsClient cmdlet (PowerShell Best Practice)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[int]$InterfaceIndex,
[Parameter()]
[switch]$DryRun
)
try {
$adapter = Get-NetAdapter -InterfaceIndex $InterfaceIndex -ErrorAction Stop
$adapterName = $adapter.Name
Write-Log -Level DEBUG -Message "Preventing DHCP DNS override on adapter: $adapterName" -Module $script:ModuleName
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would disable DHCP DNS override on $adapterName" -Module $script:ModuleName
return $true
}
# Set RegisterThisConnectionsAddress = $false to prevent DHCP from overriding DNS
Set-DnsClient -InterfaceIndex $InterfaceIndex `
-RegisterThisConnectionsAddress $false `
-ErrorAction Stop
Write-Log -Level SUCCESS -Message "DHCP DNS override disabled on $adapterName" -Module $script:ModuleName
# Verify
$dnsClient = Get-DnsClient -InterfaceIndex $InterfaceIndex -ErrorAction SilentlyContinue
if ($dnsClient.RegisterThisConnectionsAddress -eq $false) {
Write-Log -Level DEBUG -Message "Verification passed: DHCP cannot override DNS" -Module $script:ModuleName
return $true
}
else {
Write-Log -Level WARNING -Message "Verification failed: DHCP override not disabled" -Module $script:ModuleName
return $false
}
}
catch {
Write-ErrorLog -Message "Failed to disable DHCP DNS override on adapter $InterfaceIndex" -Module $script:ModuleName -ErrorRecord $_
return $false
}
}

View file

@ -0,0 +1,119 @@
function Enable-DoH {
<#
.SYNOPSIS
Enable DNS over HTTPS (DoH) for specified DNS servers
.DESCRIPTION
Configures DNS over HTTPS encryption for privacy and security.
Uses Microsoft Best Practice: Add-DnsClientDohServerAddress cmdlet.
CRITICAL SECURITY SETTINGS:
- AllowFallbackToUdp = $False (prevents fallback to unencrypted DNS)
- AutoUpgrade = $True (automatically uses DoH when available)
.PARAMETER ServerAddress
DNS server IP address (IPv4 or IPv6)
.PARAMETER DohTemplate
HTTPS URL template for DoH queries
.PARAMETER DryRun
Show what would be configured without applying changes
.EXAMPLE
Enable-DoH -ServerAddress "1.1.1.1" -DohTemplate "https://cloudflare-dns.com/dns-query"
.OUTPUTS
System.Boolean - $true if successful, $false otherwise
.NOTES
Requires Windows 11 or Windows Server 2022+ for native DoH support
Fallback to unencrypted DNS is DISABLED for security
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ServerAddress,
[Parameter(Mandatory = $true)]
[string]$DohTemplate,
[Parameter()]
[switch]$DryRun
)
try {
Write-Log -Level DEBUG -Message "Configuring DoH for $ServerAddress" -Module $script:ModuleName
# Determine AllowFallbackToUdp based on DoH mode
$allowFallback = if ($script:DoHMode -eq "ALLOW") { $True } else { $False }
$fallbackText = if ($allowFallback) { "True (fallback allowed)" } else { "False (no fallback)" }
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would enable DoH for $ServerAddress" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] Template: $DohTemplate" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] AllowFallbackToUdp: $fallbackText" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] AutoUpgrade: True" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] Method: PowerShell cmdlet + netsh (dual registration)" -Module $script:ModuleName
return $true
}
# Register DoH for this DNS server (overwrites existing if present)
Write-Log -Level DEBUG -Message "Registering DoH server: $ServerAddress" -Module "DNS"
# METHOD 1: PowerShell cmdlet (modern API)
try {
Add-DnsClientDohServerAddress -ServerAddress $ServerAddress `
-DohTemplate $DohTemplate `
-AllowFallbackToUdp $allowFallback `
-AutoUpgrade $True `
-ErrorAction Stop
Write-Log -Level DEBUG -Message "PowerShell cmdlet registration successful" -Module "DNS"
}
catch {
Write-Log -Level DEBUG -Message "PowerShell cmdlet failed (expected on some builds): $_" -Module "DNS"
}
# METHOD 2: netsh (critical for actual enforcement - what v1.0 uses!)
$udpFallbackMode = if ($allowFallback) { "yes" } else { "no" }
try {
$netshResult = netsh dnsclient add encryption `
server=$ServerAddress `
dohtemplate=$DohTemplate `
autoupgrade=yes `
udpfallback=$udpFallbackMode 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log -Level DEBUG -Message "netsh registration successful for $ServerAddress" -Module "DNS"
}
else {
Write-Log -Level DEBUG -Message "netsh returned exit code $LASTEXITCODE : $netshResult" -Module "DNS"
}
}
catch {
Write-Log -Level DEBUG -Message "netsh registration failed: $_" -Module "DNS"
}
Write-Log -Level DEBUG -Message "Successfully registered DoH for $ServerAddress" -Module "DNS"
Write-Log -Level SUCCESS -Message "DoH enabled for $ServerAddress" -Module $script:ModuleName
Write-Log -Level DEBUG -Message " Template: $DohTemplate" -Module $script:ModuleName
Write-Log -Level DEBUG -Message " Fallback: $(if ($allowFallback) { 'ENABLED (ALLOW mode)' } else { 'DISABLED (REQUIRE mode)' })" -Module $script:ModuleName
return $true
}
catch {
# DoH might not be supported on older Windows versions
$errorMessage = $_.Exception.Message
if ($errorMessage -like "*not recognized*" -or $errorMessage -like "*does not exist*") {
Write-Log -Level WARNING -Message "DoH not supported on this Windows version (requires Windows 11 or Server 2022+)" -Module $script:ModuleName
Write-Log -Level INFO -Message "DNS will work without encryption - consider upgrading Windows for DoH support" -Module $script:ModuleName
return $false
}
Write-ErrorLog -Message "Failed to enable DoH for $ServerAddress" -Module $script:ModuleName -ErrorRecord $_
return $false
}
}

View file

@ -0,0 +1,150 @@
function Get-PhysicalAdapters {
<#
.SYNOPSIS
Get physical network adapters (LAN/WLAN) excluding virtual adapters
.DESCRIPTION
Retrieves physical network adapters using multi-layer filtering to exclude:
- Virtual adapters (Hyper-V, VMware, VirtualBox)
- VPN adapters (TAP, OpenVPN, WireGuard, Cisco, etc.)
- Tunnel adapters (Teredo, 6to4, ISATAP)
- Loopback adapters
Uses Microsoft Best Practice: Get-NetAdapter with -Physical switch
and additional filtering based on InterfaceDescription patterns.
.PARAMETER IncludeDisabled
Include disabled adapters in results
.EXAMPLE
Get-PhysicalAdapters
Returns all active physical network adapters
.EXAMPLE
Get-PhysicalAdapters -IncludeDisabled
Returns all physical adapters including disabled ones
.OUTPUTS
Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT_NetAdapter
.NOTES
Uses Get-NetAdapter -Physical for primary filtering (Microsoft Best Practice)
Additional filtering excludes known virtual adapter patterns
#>
[CmdletBinding()]
param(
[Parameter()]
[switch]$IncludeDisabled
)
try {
Write-Log -Level DEBUG -Message "Retrieving physical network adapters..." -Module $script:ModuleName
# Layer 1: Get physical adapters only (Microsoft Best Practice)
$adapters = Get-NetAdapter -Physical -ErrorAction Stop
# Layer 2: Filter by status if required
if (-not $IncludeDisabled) {
# Allow 'Up' (Connected) and 'Disconnected' (Cable unplugged)
# Only filter out 'Disabled' (Administratively down) or 'Not Present'
$adapters = $adapters | Where-Object { $_.Status -eq "Up" -or $_.Status -eq "Disconnected" }
Write-Log -Level DEBUG -Message "Filtering to active/disconnected adapters (excluding disabled)" -Module $script:ModuleName
}
# Layer 3: Exclude virtual adapter patterns (COMPREHENSIVE!)
# NOTE: We distinguish between HOST-side and GUEST-side virtual adapters:
# - HOST-side (vEthernet, VMware Network Adapter VMnet*) → EXCLUDE
# - GUEST-side (Microsoft Hyper-V Network Adapter in VM) → KEEP!
$virtualPatterns = @(
# Host-side virtualization adapters (NOT guest adapters!)
'*vEthernet*', # Hyper-V HOST virtual switch
'*VMware Network Adapter*', # VMware HOST adapters (VMnet1, VMnet8)
'*VirtualBox Host-Only*', # VirtualBox HOST-only adapter
'*Virtual*Adapter*', # Generic virtual adapters
'*Container*', '*WSL*', '*Docker*',
# Generic VPN protocols
'*VPN*', '*OpenVPN*', '*WireGuard*', '*TAP*',
'*L2TP*', '*IKEv2*', '*RAS*', '*PPTP*',
# Consumer VPN vendors
'*NordVPN*', '*NordLynx*', '*ExpressVPN*', '*ProtonVPN*', '*Mullvad*',
# Enterprise VPN vendors
'*Cisco*', '*Pulse*', '*FortiClient*',
'*Palo Alto*', '*PANGP*', # Palo Alto GlobalProtect (no "VPN" in name!)
'*F5*', '*Checkpoint*', '*Check Point*', '*Sonicwall*', '*Juniper*',
# Tunnel adapters
'*Tunnel*', '*Teredo*', '*6to4*', '*ISATAP*', '*Loopback*'
)
# Layer 4: Check for active Windows VPN connections
$activeVpnConnections = @()
try {
$vpnConns = Get-VpnConnection -ErrorAction SilentlyContinue
$activeVpnConnections = $vpnConns | Where-Object { $_.ConnectionStatus -eq 'Connected' }
}
catch {
Write-Log -Level DEBUG -Message "Get-VpnConnection not available or failed: $_" -Module $script:ModuleName
}
$filteredAdapters = @($adapters | Where-Object {
$description = $_.InterfaceDescription
$name = $_.Name
$skipAdapter = $false
$skipReason = ""
# Check if adapter matches any virtual pattern
foreach ($pattern in $virtualPatterns) {
if ($description -like $pattern -or $name -like $pattern) {
$skipAdapter = $true
$skipReason = "Pattern match: $pattern"
break
}
}
# Check if InterfaceType is Tunnel (131)
if (-not $skipAdapter -and $_.InterfaceType -eq 131) {
$skipAdapter = $true
$skipReason = "InterfaceType = 131 (Tunnel)"
}
# Check MediaType for Tunnel
if (-not $skipAdapter -and $_.MediaType -match "Tunnel") {
$skipAdapter = $true
$skipReason = "MediaType contains 'Tunnel'"
}
# Check for native Windows VPN connection
if (-not $skipAdapter -and $activeVpnConnections) {
$currentAdapterAlias = $_.InterfaceAlias
$matchingVpn = $activeVpnConnections | Where-Object { $_.InterfaceAlias -eq $currentAdapterAlias }
if ($matchingVpn) {
$skipAdapter = $true
$skipReason = "Native Windows VPN active: $($matchingVpn.Name)"
}
}
if ($skipAdapter) {
Write-Log -Level DEBUG -Message "Excluding adapter: $name - $skipReason" -Module $script:ModuleName
}
-not $skipAdapter
}) # Close @( array wrapper
if ($filteredAdapters.Count -eq 0) {
Write-Log -Level WARNING -Message "No physical network adapters found" -Module $script:ModuleName
return @()
}
Write-Log -Level DEBUG -Message "Found $($filteredAdapters.Count) physical network adapter(s)" -Module $script:ModuleName
foreach ($adapter in $filteredAdapters) {
Write-Log -Level DEBUG -Message " - $($adapter.Name) ($($adapter.InterfaceDescription)) [Status: $($adapter.Status)]" -Module $script:ModuleName
}
return $filteredAdapters # Already wrapped as array in line 83
}
catch {
Write-ErrorLog -Message "Failed to retrieve physical network adapters" -Module $script:ModuleName -ErrorRecord $_
return @()
}
}

View file

@ -0,0 +1,106 @@
function Reset-DnsState {
<#
.SYNOPSIS
Cleans up ALL DoH entries from ALL known providers
.DESCRIPTION
Deletes all DoH registrations (Cloudflare, AdGuard, NextDNS, Quad9)
and removes per-adapter DoH registry keys to ensure clean state.
CRITICAL: This prevents stale DoH entries from previous providers
from interfering with new provider settings.
.PARAMETER KeepAdapterDns
If specified, keeps current DNS server addresses on adapters.
Otherwise resets adapters to automatic DHCP DNS.
#>
[CmdletBinding()]
param(
[switch]$KeepAdapterDns
)
Write-Log -Level DEBUG -Message "Cleaning up DNS state (all providers)..." -Module "DNS"
# 1. Clear network caches FIRST (remove stale mappings before any changes)
Write-Log -Level DEBUG -Message "Clearing DNS, ARP and NetBIOS caches..." -Module "DNS"
# DNS cache
ipconfig /flushdns 2>$null | Out-Null
# ARP cache (use netsh - more reliable than arp -d * on international Windows)
netsh interface ip delete arpcache 2>$null | Out-Null
# NetBIOS name cache
nbtstat -R 2>$null | Out-Null
# 2. Delete ALL known DoH server registrations
$allKnownIps = @(
# Cloudflare (Standard)
'1.1.1.1', '1.0.0.1', '2606:4700:4700::1111', '2606:4700:4700::1001',
# Cloudflare (Family - Malware blocking)
'1.1.1.2', '1.0.0.2', '2606:4700:4700::1112', '2606:4700:4700::1002',
# Cloudflare (Family - Malware + Adult blocking)
'1.1.1.3', '1.0.0.3', '2606:4700:4700::1113', '2606:4700:4700::1003',
# AdGuard
'94.140.14.14', '94.140.15.15', '2a10:50c0::ad1:ff', '2a10:50c0::ad2:ff',
# NextDNS
'45.90.28.0', '45.90.30.0', '2a07:a8c0::', '2a07:a8c1::',
# Quad9
'9.9.9.9', '149.112.112.112', '2620:fe::fe', '2620:fe::9'
) | Select-Object -Unique
foreach ($ip in $allKnownIps) {
if ([string]::IsNullOrWhiteSpace($ip)) { continue }
try {
netsh dnsclient delete encryption server=$ip 2>$null | Out-Null
Write-Log -Level DEBUG -Message " Deleted DoH entry: $ip" -Module "DNS"
}
catch {
# Ignore - entry might not exist
$null = $null
}
}
# 3. Clean per-adapter DoH registry keys (all GUIDs)
# CRITICAL: We clean these because Enable-DoH + manual DohFlags setting will recreate them
$basePath = 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters'
if (Test-Path $basePath) {
Get-ChildItem $basePath -ErrorAction SilentlyContinue | ForEach-Object {
$adapterPath = $_.PSPath
# Remove DohInterfaceSettings (contains both Doh and Doh6 branches)
if (Test-Path "$adapterPath\DohInterfaceSettings") {
Remove-Item "$adapterPath\DohInterfaceSettings" -Recurse -Force -ErrorAction SilentlyContinue
Write-Log -Level DEBUG -Message " Cleaned DoH registry: $($_.PSChildName)" -Module "DNS"
}
}
}
# 4. Optional: Reset adapters to automatic DHCP DNS
$adapterWasReset = $false
if (-not $KeepAdapterDns) {
Write-Log -Level DEBUG -Message "Resetting adapters to automatic DNS..." -Module "DNS"
$adaptersToReset = Get-DnsClient -ErrorAction SilentlyContinue |
Where-Object { $_.InterfaceOperationalStatus -eq 'Up' }
foreach ($adapter in $adaptersToReset) {
try {
Set-DnsClientServerAddress -InterfaceAlias $adapter.InterfaceAlias `
-ResetServerAddresses -ErrorAction Stop
Write-Log -Level DEBUG -Message " Reset: $($adapter.InterfaceAlias)" -Module "DNS"
$adapterWasReset = $true
}
catch {
Write-Log -Level DEBUG -Message " Failed to reset: $($adapter.InterfaceAlias)" -Module "DNS"
}
}
}
# 5. Clear caches AGAIN if adapter was reset (DHCP may have created new cache entries)
if ($adapterWasReset) {
Write-Log -Level DEBUG -Message "Clearing caches again after adapter reset..." -Module "DNS"
ipconfig /flushdns 2>$null | Out-Null
netsh interface ip delete arpcache 2>$null | Out-Null
nbtstat -R 2>$null | Out-Null
}
Write-Log -Level DEBUG -Message "DNS state cleanup complete" -Module $script:ModuleName
}

View file

@ -0,0 +1,183 @@
function Set-DNSServers {
<#
.SYNOPSIS
Set DNS server addresses on network adapter
.DESCRIPTION
Configures DNS server addresses (IPv4 and IPv6) on specified network adapter.
Uses Microsoft Best Practice: Set-DnsClientServerAddress with -Validate parameter.
Always configures both IPv4 and IPv6 addresses. Windows will use IPv6 when available,
and fall back to IPv4 otherwise.
.PARAMETER InterfaceIndex
Network adapter interface index
.PARAMETER IPv4Primary
Primary IPv4 DNS server address
.PARAMETER IPv4Secondary
Secondary IPv4 DNS server address
.PARAMETER IPv6Primary
Primary IPv6 DNS server address
.PARAMETER IPv6Secondary
Secondary IPv6 DNS server address
.PARAMETER Validate
Validate DNS servers are reachable before applying (recommended)
.PARAMETER DryRun
Show what would be configured without applying changes
.EXAMPLE
Set-DNSServers -InterfaceIndex 12 -IPv4Primary "1.1.1.1" -IPv4Secondary "1.0.0.1" `
-IPv6Primary "2606:4700:4700::1111" -IPv6Secondary "2606:4700:4700::1001" -Validate
.OUTPUTS
System.Boolean - $true if successful, $false otherwise
.NOTES
Uses Set-DnsClientServerAddress cmdlet (PowerShell Best Practice)
NEVER uses netsh (deprecated legacy method)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[int]$InterfaceIndex,
[Parameter(Mandatory = $true)]
[string]$IPv4Primary,
[Parameter(Mandatory = $true)]
[string]$IPv4Secondary,
[Parameter(Mandatory = $true)]
[string]$IPv6Primary,
[Parameter(Mandatory = $true)]
[string]$IPv6Secondary,
[Parameter()]
[switch]$Validate,
[Parameter()]
[switch]$DryRun
)
try {
$adapter = Get-NetAdapter -InterfaceIndex $InterfaceIndex -ErrorAction Stop
$adapterName = $adapter.Name
Write-Log -Level INFO -Message "Configuring DNS servers on adapter: $adapterName" -Module $script:ModuleName
# Prepare IPv4 addresses array
$ipv4Addresses = @($IPv4Primary, $IPv4Secondary)
# Prepare IPv6 addresses array
$ipv6Addresses = @($IPv6Primary, $IPv6Secondary)
Write-Log -Level DEBUG -Message " IPv4: $($ipv4Addresses -join ', ')" -Module $script:ModuleName
Write-Log -Level DEBUG -Message " IPv6: $($ipv6Addresses -join ', ')" -Module $script:ModuleName
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would configure DNS servers on $adapterName" -Module $script:ModuleName
return $true
}
# Configure IPv4 DNS servers with retry logic (fixes 0x80004005 errors)
Write-Log -Level DEBUG -Message "Setting IPv4 DNS servers..." -Module $script:ModuleName
$ipv4Params = @{
InterfaceIndex = $InterfaceIndex
ServerAddresses = $ipv4Addresses
ErrorAction = 'Stop'
}
if ($Validate) {
$ipv4Params['Validate'] = $true
Write-Log -Level DEBUG -Message "Validation enabled for IPv4 DNS servers" -Module $script:ModuleName
}
# Retry logic with fast retries (adapter stabilization or offline detection)
$maxRetries = 3
$retryDelay = 1 # Fast 1-second retries (no exponential backoff needed)
for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
try {
Set-DnsClientServerAddress @ipv4Params
Write-Log -Level SUCCESS -Message "IPv4 DNS servers configured: $($ipv4Addresses -join ', ')" -Module $script:ModuleName
break
}
catch {
if ($attempt -lt $maxRetries) {
Write-Log -Level DEBUG -Message "Attempt $attempt failed, retrying... ($($_.Exception.Message))" -Module $script:ModuleName
Start-Sleep -Seconds $retryDelay
}
else {
# Fallback to netsh if CIM fails (General Error fix - often happens when offline)
Write-Log -Level DEBUG -Message "PowerShell cmdlet failed, using netsh fallback..." -Module $script:ModuleName
try {
# Use netsh for IPv4 configuration
$netshResult = & netsh interface ip set dns name="$adapterName" source=static address=$IPv4Primary validate=no 2>&1
if ($LASTEXITCODE -eq 0) {
# Add secondary DNS
$null = & netsh interface ip add dns name="$adapterName" address=$IPv4Secondary index=2 validate=no 2>&1
Write-Log -Level SUCCESS -Message "IPv4 DNS configured via netsh fallback: $($ipv4Addresses -join ', ')" -Module $script:ModuleName
break # Success, exit retry loop
}
else {
throw "Netsh fallback also failed: $netshResult"
}
}
catch {
throw "All DNS configuration methods failed: $_"
}
}
}
}
# Configure IPv6 DNS servers
# Note: IPv6 configuration uses the same cmdlet with IPv6 addresses
Write-Log -Level DEBUG -Message "Setting IPv6 DNS servers..." -Module $script:ModuleName
# For IPv6, we need to configure it separately
# Get the IPv6 interface
$ipv6Interface = Get-NetAdapter -InterfaceIndex $InterfaceIndex |
Get-NetAdapterBinding -ComponentID ms_tcpip6 -ErrorAction SilentlyContinue
if ($ipv6Interface -and $ipv6Interface.Enabled) {
try {
# Set IPv6 DNS using netsh as PowerShell cmdlet doesn't support dual-stack properly
# NOTE: This is one of the few cases where netsh is still needed for IPv6
$primaryResult = & netsh interface ipv6 set dnsservers name="$adapterName" source=static address=$IPv6Primary validate=no 2>&1
$secondaryResult = & netsh interface ipv6 add dnsservers name="$adapterName" address=$IPv6Secondary index=2 validate=no 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log -Level SUCCESS -Message "IPv6 DNS servers configured: $($ipv6Addresses -join ', ')" -Module $script:ModuleName
}
else {
Write-Log -Level WARNING -Message "IPv6 DNS configuration had issues (non-fatal): $primaryResult $secondaryResult" -Module $script:ModuleName
}
}
catch {
Write-Log -Level WARNING -Message "Could not configure IPv6 DNS (non-fatal): $_" -Module $script:ModuleName
}
}
else {
Write-Log -Level INFO -Message "IPv6 binding is disabled on this adapter - skipping IPv6 DNS server assignment (IPv4 + DoH templates will still be used)" -Module $script:ModuleName
}
# Configuration complete - Windows cmdlets verify automatically
return $true
}
catch {
Write-ErrorLog -Message "Failed to set DNS servers on interface $InterfaceIndex" -Module $script:ModuleName -ErrorRecord $_
return $false
}
}

View file

@ -0,0 +1,113 @@
function Set-DoHPolicy {
<#
.SYNOPSIS
Enforce DNS-over-HTTPS (DoH) system-wide according to the selected mode
.DESCRIPTION
Sets Windows registry keys to enforce DoH policy based on $script:DoHMode:
- DoHPolicy = 3 (REQUIRE DoH - mandatory encryption, no fallback)
- DoHPolicy = 2 (ALLOW DoH - encryption preferred, fallback to UDP allowed)
- EnableAutoDoh = 2 (Enable automatic DoH upgrade)
- netsh global doh = yes
DoHPolicy values: 0=Default, 1=Prohibit, 2=Allow, 3=Require
In REQUIRE mode this prevents Windows from silently falling back to
unencrypted DNS on port 53. In ALLOW mode, encrypted DoH is still used
for supported servers, but fallback to classic DNS is permitted for
VPN/mobile/enterprise scenarios.
.PARAMETER DryRun
Show what would be configured without applying changes
.EXAMPLE
Set-DoHPolicy
.NOTES
Requires Administrator privileges
Based on Microsoft DNS Client documentation
#>
[CmdletBinding()]
param(
[Parameter()]
[switch]$DryRun
)
try {
# Determine DoH mode (REQUIRE or ALLOW)
$dohModeValue = if ($script:DoHMode -eq "ALLOW") { 2 } else { 3 }
$dohModeText = if ($script:DoHMode -eq "ALLOW") { "ALLOW (with fallback)" } else { "REQUIRE (no fallback)" }
Write-Log -Level INFO -Message "Enforcing DoH policy ($dohModeText)" -Module $script:ModuleName
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would set DoH policy to $dohModeText" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] DoHPolicy = $dohModeValue ($($script:DoHMode))" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] EnableAutoDoh = 2 (enforce)" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] DohFlags = 1 (use DoH)" -Module $script:ModuleName
Write-Log -Level DEBUG -Message "[DRYRUN] netsh global doh = yes" -Module $script:ModuleName
return $true
}
# Registry path for DNS Client settings
$dnsClientPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient"
$dnsParamsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
# Ensure policy path exists
if (-not (Test-Path $dnsClientPath)) {
New-Item -Path $dnsClientPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created registry path: $dnsClientPath" -Module $script:ModuleName
}
# CRITICAL ORDER: netsh FIRST (may reset registry values), then Registry entries AFTER
# This ensures Registry values persist and are not overwritten by netsh
# 1. FIRST: Activate DoH globally via netsh (this may reset EnableAutoDoh!)
try {
netsh dnsclient set global doh=yes 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Log -Level DEBUG -Message "netsh global DoH activated" -Module $script:ModuleName
}
else {
Write-Log -Level WARNING -Message "netsh global DoH returned exit code $LASTEXITCODE" -Module $script:ModuleName
}
}
catch {
Write-Log -Level WARNING -Message "Could not activate global DoH via netsh: $_" -Module $script:ModuleName
}
# 2. SECOND: EnableAutoDoh = 2 (Enable automatic DoH) - AFTER netsh!
if (-not (Test-Path $dnsParamsPath)) {
New-Item -Path $dnsParamsPath -Force | Out-Null
}
$existing = Get-ItemProperty -Path $dnsParamsPath -Name "EnableAutoDoh" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $dnsParamsPath -Name "EnableAutoDoh" -Value 2 -Force | Out-Null
} else {
New-ItemProperty -Path $dnsParamsPath -Name "EnableAutoDoh" -Value 2 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Set EnableAutoDoh = 2 (Automatic DoH enabled)" -Module $script:ModuleName
# 3. THIRD: DoHPolicy = 2 (ALLOW) or 3 (REQUIRE) - LAST for highest priority
# Values: 0=Default, 1=Prohibit, 2=Allow, 3=Require
$existing = Get-ItemProperty -Path $dnsClientPath -Name "DoHPolicy" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $dnsClientPath -Name "DoHPolicy" -Value $dohModeValue -Force | Out-Null
} else {
New-ItemProperty -Path $dnsClientPath -Name "DoHPolicy" -Value $dohModeValue -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Set DoHPolicy = $dohModeValue ($dohModeText)" -Module $script:ModuleName
# NOTE: Global DohFlags removed - we use per-adapter DohFlags instead (set in Invoke-DNSConfiguration)
# Per-adapter DohFlags are more reliable and prevent conflicts
Write-Log -Level SUCCESS -Message "DoH policy verified: $dohModeText" -Module $script:ModuleName
return $true
}
catch {
Write-ErrorLog -Message "Failed to set DoH policy" -Module $script:ModuleName -ErrorRecord $_
return $false
}
}

Some files were not shown because too many files have changed in this diff Show more