499 lines
14 KiB
PowerShell
499 lines
14 KiB
PowerShell
# ============================================================
|
||
# 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 "执行完毕,按回车退出"
|
||
} |