Files
windows-builder/includes/utils/change-bootres.ps1
2026-06-02 03:37:09 -07:00

465 lines
15 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string]$BootResDllPath,
[Parameter(Mandatory = $false)]
[string]$BootResMuiPath,
[Parameter(Mandatory = $true)]
[string[]]$LogoPaths,
[Parameter(Mandatory = $false)]
[string]$CertificateThumbprint = "",
[Parameter(Mandatory = $false)]
[string]$PfxPath = "",
[Parameter(Mandatory = $false)]
[string]$PfxPassword = "",
[switch]$UseLegacySizes
)
#Requires -RunAsAdministrator
# Boot logo sizes for Windows 10/11
$LogoSizes = @(
@{Name = "winlogo1.bmp"; Width = 72; Height = 72; Description = "Small Logo" },
@{Name = "winlogo2.bmp"; Width = 90; Height = 90; Description = "Medium Logo" },
@{Name = "winlogo3.bmp"; Width = 115; Height = 115; Description = "Standard Logo" },
@{Name = "winlogo3n.bmp"; Width = 87; Height = 115; Description = "Narrow Logo" },
@{Name = "winlogo4.bmp"; Width = 214; Height = 214; Description = "Large Logo" },
@{Name = "winlogo5.bmp"; Width = 284; Height = 284; Description = "Extra Large Logo" }
)
# Legacy Windows 8/10 sizes
$LegacyLogoSizes = @(
@{Name = "winlogo1.bmp"; Width = 82; Height = 72 },
@{Name = "winlogo2.bmp"; Width = 102; Height = 90 },
@{Name = "winlogo3.bmp"; Width = 129; Height = 115 },
@{Name = "winlogo3n.bmp"; Width = 98; Height = 115 },
@{Name = "winlogo4.bmp"; Width = 242; Height = 214 },
@{Name = "winlogo5.bmp"; Width = 321; Height = 284 }
)
# Function to log messages
function Write-Log {
param(
[string]$Message,
[string]$Color = "White"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
switch ($Color) {
"Green" { Write-Host "[$timestamp] $Message" -ForegroundColor Green }
"Yellow" { Write-Host "[$timestamp] $Message" -ForegroundColor Yellow }
"Red" { Write-Host "[$timestamp] $Message" -ForegroundColor Red }
"Cyan" { Write-Host "[$timestamp] $Message" -ForegroundColor Cyan }
default { Write-Host "[$timestamp] $Message" }
}
}
# Function to extract RCDATA resource from DLL
function Extract-RCDataResource {
param(
[string]$DllPath,
[string]$OutputPath,
[string]$ResourceHackerPath
)
Write-Log "Extracting RCDATA resource from DLL..."
$rhArgs = @(
"-open", "`"$DllPath`"",
"-save", "`"$OutputPath`"",
"-action", "extract",
"-mask", "RCDATA,1,"
)
$process = Start-Process -FilePath $ResourceHackerPath -ArgumentList $rhArgs -Wait -NoNewWindow -PassThru
if ($process.ExitCode -ne 0) {
throw "ResourceHacker failed to extract RCDATA resource (exit code: $($process.ExitCode))"
}
if (-not (Test-Path $OutputPath)) {
throw "RCDATA resource was not extracted to: $OutputPath"
}
# Verify it's a WIM
$bytes = [System.IO.File]::ReadAllBytes($OutputPath)
if ($bytes.Length -lt 5) {
throw "Extracted file is too small to be a WIM"
}
$signature = [System.Text.Encoding]::ASCII.GetString($bytes[0..4])
if ($signature -ne "MSWIM") {
throw "Extracted resource is not a valid WIM file (signature: $signature)"
}
Write-Log "RCDATA resource extracted: $([math]::Round((Get-Item $OutputPath).Length / 1KB, 2)) KB" -Color "Green"
}
# Function to inject RCDATA resource into DLL
function Inject-RCDataResource {
param(
[string]$DllPath,
[string]$ResourcePath,
[string]$OutputDllPath,
[string]$ResourceHackerPath
)
Write-Log "Injecting modified RCDATA resource into DLL..."
# Copy original DLL if output path differs
if ($DllPath -ne $OutputDllPath) {
Copy-Item -Path $DllPath -Destination $OutputDllPath -Force
}
# ResourceHacker CLI arguments
$rhArgs = @(
"-open", "`"$OutputDllPath`"",
"-save", "`"$OutputDllPath`"",
"-action", "addoverwrite",
"-resource", "`"$ResourcePath`"",
"-mask", "RCDATA,1,1033"
)
$process = Start-Process `
-FilePath $ResourceHackerPath `
-ArgumentList $rhArgs `
-Wait `
-NoNewWindow `
-PassThru
if ($process.ExitCode -ne 0) {
throw "ResourceHacker failed to inject RCDATA resource (exit code: $($process.ExitCode))"
}
Write-Log "RCDATA resource injected successfully" -Color "Green"
}
# Function to check if 7-Zip is available
function Test-7ZipAvailable {
$7zipPaths = @(
"C:\Program Files\7-Zip\7z.exe",
"C:\Program Files (x86)\7-Zip\7z.exe",
"$env:ProgramFiles\7-Zip\7z.exe"
)
foreach ($path in $7zipPaths) {
if (Test-Path $path) {
return $path
}
}
try {
$null = Get-Command 7z -ErrorAction Stop
return "7z"
}
catch {
return $null
}
}
# Function to find SignTool from Windows SDK
function Find-SignTool {
$sdkPaths = @(
"C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe",
"C:\Program Files\Windows Kits\10\bin\*\x64\signtool.exe",
"C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe",
"C:\Program Files\Windows Kits\10\App Certification Kit\signtool.exe"
)
foreach ($pathPattern in $sdkPaths) {
$found = Get-ChildItem -Path $pathPattern -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending |
Select-Object -First 1
if ($found) {
return $found.FullName
}
}
try {
$cmd = Get-Command signtool.exe -ErrorAction Stop
return $cmd.Source
}
catch {
return $null
}
}
# Function to sign a file with a certificate or PFX
function Sign-FileWithCertificate {
param(
[string]$FilePath,
[string]$SignToolPath,
[string]$CertificateThumbprint = "",
[string]$PfxPath = "",
[string]$PfxPassword = "",
[string]$TimestampServer = "http://timestamp.digicert.com"
)
Write-Log "Signing file: $FilePath"
if ($PfxPath -and (Test-Path $PfxPath)) {
$signArgs = @(
"sign",
"/f", "`"$PfxPath`""
)
if ($PfxPassword) {
$signArgs += @("/p", $PfxPassword)
}
$signArgs += @(
"/fd", "SHA256",
"/t", $TimestampServer,
"`"$FilePath`""
)
}
elseif ($CertificateThumbprint) {
$signArgs = @(
"sign",
"/sha1", $CertificateThumbprint,
"/fd", "SHA256",
"/t", $TimestampServer,
"`"$FilePath`""
)
}
else {
$signArgs = @(
"sign",
"/a",
"/fd", "SHA256",
"/t", $TimestampServer,
"`"$FilePath`""
)
}
$outputLog = Join-Path $env:TEMP "signtool_out.log"
$errorLog = Join-Path $env:TEMP "signtool_err.log"
$process = Start-Process -FilePath $SignToolPath -ArgumentList $signArgs -Wait -NoNewWindow -PassThru -RedirectStandardOutput $outputLog -RedirectStandardError $errorLog
if ($process.ExitCode -eq 0) {
Write-Log "File signed successfully" -Color "Green"
return $true
}
else {
$errors = Get-Content $errorLog -Raw -ErrorAction SilentlyContinue
Write-Log "Signing failed: $errors" -Color "Yellow"
return $false
}
}
# Function to resize bitmap
function Resize-Bitmap {
param(
[string]$SourcePath,
[string]$DestPath,
[int]$Width,
[int]$Height
)
Add-Type -AssemblyName System.Drawing
$sourceImage = [System.Drawing.Image]::FromFile($SourcePath)
$destImage = New-Object System.Drawing.Bitmap($Width, $Height)
$graphics = [System.Drawing.Graphics]::FromImage($destImage)
# High quality resize
$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
$graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
$graphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
$graphics.DrawImage($sourceImage, 0, 0, $Width, $Height)
$destImage.Save($DestPath, [System.Drawing.Imaging.ImageFormat]::Bmp)
$graphics.Dispose()
$destImage.Dispose()
$sourceImage.Dispose()
}
# Function to update WIM with new bitmaps
function Update-WimBitmaps {
param(
[string]$WimPath,
[string]$BitmapsFolder,
[string]$SevenZipPath
)
Write-Log "Updating WIM with new bitmaps..."
$bitmapFiles = Get-ChildItem -Path $BitmapsFolder -Filter "winlogo*.bmp" | ForEach-Object { "`"$($_.FullName)`"" }
if ($bitmapFiles.Count -eq 0) {
throw "No winlogo bitmaps found in: $BitmapsFolder"
}
$updateArgs = @("u", "`"$WimPath`"") + $bitmapFiles + @("-y")
Write-Log "Updating WIM with $($bitmapFiles.Count) bitmaps..."
$process = Start-Process -FilePath $SevenZipPath -ArgumentList $updateArgs -Wait -NoNewWindow -PassThru
if ($process.ExitCode -ne 0) {
throw "Failed to update WIM with bitmaps (7z exit code: $($process.ExitCode))"
}
Write-Log "WIM updated successfully" -Color "Green"
}
# Main script execution
try {
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Windows Boot Logo Changer" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Log "Starting boot resource modification..."
# Validate input files
if (-not (Test-Path $BootResDllPath)) {
throw "bootres.dll not found at: $BootResDllPath"
}
foreach ($logoPath in $LogoPaths) {
if (-not (Test-Path $logoPath)) {
throw "Logo file not found at: $logoPath"
}
}
# ResourceHacker path
$rhPath = Join-Path $PSScriptRoot "ResourceHacker.exe"
if (-not (Test-Path $rhPath)) {
throw "ResourceHacker.exe not found at: $rhPath"
}
Write-Log "Found ResourceHacker at: $rhPath" -Color "Green"
# Check for 7-Zip
$7zPath = Test-7ZipAvailable
if (-not $7zPath) {
throw "7-Zip is required but not found. Please install 7-Zip from https://www.7-zip.org/"
}
Write-Log "Found 7-Zip at: $7zPath" -Color "Green"
# Create temporary working directory in script location
$tempDir = Join-Path $PSScriptRoot "temp_bootres_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
Write-Log "Working directory: $tempDir"
# Step 1: Extract RCDATA resource (WIM) from bootres.dll
$extractedWim = Join-Path $tempDir "bootres.wim"
Extract-RCDataResource -DllPath $BootResDllPath -OutputPath $extractedWim -ResourceHackerPath $rhPath
# Step 2: Create modified WIM
$modifiedWim = Join-Path $tempDir "bootres_modified.wim"
Copy-Item -Path $extractedWim -Destination $modifiedWim -Force
# Step 3: Create bitmaps directory
$bitmapsDir = Join-Path $tempDir "bitmaps"
New-Item -ItemType Directory -Path $bitmapsDir -Force | Out-Null
# Choose which size set to use
$sizesToUse = if ($UseLegacySizes) {
Write-Log "Using legacy Windows 8/10 logo sizes"
$LegacyLogoSizes
}
else {
Write-Log "Using modern Windows 11 logo sizes"
$LogoSizes
}
# Step 4: Generate resized bitmaps
Write-Log "Generating resized bitmaps..."
for ($i = 0; $i -lt $sizesToUse.Count; $i++) {
$logoInfo = $sizesToUse[$i]
$destPath = Join-Path $bitmapsDir $logoInfo.Name
if ($LogoPaths.Count -eq 1) {
$sourceLogo = $LogoPaths[0]
}
elseif ($i -lt $LogoPaths.Count) {
$sourceLogo = $LogoPaths[$i]
}
else {
$sourceLogo = $LogoPaths[-1]
}
Resize-Bitmap -SourcePath $sourceLogo -DestPath $destPath -Width $logoInfo.Width -Height $logoInfo.Height
}
# Step 5: Update WIM with new bitmaps
Update-WimBitmaps -WimPath $modifiedWim -BitmapsFolder $bitmapsDir -SevenZipPath $7zPath
# Step 6: Create modified bootres.dll with ResourceHacker
$outputDll = Join-Path $tempDir "bootres_modified.dll"
Inject-RCDataResource -DllPath $BootResDllPath -ResourcePath $modifiedWim -OutputDllPath $outputDll -ResourceHackerPath $rhPath
# Step 7: Sign the DLL (if requested)
$signToolPath = Find-SignTool
$signed = $false
if ($signToolPath) {
Write-Log "Found SignTool at: $signToolPath" -Color "Green"
if ($PfxPath -or $CertificateThumbprint) {
$signed = Sign-FileWithCertificate -FilePath $outputDll -SignToolPath $signToolPath `
-CertificateThumbprint $CertificateThumbprint -PfxPath $PfxPath -PfxPassword $PfxPassword
}
else {
Write-Log "No certificate specified. Skipping code signing." -Color "Yellow"
}
}
else {
Write-Log "SignTool not found. Skipping code signing." -Color "Yellow"
}
# Final summary
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host " SUCCESS!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Log "Modified bootres.dll created at: $outputDll" -Color "Green"
Write-Host ""
# Check if file is signed
$finalSig = Get-AuthenticodeSignature -FilePath $outputDll
if ($finalSig.Status -eq 'Valid') {
Write-Host "[OK] File is digitally signed" -ForegroundColor Green
Write-Host " Signed by: $($finalSig.SignerCertificate.Subject)" -ForegroundColor Gray
}
else {
Write-Host "[!] File is NOT signed" -ForegroundColor Yellow
Write-Host " Requires Test Signing mode or Secure Boot disabled" -ForegroundColor Gray
}
# Copy final DLL to script directory
$FinallDLL = Join-Path $PSScriptRoot "bootres_modified.dll"
Copy-Item -Path $outputDll -Destination $FinallDLL -Force
# Cleanup temporary directory
Remove-Item -Path $tempDir -Recurse -Force
Write-Host ""
Write-Host "Modified DLL located at: $FinallDLL" -ForegroundColor Cyan
Write-Host ""
}
catch {
Write-Host ""
Write-Host "========================================" -ForegroundColor Red
Write-Host " ERROR!" -ForegroundColor Red
Write-Host "========================================" -ForegroundColor Red
Write-Host ""
Write-Host "Error: $_" -ForegroundColor Red
Write-Host ""
Write-Host "Stack trace:" -ForegroundColor Gray
Write-Host $_.ScriptStackTrace -ForegroundColor Gray
exit 1
}