#Requires -RunAsAdministrator # === RC Multi SafeBlock v2 — Adobe / Autodesk / SketchUp / Hancom / ZWCAD === # 관리자 PowerShell에서 실행 # ====== 글로벌 토글 ====== $DryRun = $false $Outbound = $true $Inbound = $false $CreateAllow = $true $Uninstall = $false $RuleGroup = "RC Multi SafeBlock v2" # ====== 복원지점 토글 ====== $RestorePoint_Enable = $true # 복원지점 생성/검증 수행 $RestorePoint_Drive = 'C:' # 보호 켤 드라이브 $RestorePoint_Tag = '리얼컴' # 설명 프리픽스 # ====== 벤더별 사용 여부 ====== $EnableAdobe = $true $EnableAutodesk = $true $EnableSketchUp = $true $EnableHancom = $true $EnableZWCAD = $true # ZWSOFT / ZWCAD # ====== 벤더별 고급 토글 ====== $Adobe_BlockCoreSync = $false $Autodesk_BlockLicCore = $false $Hancom_BlockCloud = $true # ====== ZWCAD 상세 토글 ====== $ZWCAD_BlockMainApp = $true # true면 ZWCAD.exe까지 아웃바운드 차단 $ZWCAD_BlockLicensing = $true # ZwAuthHost/License Manager 등 $ZWCAD_BlockUpdater = $true # Online update/업데이터 $ZWCAD_BlockCrashReportEtc = $true # CrashReport/Feedback/Report/Telemetry $ZWCAD_RequireZwsoftSignature = $true # true면 서명이 'ZWSOFT'일 때만 규칙 적용 # ====== hosts 싱크홀(선택) ====== $HostsSinkhole_Enable = $false $HostsSinkhole_File = "" $Hosts_Marker_Begin = "# === RC_MULTIBLOCK_BEGIN ===" $Hosts_Marker_End = "# === RC_MULTIBLOCK_END ===" $Hosts_MinList = @( "activate.adobe.com","practivate.adobe.com","lm.licenses.adobe.com","licenses.adobe.com", "lmlicenses.wip4.adobe.com","genuine.adobe.com","prod.adobegenuine.com", "na1r.services.adobe.com","adobeid-na1.services.adobe.com","auth.services.adobe.com", "ic.adobe.io","cc-api-data.adobe.io","oobesaas.adobe.com","assets.adobedtm.com" ) Import-Module NetSecurity -ErrorAction SilentlyContinue | Out-Null # ====== 공통 유틸 ====== function Add-Rule { param([string]$Name,[string]$Program,[ValidateSet('Block','Allow')]$Action,[ValidateSet('Outbound','Inbound')]$Direction) if (-not (Test-Path -LiteralPath $Program)) { return } if ($DryRun) { Write-Host "[DRYRUN] $Action $Direction $Program ($Name)"; return } try { New-NetFirewallRule -DisplayName $Name -Group $RuleGroup -Program $Program ` -Direction $Direction -Action $Action -Profile Any -ErrorAction SilentlyContinue | Out-Null } catch {} } function Remove-Rules-ByProgram([string]$exeFullPath, [string]$actionFilter = $null) { try { $filters = Get-NetFirewallRule -All -ErrorAction SilentlyContinue | Where-Object { -not $actionFilter -or $_.Action -eq $actionFilter } | Get-NetFirewallApplicationFilter -ErrorAction SilentlyContinue | Where-Object { $_.Program -eq $exeFullPath } if ($filters) { $ids = $filters | Select-Object -ExpandProperty InstanceID -Unique if ($DryRun) { Write-Host "[DRYRUN] Remove existing rules($actionFilter) for $exeFullPath : $($ids.Count)" } else { foreach ($id in $ids) { Get-NetFirewallRule -InstanceID $id | Remove-NetFirewallRule } } } } catch {} } function Collect-ExeSet($Dirs, $NamesMaybeRunning) { $set = New-Object 'System.Collections.Generic.HashSet[string]' ([StringComparer]::OrdinalIgnoreCase) foreach ($n in ($NamesMaybeRunning | Select-Object -Unique)) { $base = [System.IO.Path]::GetFileNameWithoutExtension($n) try { Get-Process -Name $base -ErrorAction Stop | ForEach-Object { if ($_.Path -and (Test-Path $_.Path)) { $set.Add($_.Path) | Out-Null } } } catch {} } foreach ($d in $Dirs) { try { if (Test-Path $d) { Get-ChildItem -LiteralPath $d -Recurse -Force -ErrorAction SilentlyContinue -Filter *.exe | ForEach-Object { $set.Add($_.FullName) | Out-Null } } } catch {} } return [string[]]$set } # 파일 강제 삭제(읽기전용/숨김 풀고 삭제, 실패해도 계속) function Remove-FileIfExists([string[]]$Paths) { foreach ($p in $Paths) { try { if (Test-Path -LiteralPath $p -PathType Leaf) { if ($DryRun) { Write-Host "[DRYRUN][DEL] $p"; continue } try { [System.IO.File]::SetAttributes($p, [System.IO.FileAttributes]::Normal) } catch {} try { Remove-Item -LiteralPath $p -Force -ErrorAction SilentlyContinue } catch {} if (-not (Test-Path -LiteralPath $p)) { Write-Host "[DEL] $p" } else { Write-Host "[DEL][WARN] 삭제 실패: $p" } } } catch {} } } # 서명 검사(옵션) function Test-FileSigner([string]$Path,[string]$Contains) { try { $sig = Get-AuthenticodeSignature -FilePath $Path -ErrorAction SilentlyContinue if ($sig -and $sig.SignerCertificate -and ($sig.SignerCertificate.Subject -like "*$Contains*")) { return $true } } catch {} return $false } # ====== 복원지점: 강제 생성 + 검증 ====== function New-RealcomRestorePointStrict { param( [string]$Drive = 'C:', [string]$Tag = '리얼컴' ) $date = Get-Date -Format 'yyyy.MM.dd' $desc = "$Tag ($date) 셋팅지점" Write-Host "[RESTORE] 시도: $desc (Drive=$Drive)" # 1) 시스템 보호 켜기 try { Enable-ComputerRestore -Drive $Drive -ErrorAction SilentlyContinue } catch {} # 2) 이전 마지막 시퀀스 기억 $lastBefore = $null try { $before = Get-ComputerRestorePoint -ErrorAction SilentlyContinue if ($before) { $lastBefore = ($before | Sort-Object SequenceNumber | Select-Object -Last 1).SequenceNumber } } catch {} # 3) 빈도 제한 완화 (1440분 -> 0) 후 생성 시도 $srKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore' $valName = 'SystemRestorePointCreationFrequency' $hadKey = $false try { if (Test-Path $srKey) { $hadKey = $true } else { New-Item -Path $srKey -Force | Out-Null } } catch {} $prev = $null try { $prev = (Get-ItemProperty -Path $srKey -Name $valName -ErrorAction SilentlyContinue).$valName } catch {} try { Set-ItemProperty -Path $srKey -Name $valName -Value 0 -Type DWord -ErrorAction SilentlyContinue } catch {} try { Checkpoint-Computer -Description $desc -RestorePointType "MODIFY_SETTINGS" -ErrorAction SilentlyContinue } catch {} # 4) 생성 검증(최대 30초 폴링) $ok = $false for ($i=0; $i -lt 30; $i++) { Start-Sleep -Seconds 1 try { $after = Get-ComputerRestorePoint -ErrorAction SilentlyContinue if ($after) { $lastAfter = ($after | Sort-Object SequenceNumber | Select-Object -Last 1) if ($lastAfter -and ($null -eq $lastBefore -or $lastAfter.SequenceNumber -gt $lastBefore)) { $ok = $true break } } } catch {} } # 5) 빈도 레지스트리 원복 try { if ($null -ne $prev) { Set-ItemProperty -Path $srKey -Name $valName -Value $prev -Type DWord -ErrorAction SilentlyContinue } else { Remove-ItemProperty -Path $srKey -Name $valName -ErrorAction SilentlyContinue if (-not $hadKey) { Remove-Item $srKey -Force -ErrorAction SilentlyContinue } } } catch {} if ($ok) { Write-Host "[RESTORE] 생성 확인됨: $desc" try { Get-ComputerRestorePoint -ErrorAction SilentlyContinue | Sort-Object SequenceNumber -Descending | Select-Object -First 3 SequenceNumber, CreationTime, Description | Format-Table -Auto } catch {} } else { Write-Warning "[RESTORE] 새 복원지점 확인 실패. (시스템 보호/정책/서비스/공간 문제 가능)" } } # ====== 벤더 스펙 ====== # -- Adobe -- $Adobe = @{ Dirs = @( "$env:ProgramFiles\Adobe", "$env:ProgramFiles (x86)\Adobe", "$env:ProgramFiles\Common Files\Adobe", "$env:ProgramFiles (x86)\Common Files\Adobe", "$env:ProgramData\Adobe", "$env:LOCALAPPDATA\Adobe" ) Allow = @( "Creative Cloud.exe","Creative Cloud Helper.exe","Creative Cloud Desktop.exe", "Creative Cloud Content Manager.exe","Creative Cloud Interprocess Service.exe", "AdobeIPCBroker.exe","Adobe CEF Helper.exe" ) Block = @( "AdobeUpdateService.exe","AdobeARMservice.exe","Acrobat Update Service.exe", "AdobeGCClient.exe","AGSService.exe","Adobe Genuine Monitor Service.exe", "AdobeNotificationClient.exe","CCLibrary.exe" ) Svc = @( "AdobeUpdateService","AdobeARMservice", "AGSService","Adobe Genuine Monitor Service", "Adobe Acrobat Update Service" ) } if ($Adobe_BlockCoreSync) { $Adobe.Block += "CoreSync.exe" } # -- Autodesk -- $Autodesk = @{ Dirs = @( "$env:ProgramFiles\Autodesk", "$env:ProgramFiles (x86)\Autodesk", "$env:ProgramFiles\Common Files\Autodesk Shared", "$env:ProgramData\Autodesk", "$env:LOCALAPPDATA\Autodesk" ) Allow = @("3dsmax.exe","maya.exe","acad.exe","revit.exe","Fusion360.exe","Inventor.exe","Alias.exe","mudbox.exe","recap.exe") Block = @("AdAppMgrSvc.exe","AutodeskDesktopApp.exe","AdAppMgr.exe","AdskAccessServiceHost.exe","AdskAccess.exe","GenuineService.exe") LicCore = @("AdskLicensingService.exe","AdskLicensingAgent.exe") Svc = @("AdAppMgrSvc") } if ($Autodesk_BlockLicCore) { $Autodesk.Block += $Autodesk.LicCore; $Autodesk.Svc += "AdskLicensingService" } # -- SketchUp -- $SketchUp = @{ Dirs = @( "$env:ProgramFiles\SketchUp", "$env:ProgramFiles (x86)\SketchUp", "$env:ProgramData\SketchUp", "$env:LOCALAPPDATA\SketchUp" ) Allow = @("SketchUp.exe","LayOut.exe","Style Builder.exe") Block = @("SketchUpWebHelper.exe","Trimble Connect.exe","Connect.exe","Connect.Agent.exe","Connect.Client.exe") Svc = @() } # -- Hancom (수정본) -- $Hancom = @{ Dirs = @( "$env:ProgramFiles\HNC", "$env:ProgramFiles\Hancom", "$env:ProgramFiles (x86)\HNC", "$env:ProgramFiles (x86)\Hancom", "$env:ProgramData\HNC", "$env:ProgramData\Hancom", "$env:LOCALAPPDATA\HNC", "$env:LOCALAPPDATA\Hancom" ) Allow = @("Hwp.exe","Hcell.exe","Hshow.exe","HwpM.exe","HOffice.exe") Block = @() Svc = @("HncUpdateService") } if ($Hancom_BlockCloud) { $Hancom.Block += @( "HncUpdateService.exe","HncUpdater.exe","HncReporter.exe", "HncFeedback.exe","HncCloud.exe","HncTray.exe" ) } # -- ZWCAD (ZWSOFT) $ZWCAD = @{ Dirs = @( "$env:ProgramFiles\ZWSOFT\ZWCAD 2026", "$env:ProgramFiles\ZWSOFT", "$env:ProgramFiles (x86)\ZWSOFT", "$env:ProgramData\ZWSOFT", "$env:LOCALAPPDATA\ZWSOFT" ) Allow = @() # 기본은 전면 차단. 메인 허용 원하면 아래 if에서 추가 Block = @() # 패턴 매칭으로 처리 (switch에서) Svc = @() # 알려진 서비스명 없어서 비움(오탐 방지) } if (-not $ZWCAD_BlockMainApp) { $ZWCAD.Allow += "ZWCAD.exe" } # 메인 허용 모드 # ====== Uninstall (규칙/hosts 블록 제거) ====== if ($Uninstall) { try { $r = Get-NetFirewallRule -Group $RuleGroup -ErrorAction Stop if ($r) { if ($DryRun) { Write-Host "[DRYRUN] Remove group: $RuleGroup ($($r.Count) rules)" } else { $r | Remove-NetFirewallRule } } else { Write-Host "해당 그룹 규칙 없음: $RuleGroup" } } catch { Write-Host "해당 그룹 규칙 없음: $RuleGroup" } $hosts = "$env:WINDIR\System32\drivers\etc\hosts" if (Test-Path $hosts) { $orig = Get-Content -LiteralPath $hosts -ErrorAction SilentlyContinue if ($orig) { $out = New-Object System.Collections.Generic.List[string] $in = $false foreach ($l in $orig) { if ($l.Trim() -eq $Hosts_Marker_Begin) { $in = $true; continue } if ($l.Trim() -eq $Hosts_Marker_End) { $in = $false; continue } if (-not $in) { $out.Add($l) | Out-Null } } if (-not $DryRun) { Set-Content -LiteralPath $hosts -Value $out -Encoding ASCII -Force } Write-Host "[HOSTS] 싱크홀 블록 제거 완료" } } return } # ====== 벤더 적용 함수 ====== function Apply-Vendor { param([string]$Vendor,[hashtable]$Spec) Write-Host "`n--- $Vendor 적용 시작 ---" foreach ($svc in ($Spec.Svc | ForEach-Object { $_ })) { if (-not $svc) { continue } try { $obj = Get-Service -Name $svc -ErrorAction SilentlyContinue if ($null -ne $obj) { if ($DryRun) { Write-Host "[DRYRUN][$Vendor] Stop & Disable service: $svc" } else { try { if ($obj.Status -ne 'Stopped') { Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue; Write-Host "[$Vendor][SERVICE-STOP] $svc" } } catch {} try { Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue; Write-Host "[$Vendor][SERVICE-DISABLE] $svc" } catch {} } } } catch {} } $namesMaybe = @() if ($Spec.Allow) { $namesMaybe += $Spec.Allow } if ($Spec.Block) { $namesMaybe += $Spec.Block } $allExes = Collect-ExeSet -Dirs $Spec.Dirs -NamesMaybeRunning $namesMaybe $blocked = 0; $allowed = 0; $skipped = 0 foreach ($exe in $allExes) { $leaf = Split-Path $exe -Leaf $lower = $exe.ToLowerInvariant() if ($Spec.Allow -and ($Spec.Allow -contains $leaf)) { Remove-Rules-ByProgram -exeFullPath $exe -actionFilter 'Block' if ($CreateAllow) { if ($Outbound) { Add-Rule -Name "$Vendor`_ALLOW_OUT_$leaf" -Program $exe -Action Allow -Direction Outbound ; $allowed++ } if ($Inbound) { Add-Rule -Name "$Vendor`_ALLOW_IN_$leaf" -Program $exe -Action Allow -Direction Inbound ; $allowed++ } } continue } $isBlock = $false if ($Spec.Block -and ($Spec.Block -contains $leaf)) { $isBlock = $true } if (-not $isBlock) { switch ($Vendor) { "Adobe" { if ($lower -match '\\adobegcclient\\' -or $lower -match '\\adobe desktop (common|service)\\' ` -or $leaf -match '^adobe(update|arm|genuine).*\.exe$' -or $leaf -match '^acrobat update service\.exe$' ` -or $leaf -match '^cclibrary\.exe$' -or ($Adobe_BlockCoreSync -and $leaf -match '^coresync\.exe$')) { $isBlock = $true } } "Autodesk" { if ($lower -match '\\autodesk desktop app\\' -or $leaf -match '^adappmgr.*\.exe$' ` -or $leaf -match '^autodeskdesktopapp\.exe$' -or $leaf -match '^adskaccess.*\.exe$' -or $leaf -match 'genuine.*\.exe$') { $isBlock = $true } if ($Autodesk_BlockLicCore -and ($leaf -match '^adsklicensing.*\.exe$' -or $leaf -eq 'adsklicensingservice.exe')) { $isBlock = $true } } "SketchUp" { if ($leaf -match '^sketchupwebhelper\.exe$' -or $lower -match '\\trimble(\\| )connect' ` -or $leaf -match '^connect(\.agent|\.client)?\.exe$' -or $leaf -match '^trimble.*\.exe$') { $isBlock = $true } } "Hancom" { if ($leaf -match '^hnc(update|updater|reporter|feedback|cloud|tray).*\.exe$') { $isBlock = $true } } "ZWCAD" { $vendorMatch = ($lower -match '\\zwsoft\\') -or ($leaf -match '^zw') if ($ZWCAD_RequireZwsoftSignature) { $vendorMatch = Test-FileSigner -Path $exe -Contains 'ZWSOFT CO., LTD.' } if ($vendorMatch) { if ($ZWCAD_BlockMainApp -and $leaf -match '^zwcad\.exe$') { $isBlock = $true } if ($ZWCAD_BlockLicensing -and ($leaf -match 'auth|licen[cs]e' -or $lower -match '\\license( manager| log viewer)?\\')) { $isBlock = $true } if ($ZWCAD_BlockUpdater -and ($leaf -match 'update|updater|online.*update' -or $lower -match '\\utility tools\\')) { $isBlock = $true } if ($ZWCAD_BlockCrashReportEtc -and ($leaf -match 'crash|report|feedback|telemetry|analytics')) { $isBlock = $true } } } } } if ($isBlock) { if ($Outbound) { Add-Rule -Name "$Vendor`_OUT_$leaf" -Program $exe -Action Block -Direction Outbound ; $blocked++ } if ($Inbound) { Add-Rule -Name "$Vendor`_IN_$leaf" -Program $exe -Action Block -Direction Inbound ; $blocked++ } } else { $skipped++ } } Write-Host "[$Vendor] Block: $blocked, Allow: $allowed, Skip: $skipped" } Write-Host "=== RC Multi SafeBlock v2 시작 === (DryRun=$DryRun | Out=$Outbound In=$Inbound)" # ====== HNC ci.png 영구 삭제 ====== try { $hncRoots = @( "$env:ProgramFiles (x86)\HNC", "$env:ProgramFiles (x86)\hnc", "$env:ProgramFiles\HNC" ) $targets = New-Object System.Collections.Generic.List[string] foreach ($root in $hncRoots) { if (Test-Path -LiteralPath $root) { $targets.Add((Join-Path $root 'ci.png')) | Out-Null try { $found = Get-ChildItem -LiteralPath $root -Filter 'ci.png' -Recurse -Force -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName if ($found) { $targets.AddRange($found) } } catch {} } } Remove-FileIfExists -Paths ($targets | Select-Object -Unique) } catch {} # ====== 벤더 적용 ====== if ($EnableAdobe) { Apply-Vendor -Vendor "Adobe" -Spec $Adobe } if ($EnableAutodesk) { Apply-Vendor -Vendor "Autodesk" -Spec $Autodesk } if ($EnableSketchUp) { Apply-Vendor -Vendor "SketchUp" -Spec $SketchUp } if ($EnableHancom) { Apply-Vendor -Vendor "Hancom" -Spec $Hancom } if ($EnableZWCAD) { Apply-Vendor -Vendor "ZWCAD" -Spec $ZWCAD } # ====== hosts 싱크홀(선택) ====== if ($HostsSinkhole_Enable) { $hosts = "$env:WINDIR\System32\drivers\etc\hosts" $lines = @() if ($HostsSinkhole_File -and (Test-Path $HostsSinkhole_File)) { $lines = Get-Content -LiteralPath $HostsSinkhole_File -ErrorAction SilentlyContinue | Where-Object { $_ -and $_.Trim() -ne "" } } else { $lines = $Hosts_MinList | ForEach-Object { "0.0.0.0 $_" } } if ($lines.Count -gt 0) { $orig = @() if (Test-Path $hosts) { $orig = Get-Content -LiteralPath $hosts -ErrorAction SilentlyContinue } $filtered = New-Object System.Collections.Generic.List[string] $in = $false foreach ($l in $orig) { if ($l.Trim() -eq $Hosts_Marker_Begin) { $in = $true; continue } if ($l.Trim() -eq $Hosts_Marker_End) { $in = $false; continue } if (-not $in) { $filtered.Add($l) | Out-Null } } $new = @() $new += $filtered $new += $Hosts_Marker_Begin $new += ($lines | Select-Object -Unique) $new += $Hosts_Marker_End if ($DryRun) { Write-Host "[DRYRUN][HOSTS] $(($lines | Measure-Object).Count) lines 적용" } else { Set-Content -LiteralPath $hosts -Value $new -Encoding ASCII -Force Write-Host "[HOSTS] 싱크홀 적용: $(($lines | Measure-Object).Count) 라인" } } } # ====== 복원지점 생성/검증 ====== if ($RestorePoint_Enable -and -not $Uninstall) { New-RealcomRestorePointStrict -Drive $RestorePoint_Drive -Tag $RestorePoint_Tag } Write-Host "=== 완료: 그룹 '$RuleGroup' 적용 및 복원지점 처리 끝 ==="