465 lines
15 KiB
PowerShell
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
|
|
} |