소개
Microsoft는 CVE-2024-20666의 보안 취약성을 해결하기 위해 배포된 디바이스에서 WinRE(Windows 복구 환경) 업데이트를 자동화하는 데 도움이 되는 샘플 PowerShell 스크립트를 개발했습니다.
샘플 PowerShell 스크립트
샘플 PowerShell 스크립트는 WinRE 이미지 업데이트를 자동화하는 데 도움이 되도록 Microsoft 제품 팀에서 개발했습니다. 영향을 받는 디바이스의 PowerShell에서 관리자 자격 증명을 사용하여 스크립트를 실행합니다.
참고 이 스크립트는 지원되는 모든 버전의 Windows용입니다.
#################################################################################
#
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
# 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.
#
#################################################################################
Param (
    [Parameter(Mandatory=$true, HelpMessage="Path to target package")][string]$PackagePath,
    [Parameter(Mandatory=$false, HelpMessage="Directory to dism get-packages logs")][string]$LogDir
)
Set-StrictMode -Version Latest;
# Function to get WinRE path
function GetWinREPath {
    $WinRELocation = (reagentc /info | Select-String "Windows RE location")
    if ($WinRELocation) {
        return $WinRELocation.ToString().Split(':')[-1].Trim()
    } else {
        Write-Host "Failed to find WinRE path" -ForegroundColor Red
        exit 1
    }
}
# Function to get WinRE version
function GetWinREVersion {
    $filePath = "C:\mnt\Windows\System32\winpeshl.exe"
    $WinREVersion = (Get-Item $filePath).VersionInfo.FileVersionRaw.Revision
    return [int]$WinREVersion
}
# Function to get package version
function GetPackageVersion {
    $PackageInfo = dism /Online /Get-PackageInfo /PackagePath:"$PackagePath" | Select-String "Version :"
    if ($PackageInfo) {
        $VersionString = ($PackageInfo -split ':')[-1].Trim()
        if ($VersionString -match "\d+\.\d+\.\d+\.(\d+)") {
            return [int]$matches[1]  # Extract the last part (build number)
        } else {
            Write-Host "Failed to parse package version" -ForegroundColor Red
            exit 1
        }
    } else {
        Write-Host "Failed to retrieve package version" -ForegroundColor Red
        exit 1
    }
}
# Function to ensure log directory access
function EnsureLogDirAccess {
    param([string]$LogDir)
    if (Test-Path $LogDir) {
        try {
            $TestFile = "$LogDir\test_write_access.txt"
            Set-Content -Path $TestFile -Value "Test" -ErrorAction Stop
            Remove-Item -Path $TestFile -Force -ErrorAction Stop
            Write-Host "Log directory access verified." -ForegroundColor Green
        } catch {
            Write-Host "Insufficient permissions for log directory: $LogDir. Attempting to gain access..." -ForegroundColor Yellow
            try {
                $acl = Get-Acl $LogDir
                $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("Everyone", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
                $acl.SetAccessRule($rule)
                Set-Acl -Path $LogDir -AclObject $acl
                Write-Host "Successfully gained access to log directory: $LogDir" -ForegroundColor Green
            } catch {
                Write-Host "Failed to gain access to log directory: $LogDir" -ForegroundColor Red
                exit 1
            }
        }
    } else {
        Write-Host "Log directory does not exist: $LogDir. Creating directory..." -ForegroundColor Yellow
        try {
            New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
            Write-Host "Log directory created: $LogDir" -ForegroundColor Green
        } catch {
            Write-Host "Failed to create log directory: $LogDir" -ForegroundColor Red
            exit 1
        }
    }
}
# If file is other than .cab then exit
if ($PackagePath -notmatch "\.cab$") {
    Write-Host "Invalid package format. Only .cab files are supported." -ForegroundColor Red
    exit 1
}
# Main Execution
$WinREPath = GetWinREPath
$PackageVersion = GetPackageVersion
Write-Host "WinRE Path: $WinREPath" -ForegroundColor Cyan
Write-Host "Package Version: $PackageVersion" -ForegroundColor Cyan
if ($LogDir) {
    EnsureLogDirAccess -LogDir $LogDir
}
# Make dir C:\mnt if not exists
$TempDir = "C:\mnt"
# Get the read write permission for this directory
if (-not (Test-Path $TempDir)) {
    New-Item -ItemType Directory -Path $TempDir -Force | Out-Null
}
# Mount WinRE image
dism /Mount-Image /ImageFile:"$WinREPath\winre.wim" /Index:1 /MountDir:"$TempDir"
$WinREVersion = GetWinREVersion
Write-Host "WinRE Version: $WinREVersion" -ForegroundColor Cyan
if ($PackageVersion -gt $WinREVersion)
{
    Write-Host "Applying patch..." -ForegroundColor Yellow
    if ($LogDir) {
        dism /Image:"$TempDir" /Add-Package /PackagePath:"$PackagePath" /LogPath:"$LogDir\PatchWinRE.log"
    } else {
        dism /Image:"$TempDir" /Add-Package /PackagePath:"$PackagePath"
    }
    Write-Host "Committing changes..." -ForegroundColor Yellow
    dism /Unmount-Image /MountDir:"$TempDir" /Commit
    Write-Host "Patch applied and committed successfully!" -ForegroundColor Green
}
else{
    Write-Host "Already have a greater or equal version, no update needed." -ForegroundColor Green
    dism /Unmount-Image /MountDir:"$TempDir" /Discard
}
# Disable WinRE and re-enable it to let new WinRE be trusted by BitLocker
Write-Host "Disable WinRE"
reagentc /disable
Write-Host "Re-enable WinRE"
reagentc /enable
reagentc /info
# Cleanup
Remove-Item -Path $TempDir -Force -Recurse
## Usage
# .\Patch_WinRE_Generic.ps1 -PackagePath <path_to_cab_file>
# .\Patch_WinRE_Generic.ps1 -PackagePath <path_to_cab_file> -LogDir <path_to_custom_log_folder>추가 정보
디바이스가 디바이스에 설치된 실행 중인 Windows 버전으로 시작되면 스크립트는 다음 단계를 수행합니다.
- 
              기존 WinRE 이미지(WINRE)를 탑재합니다. WIM). 
