# ============================================================ # Config # ============================================================ $ErrorActionPreference = "Stop" $isoPath = "D:\ae\iso\feiq_zh-cn_windows_10_business_editions_version_22h2_updated_oct_2025_x64_dvd_d4e92df7.iso" $index = 2 $baseDir = "D:\sysmbols_1405" $mountDir = Join-Path $baseDir "mount" $symbolDir = Join-Path $baseDir "symbols" $logDir = Join-Path $baseDir "symbol_logs" $symchk = "D:\ae\env\EWDK_vb_release_svc_prod1_19041_201201-2105\Program Files\Windows Kits\10\Debuggers\x64\symchk.exe" # true = symchk 输出更详细,但更慢 # false = 输出少一些 $verboseSymchk = $true # ============================================================ # Precomputed paths # ============================================================ $winDir = Join-Path $mountDir "Windows" $system32Dir = Join-Path $winDir "System32" $driversDir = Join-Path $system32Dir "drivers" $ntoskrnlPath = Join-Path $system32Dir "ntoskrnl.exe" $symbolPath = "srv*$symbolDir*https://msdl.microsoft.com/download/symbols" $coreSymbolListFile = Join-Path $logDir "core_symbol_files.txt" $missingSymbolListFile = Join-Path $logDir "core_symbol_missing.txt" $coreSymchkLogFile = Join-Path $logDir "core_symbols_symchk.log" # 只下载核心符号,不递归扫描整个 drivers 目录 $coreSymbolRelativePaths = @( # Kernel / HAL / debug transport "Windows\System32\ntoskrnl.exe" "Windows\System32\hal.dll" "Windows\System32\kdcom.dll" # Code integrity / crypto / security "Windows\System32\ci.dll" "Windows\System32\drivers\ksecdd.sys" "Windows\System32\drivers\ksecpkg.sys" "Windows\System32\drivers\cng.sys" # Win32 kernel side "Windows\System32\win32k.sys" "Windows\System32\win32kbase.sys" "Windows\System32\win32kfull.sys" # Plug and Play / ACPI / PCI "Windows\System32\drivers\ACPI.sys" "Windows\System32\drivers\pci.sys" "Windows\System32\drivers\msisadrv.sys" "Windows\System32\drivers\vdrvroot.sys" # Storage stack "Windows\System32\drivers\disk.sys" "Windows\System32\drivers\partmgr.sys" "Windows\System32\drivers\mountmgr.sys" "Windows\System32\drivers\volmgr.sys" "Windows\System32\drivers\volsnap.sys" "Windows\System32\drivers\classpnp.sys" "Windows\System32\drivers\storport.sys" "Windows\System32\drivers\storahci.sys" "Windows\System32\drivers\stornvme.sys" # File system stack "Windows\System32\drivers\ntfs.sys" "Windows\System32\drivers\fltmgr.sys" "Windows\System32\drivers\fileinfo.sys" # Network stack "Windows\System32\drivers\ndis.sys" "Windows\System32\drivers\NETIO.SYS" "Windows\System32\drivers\tcpip.sys" "Windows\System32\drivers\fwpkclnt.sys" # Driver framework "Windows\System32\drivers\Wdf01000.sys" "Windows\System32\drivers\WppRecorder.sys" # Graphics kernel "Windows\System32\drivers\dxgkrnl.sys" "Windows\System32\drivers\dxgmms2.sys" ) # ============================================================ # Helpers # ============================================================ function Ensure-Dirs { New-Item -ItemType Directory -Force -Path $baseDir | Out-Null New-Item -ItemType Directory -Force -Path $mountDir | Out-Null New-Item -ItemType Directory -Force -Path $symbolDir | Out-Null New-Item -ItemType Directory -Force -Path $logDir | Out-Null } function Test-WimMounted { param( [Parameter(Mandatory = $true)] [string]$MountDir ) $info = dism /Get-MountedWimInfo 2>&1 return ($info -match [regex]::Escape($MountDir)) } function Ensure-MountDirReady { if (Test-WimMounted -MountDir $mountDir) { Write-Host "WIM already mounted by DISM: $mountDir" if (-not (Test-Path -LiteralPath $ntoskrnlPath)) { throw "DISM 认为 WIM 已挂载,但找不到 ntoskrnl.exe:$ntoskrnlPath。请先执行 clean_env 或手动 dism /Unmount-Wim /Discard。" } return $true } if (Test-Path -LiteralPath $mountDir) { $items = Get-ChildItem -LiteralPath $mountDir -Force -ErrorAction SilentlyContinue if ($items.Count -gt 0) { Write-Host "Mount directory is not empty but DISM says not mounted. Cleaning: $mountDir" -ForegroundColor Yellow Get-ChildItem -LiteralPath $mountDir -Force | Remove-Item -Recurse -Force -ErrorAction Stop } } else { New-Item -ItemType Directory -Force -Path $mountDir | Out-Null } return $false } function Get-IsoRoot { param( [Parameter(Mandatory = $true)] [string]$ImagePath ) $diskImage = Get-DiskImage -ImagePath $ImagePath -ErrorAction SilentlyContinue if ($null -eq $diskImage -or -not $diskImage.Attached) { Write-Host "Mounting ISO..." Mount-DiskImage -ImagePath $ImagePath | Out-Null Start-Sleep -Seconds 2 } else { Write-Host "ISO already mounted." } $diskImage = Get-DiskImage -ImagePath $ImagePath $volume = $diskImage | Get-Volume | Where-Object { $_.DriveLetter } | Select-Object -First 1 if ($null -eq $volume) { throw "无法获取 ISO 盘符" } return "$($volume.DriveLetter):\" } function Show-Config { Write-Host "" Write-Host "========== Config ==========" Write-Host "isoPath = $isoPath" Write-Host "index = $index" Write-Host "baseDir = $baseDir" Write-Host "mountDir = $mountDir" Write-Host "symbolDir = $symbolDir" Write-Host "logDir = $logDir" Write-Host "symchk = $symchk" Write-Host "symbolPath = $symbolPath" Write-Host "ntoskrnl = $ntoskrnlPath" Write-Host "driversDir = $driversDir" Write-Host "============================" Write-Host "" } function Assert-BaseTools { if (-not (Test-Path -LiteralPath $isoPath)) { throw "ISO 文件不存在:$isoPath" } if (-not (Test-Path -LiteralPath $symchk)) { throw "symchk.exe 不存在:$symchk" } } function Assert-ImageReady { if (-not (Test-Path -LiteralPath $ntoskrnlPath)) { throw "离线 Windows 镜像还没有准备好,找不到:$ntoskrnlPath。请先执行 init_env。" } if (-not (Test-Path -LiteralPath $driversDir)) { throw "离线 Windows drivers 目录不存在:$driversDir。请先执行 init_env。" } } function Write-CoreSymbolFileList { Ensure-Dirs Assert-ImageReady $existing = New-Object System.Collections.Generic.List[string] $missing = New-Object System.Collections.Generic.List[string] foreach ($rel in $coreSymbolRelativePaths) { $fullPath = Join-Path $mountDir $rel if (Test-Path -LiteralPath $fullPath) { $existing.Add($fullPath) } else { $missing.Add($fullPath) } } if ($missing.Count -gt 0) { $missing | Set-Content -LiteralPath $missingSymbolListFile -Encoding UTF8 Write-Host "" Write-Host "部分核心文件不存在,已记录到:" -ForegroundColor Yellow Write-Host $missingSymbolListFile -ForegroundColor Yellow } if ($existing.Count -eq 0) { throw "核心符号文件列表为空,无法执行 symchk。" } $existing | Set-Content -LiteralPath $coreSymbolListFile -Encoding ASCII Write-Host "" Write-Host "Core symbol files: $($existing.Count)" Write-Host "Core symbol list : $coreSymbolListFile" return $coreSymbolListFile } function Invoke-SymChk { param( [Parameter(Mandatory = $true)] [string[]]$Arguments, [Parameter(Mandatory = $true)] [string]$LogFile ) Write-Host "" Write-Host "symchk arguments:" -ForegroundColor Cyan Write-Host ($Arguments -join " ") $oldErrorActionPreference = $ErrorActionPreference $hasNativeCommandPreference = Test-Path Variable:\PSNativeCommandUseErrorActionPreference $oldNativeCommandUseErrorActionPreference = $null if ($hasNativeCommandPreference) { $oldNativeCommandUseErrorActionPreference = $PSNativeCommandUseErrorActionPreference } $exitCode = $null try { # symchk 的详细信息可能写到 stderr。 # 这里临时放宽 ErrorActionPreference,避免正常 stderr 被 PowerShell 当成 NativeCommandError 中断。 $ErrorActionPreference = "Continue" if ($hasNativeCommandPreference) { $global:PSNativeCommandUseErrorActionPreference = $false } & $symchk @Arguments 2>&1 | ForEach-Object { $line = $_.ToString() Write-Host $line Add-Content ` -LiteralPath $LogFile ` -Value $line ` -Encoding UTF8 } $exitCode = $LASTEXITCODE } finally { $ErrorActionPreference = $oldErrorActionPreference if ($hasNativeCommandPreference) { $global:PSNativeCommandUseErrorActionPreference = $oldNativeCommandUseErrorActionPreference } } if ($exitCode -ne 0) { throw "symchk 执行失败,ExitCode=$exitCode,日志:$LogFile" } } # ============================================================ # Main functions # ============================================================ function init_env { Write-Host "" Write-Host "Initializing environment..." Ensure-Dirs Assert-BaseTools Show-Config Write-Host "[init] Getting ISO root..." $isoRoot = Get-IsoRoot -ImagePath $isoPath Write-Host "[init] Checking install.wim..." $wimFile = Join-Path $isoRoot "sources\install.wim" if (-not (Test-Path -LiteralPath $wimFile)) { throw "install.wim 不存在:$wimFile" } Write-Host "ISO Root: $isoRoot" Write-Host "WIM File: $wimFile" $alreadyMounted = Ensure-MountDirReady if (-not $alreadyMounted) { Write-Host "[init] Mounting Windows image, Index $index..." dism /Mount-Wim "/WimFile:$wimFile" "/Index:$index" "/MountDir:$mountDir" /ReadOnly if ($LASTEXITCODE -ne 0) { throw "DISM 挂载 install.wim 失败,ExitCode=$LASTEXITCODE" } Write-Host "[init] DISM mount finished." } else { Write-Host "Skip mounting. WIM is already mounted: $mountDir" } Write-Host "[init] Checking mounted image..." Assert-ImageReady Write-Host "[init] Writing core symbol file list..." Write-CoreSymbolFileList | Out-Null Write-Host "" Write-Host "init_env finished." -ForegroundColor Green } function exec { Write-Host "" Write-Host "Downloading core Windows symbols..." Ensure-Dirs Assert-BaseTools Assert-ImageReady Show-Config $listFile = Write-CoreSymbolFileList $coreFiles = Get-Content -LiteralPath $listFile | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if ($coreFiles.Count -eq 0) { throw "核心符号文件列表为空:$listFile" } if (Test-Path -LiteralPath $coreSymchkLogFile) { Remove-Item -LiteralPath $coreSymchkLogFile -Force } Write-Host "" Write-Host "Core files to process: $($coreFiles.Count)" Write-Host "Log file: $coreSymchkLogFile" Write-Host "" $current = 0 foreach ($file in $coreFiles) { $current++ Write-Host "============================================================" Write-Host "[$current/$($coreFiles.Count)] Checking symbols for:" -ForegroundColor Yellow Write-Host $file -ForegroundColor Yellow Write-Host "============================================================" if (-not (Test-Path -LiteralPath $file)) { Write-Host "File missing, skipped: $file" -ForegroundColor Red continue } $symchkArgs = @( "/if", $file "/s", $symbolPath "/od" "/ob" "/os" "/ot" ) if ($verboseSymchk) { $symchkArgs += "/v" } Invoke-SymChk ` -Arguments $symchkArgs ` -LogFile $coreSymchkLogFile Write-Host "" } Write-Host "" Write-Host "Done." -ForegroundColor Green Write-Host "Symbols saved to: $symbolDir" Write-Host "Logs saved to: $logDir" } function clean_env { Write-Host "" Write-Host "Cleaning up..." Get-Process symchk -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue if (Test-WimMounted -MountDir $mountDir) { Write-Host "Unmounting WIM: $mountDir" dism /Unmount-Wim "/MountDir:$mountDir" /Discard if ($LASTEXITCODE -ne 0) { throw "DISM 卸载 WIM 失败,ExitCode=$LASTEXITCODE" } Write-Host "WIM unmounted: $mountDir" } else { Write-Host "WIM is not mounted according to DISM: $mountDir" } Write-Host "Running DISM cleanup..." dism /Cleanup-Wim | Out-Host $diskImage = Get-DiskImage -ImagePath $isoPath -ErrorAction SilentlyContinue if ($null -ne $diskImage -and $diskImage.Attached) { Write-Host "Dismounting ISO..." Dismount-DiskImage -ImagePath $isoPath | Out-Null } else { Write-Host "ISO is not mounted." } Write-Host "Cleanup finished." -ForegroundColor Green } # ============================================================ # Run # ============================================================ function Test-Admin { $currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($currentIdentity) return $principal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) } if (-not (Test-Admin)) { Write-Host "错误:当前 PowerShell 不是管理员权限。" -ForegroundColor Red Write-Host "请右键 PowerShell / CLion,选择“以管理员身份运行”,然后重新执行脚本。" -ForegroundColor Yellow Read-Host "按回车退出" exit 1 } try { # init_env # exec clean_env } finally { Write-Host "" Read-Host "执行完毕,按回车退出" }