적용 대상
Windows 10 Home and Pro, version 21H2 Windows 10 Enterprise and Education, version 21H2 Windows 10 IoT Enterprise, version 21H2 Windows 10 Home and Pro, version 22H2 Windows 10 Enterprise Multi-Session, version 22H2 Windows 10 Enterprise and Education, version 22H2 Windows 10 IoT Enterprise, version 22H2 Windows 11 Home and Pro, version 21H2 Windows 11 Enterprise Multi-Session, version 21H2 Windows 11 Enterprise and Education, version 21H2 Windows 11 IoT Enterprise, version 21H2 Azure Local, version 22H2 Windows Server 2022

소개

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 버전으로 시작되면 스크립트는 다음 단계를 수행합니다.

  1. 기존 WinRE 이미지(WINRE)를 탑재합니다. WIM).

  2. WinRE 이미지를 Windows 업데이트 카탈로그에서 사용할 수 있는 지정된 안전한 OS 동적 업데이트(호환성 업데이트) 패키지로 업데이트합니다. 디바이스에 설치된 Windows 버전에 사용할 수 있는 최신 안전 OS 동적 업데이트를 사용하는 것이 좋습니다.

  3. WinRE 이미지를 분리합니다.

  4. BitLocker TPM 보호기가 있는 경우 BitLocker 서비스에 대한 WinRE를 다시 구성합니다.

    중요 이 단계는 WinRE 이미지에 업데이트를 적용하기 위한 대부분의 타사 스크립트에 없습니다.

사용법

스크립트에 다음 매개 변수를 전달할 수 있습니다.

매개 변수

설명

LogDir

<선택적> WinRE를 패치하는 데 사용되는 스크래치 공간을 지정합니다. 지정하지 않으면 스크립트는 디바이스에 대한 기본 임시 폴더를 사용합니다.

packagePath

<필수> WinRE 이미지를 업데이트하는 데 사용할 OS 버전별 및 프로세서 아키텍처별 안전 OS 동적 업데이트 패키지의 경로와 이름을 지정합니다.메모 로컬 경로 또는 원격 UNC 경로일 수 있지만 안전한 OS 동적 업데이트를 다운로드 하여 스크립트에서 사용할 수 있어야 합니다.

예: 

.\Patch_WinRE_Generic.ps1 -packagePath "\\server\share\windows10.0-kb5021043-x64_efa19d2d431c5e782a59daaf2d.cab

참조 자료

Windows PowerShell ISE에서 스크립트를 작성하고 실행하는 방법 

도움이 더 필요하세요?

더 많은 옵션을 원하세요?

구독 혜택을 살펴보고, 교육 과정을 찾아보고, 디바이스를 보호하는 방법 등을 알아봅니다.