- 
              WinRE 이미지를 Windows 업데이트 카탈로그에서 사용할 수 있는 지정된 안전한 OS 동적 업데이트(호환성 업데이트) 패키지로 업데이트합니다. 디바이스에 설치된 Windows 버전에 사용할 수 있는 최신 안전 OS 동적 업데이트를 사용하는 것이 좋습니다. 
- 
              WinRE 이미지를 분리합니다. 
- 
              BitLocker TPM 보호기가 있는 경우 BitLocker 서비스에 대한 WinRE를 다시 구성합니다. 중요 이 단계는 WinRE 이미지에 업데이트를 적용하기 위한 대부분의 타사 스크립트에 없습니다. 
사용법
스크립트에 다음 매개 변수를 전달할 수 있습니다.
| 매개 변수 | 설명 | 
|---|---|
| LogDir | <선택적> WinRE를 패치하는 데 사용되는 스크래치 공간을 지정합니다. 지정하지 않으면 스크립트는 디바이스에 대한 기본 임시 폴더를 사용합니다. | 
| packagePath | <필수> WinRE 이미지를 업데이트하는 데 사용할 OS 버전별 및 프로세서 아키텍처별 안전 OS 동적 업데이트 패키지의 경로와 이름을 지정합니다.메모 로컬 경로 또는 원격 UNC 경로일 수 있지만 안전한 OS 동적 업데이트를 다운로드 하여 스크립트에서 사용할 수 있어야 합니다. 예:  | 
 
                         
				 
				