-
Notifications
You must be signed in to change notification settings - Fork 6
/
Windows-DNS-AdBlocker.ps1
446 lines (388 loc) · 15.3 KB
/
Windows-DNS-AdBlocker.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
<#
.SYNOPSIS
Populate Windows DNS with entries for known ad-server domains to block them
.DESCRIPTION
Use DNS to block known ad-server domains by redirecting requests to localhost
This needs to be run with administrator privilege, and if using Active Directory integration,
you should run this under an account with domain admin privilege.
.PARAMETER Remove
If set, remove all ad-server entries from the DNS
If not set, update DNS with latest ad-server entries.
.PARAMETER ActiveDirectoryIntegrated
If set, detect Active Directory and create AD integrated zones if found.
Integrated zones will replicate to all DNS servers in AD forest.
#>
param
(
[switch]$Remove,
[switch]$ActiveDirectoryIntegrated
)
Write-Host "===================================================="
Write-Host "Windows-DNS-AdBlocker"
Write-Host "https://github.com/perplexityjeff/Windows-DNS-AdBlocker"
Write-Host "===================================================="
function Insert-Content ($file)
{
BEGIN
{
$content = Get-Content $file
}
PROCESS
{
$_ | Set-Content $file
}
END
{
$content | Add-Content $file
}
}
#Declares
$artifactPath = Join-Path $env:TEMP "DNS Blocklist"
$url = "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=win32reg-sp4&showintro=0&mimetype=plaintext"
$adServerZoneFile = "adservers.dns"
$adserversurl = "https://raw.githubusercontent.com/perplexityjeff/Windows-DNS-AdBlocker/master/$adServerZoneFile"
$adserverstemp = Join-Path $artifactPath $adServerZoneFile
$adServersZoneFileLocation = Join-Path $env:SYSTEMROOT (Join-Path "System32\dns" $adServerZoneFile)
$date = Get-Date -format "yyyyMMdd"
$blocklist = Join-Path $artifactPath ("Blocklist_" + $date + ".reg")
$limit = (Get-Date).AddDays(-15)
# How we will identify Active Directory integrated zones created by this tool.
$responsiblePerson = "adserver-71e5831a-aba8-4890-9037-399cb92de586"
# Detect Active Directory and DnsServer PowerShell module
$useActiveDirectory = $false
$activeDirectoryDetected = $null -ne $env:LOGONSERVER -and $null -ne $env:USERDOMAIN
$haveDnsServerModule = $null -ne (Get-Module -ListAvailable DnsServer)
# This sets the security used in the WebClient to TLS 1.2, if it fails like on V2 it uses another method
try
{
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}
catch
{
$p = [Enum]::ToObject([System.Net.SecurityProtocolType], 3072);
[System.Net.ServicePointManager]::SecurityProtocol = $p;
}
try
{
# Test for admin rights
if (-not (New-Object Security.Principal.WindowsPrincipal -ArgumentList ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
{
throw "Must be Administrator to run this program."
}
# Test if DNS server exists on this host
if (-not (Get-Service -Name DNS -ErrorAction SilentlyContinue))
{
throw "Local DNS server not found. Please run on a server that hosts DNS."
}
if (-not ([Environment]::UserInteractive))
{
# Hide progress bars if running e.g. in task scheduler.
$ProgressPreference = 'SilentlyContinue'
}
if ($haveDnsServerModule)
{
# For Windows 2012 and above, use DnsServer cmdlets
Write-Host "Importing DnsServer module"
Import-Module DnsServer
}
if ($activeDirectoryDetected -and $haveDnsServerModule -and ([Environment]::UserInteractive) -and -not ($ActiveDirectoryIntegrated -or $Remove))
{
# Recommend AD integration if not selected.
$choice = $host.ui.PromptForChoice(
"Active Directory detected. It is recommended to use it as it will replicate zones to all DNS servers in the forest. Use Active Directory?",
$null,
@(
New-Object System.Management.Automation.Host.ChoiceDescription ('&Yes', "Use Active Directory." )
New-Object System.Management.Automation.Host.ChoiceDescription ('&No', 'Use file-based zones (no replication).')
),
0
)
if ($choice -eq 0)
{
$useActiveDirectory = $true
}
}
if ($ActiveDirectoryIntegrated)
{
# If user asked for Active Directory integration, check we can actually do it.
if (-not $activeDirectoryDetected)
{
Write-Warning "Active Directory domain not detected, or you are not using a domain login. Falling back to file-based zones"
}
else
{
# Check we have the required PowerShell module for integration
if ($haveDnsServerModule)
{
$useActiveDirectory = $true
}
else
{
Write-Warning "PowerShell module DnsServer not detected. Cannot use AD integration. Perhaps this version of Windows is too old. Falling back to file-based zones"
}
}
}
if ($Remove)
{
Write-Host "Removing ad-block entries from DNS only."
}
else
{
# Testing if DNS Blocklist folder exists
Write-Host "Detecting if download location exists..."
if (Test-Path $artifactPath)
{
Write-Host "Download location exists"
}
else
{
New-Item $artifactPath -ItemType directory
Write-Host "Download location has been created"
}
# Testing if $adServerZoneFile file exists
Write-Host "Detecting if $adServerZoneFile file exists..."
if (-not (Test-Path $adServersZoneFileLocation))
{
Write-Host "Downloading default $adServerZoneFile file..."
$client = new-object System.Net.WebClient
try
{
$client.DownloadFile($adserversurl, $adserverstemp)
}
finally
{
if ($client)
{
$client.Dispose()
}
}
Write-Host "Downloaded default $adServerZoneFile file"
Write-Host "Placing downloaded $adServerZoneFile file in the systemroot..."
Move-Item $adserverstemp $adServersZoneFileLocation
Write-Host "Placed downloaded $adServerZoneFile file in the systemroot"
}
else
{
Write-Host "Detected $adServerZoneFile file"
}
# Testing if DNS Blocklist exists
Write-Host "Detecting if older DNS Blocklist exists..."
if (Test-Path $blocklist)
{
Write-Host "Deleting old DNS Blocklist..."
Remove-Item ($blocklist)
Write-Host "Deleted old DNS Blocklist"
}
else
{
Write-Host "No existing DNS Blocklist found"
}
# Downloading of the Adblock reg file
Write-Host "Downloading newest AdBlock file..."
if (-not (Split-Path -parent $artifactPath) -or -not (Test-Path -pathType Container (Split-Path -parent $artifactPath)))
{
$artifactPath = Join-Path $pwd (Split-Path -leaf $artifactPath)
}
try
{
$client = new-object System.Net.WebClient
try
{
$client.DownloadFile($url, $blocklist)
}
finally
{
if ($client)
{
$client.Dispose()
}
}
Write-Host "Downloaded newest AdBlock file"
}
catch
{
throw "Download of the DNS Blocklist failed"
}
Write-Host "Detecting if new DNS Blocklist exists..."
if (-Not(Test-Path $blocklist))
{
throw "Download of the DNS Blocklist failed"
}
}
if (-not $haveDnsServerModule)
{
# With DnsServer PowerShell module, we can operate on the DNS hot.
# If poking the registry, we need to do it cold.
# Stopping the DNS Server.
Write-Host "Stopping DNS Server..."
Stop-Service -Name DNS
Write-Host "Stopped DNS Server"
}
# Remove All Old Entries (CAUTION: Be sure to tweak this to your environment and not delete valid DNS entries)
Write-Host "Deleting old Blocklist entries"
$zoneKey = "HKLM:\software\Microsoft\Windows NT\CurrentVersion\DNS Server\Zones\"
# Read all zones _before_ modifying anything
if ($haveDnsServerModule)
{
# We can do some pre-filtering here
$allZones = Get-DnsServerZone | Where-Object { $_.ZoneType -eq 'Primary' -and -not $_.IsReverseLookupZone -and -not $_.IsAutoCreated }
}
else
{
$allZones = (Get-ChildItem -Path $zoneKey).Name |
Split-Path -Leaf |
ForEach-Object {
# Make these results look like a DNS Zone object returned by Get-DnsZerverZone
New-Object PSObject -Property @{
ZoneName = $_
}
}
}
# Further filter out zones matching the the user DNS domain and the TrustAnchors special domain
$allZones = $allZones |
Where-Object {
$_.ZoneName -ne 'TrustAnchors'
} |
Where-Object {
if ($null -eq $env:USERDNSDOMAIN)
{
# Include all if no DNS domain to compare with
$true
}
else
{
# Include those not matching the local DNS domain
-not $_.ZoneName.EndsWith($env:USERDNSDOMAIN, [StringComparison]::OrdinalIgnoreCase)
}
}
# Now do the deletions
# Use Measure-Object as it can count the input object being $null
$totalZones = ($allZones | Measure-Object).Count
$numRemoved = 0
$numProcessed = 0
$allZones |
ForEach-Object {
if ($numRemoved % 20 -eq 0)
{
# Update progress every 20 zones
$percentComplete = [int](($numProcessed / $totalZones) * 100)
Write-Progress -Activity "Deleting adserver DNS zones" -Status "$($percentComplete)% Complete:" -PercentComplete $percentComplete
}
if ($haveDnsServerModule)
{
$zone = Get-DnsServerZone -Name $_.ZoneName -ErrorAction SilentlyContinue
if ($null -ne $zone)
{
# Is it one of ours?
if ($zone.IsDsIntegrated)
{
# AD Integrated - Identify by the value we set for Responsible Person in the SOA record...
# Get SOA record
$soa = $zone | Get-DnsServerResourceRecord -RRType SOA
if ($soa.RecordData.ResponsiblePerson.StartsWith($responsiblePerson))
{
# Delete it.
Write-Verbose "Removing zone: $($_.ZoneName)"
$zone | Remove-DnsServerZone -Force
++$numRemoved
}
}
else
{
# File zone - Identify by the zone filename
if ($zone.ZoneFile -eq $adServerZoneFile)
{
# Delete it.
Write-Verbose "Removing zone: $($_.ZoneName)"
$zone | Remove-DnsServerZone -Force
++$numRemoved
}
}
}
++$numProcessed
}
else
{
$CurrentKey = Get-ItemProperty -Path (Join-Path $zoneKey $_.ZoneName)
# Cleanly detect zones that are using adservers.dns
if ($CurrentKey.PSObject.Properties.Name -icontains 'DatabaseFile' -and $CurrentKey.DatabaseFile -ieq $adServerZoneFile)
{
Write-Verbose "Removing zone: $($CurrentKey.PSChildName)"
$CurrentKey | Remove-Item -Force #-Whatif
++$numRemoved
}
}
}
# Import new adserver zones
# Clear progress bar
Write-Progress -Activity "Deleting adserver DNS zones" -Status "100% Complete:" -PercentComplete 100 -Completed
Write-Host "Deleted $numRemoved old Blocklist entries from Registry"
if (-not $Remove)
{
Write-Host "Importing AdBlock file..."
if ($haveDnsServerModule)
{
$numAdded = 0
# Parse the REG file for domains and add them
$domains = Get-Content $blocklist |
Foreach-Object {
if ($_ -match '\\(?<domain>[^\\]+)\]\s*$')
{
$Matches.domain
}
}
$totalZones = ($domains | Measure-Object).Count
$domains |
Foreach-Object {
if ($numAdded % 20 -eq 0)
{
# Update progress every 20 zones
$percentComplete = [int](($numAdded / $totalZones) * 100)
Write-Progress -Activity "Adding adserver DNS zones" -Status "$($percentComplete)% Complete:" -PercentComplete $percentComplete
}
if ($useActiveDirectory)
{
# Create Active Directory integrated zone and add records to block
# Set the responsible person field in the SOA record to our magic value for easy indentification on delete.
$zone = Add-DnsServerPrimaryZone -Name $_ -ResponsiblePerson $responsiblePerson -ReplicationScope Forest -PassThru
$zone | Add-DnsServerResourceRecordA -IPv4Address 0.0.0.0 -Name '*' -TimeToLive 01:00:00
$zone | Add-DnsServerResourceRecordA -IPv4Address 0.0.0.0 -Name '@' -TimeToLive 01:00:00
}
else
{
# Create zone and point to our zone file
Add-DnsServerPrimaryZone -Name $_ -ZoneFile $adServerZoneFile -LoadExisting -ResponsiblePerson $responsiblePerson
}
++$numAdded
}
# Clear progress bar
Write-Progress -Activity "Adding adserver DNS zones" -Status "100% Complete:" -PercentComplete 100 -Completed
Write-Host "Imported $numAdded zones from AdBlock file"
}
else
{
# Importing the file into regedit
"REGEDIT4`n" | Insert-Content $blocklist
regedit.exe /s $blocklist
Write-Host "Imported AdBlock file"
}
}
if ((Get-Service -Name DNS).Status -ne 'Running')
{
# Starting the DNS Server
Write-Host "Starting DNS Server..."
Start-Service -Name DNS
Write-Host "Started DNS Server"
}
#Removing Blocklist files older then 15 days
Write-Host "Removing old AdBlock files..."
Get-ChildItem -Path $artifactPath -Recurse -Force | Where-Object { -not $_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
Write-Host "Removed old AdBlock files"
#Script has been completed
Write-Host "Script has been completed"
}
catch
{
Write-Host -ForegroundColor Red $_.Exception.Message
exit 1
}