AI工具:文件签名校验工具

张开发
2026/4/6 3:26:22 15 分钟阅读

分享文章

AI工具:文件签名校验工具
AI模型Deepseek开发方法AI人工使用方法管理员运行powershell→cd 脚本目录→./checkFile.ps1注意事项输入文件是文章《AI工具ProcessMonitor监控程序安装工具》的结果文件https://blog.csdn.net/humors221/article/details/159733489**解析文件内容**- 跳过第一行假定是标题行。- 每行取第一个逗号之前的内容并去除首尾双引号和空格。- 收集所有路径去重。代码# .SYNOPSIS 使用 Sigcheck 批量校验文件安全性VirusTotal .DESCRIPTION 读取当前目录下所有包含“扫描件”前缀的 .txt 文件提取文件路径 调用 Sigcheck -accepteula -nobanner -vt -vr 查询 VirusTotal 生成 CSV 报告和 TXT 日志。自动请求管理员权限并规避系统目录权限问题。 .NOTES 版本1.5 (彻底解决权限拒绝问题输出到桌面) # # 1. 自动请求管理员权限 $currentPrincipal New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) $isAdmin $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host 当前非管理员权限尝试以管理员身份重启脚本... -ForegroundColor Yellow Start-Sleep -Seconds 1 $scriptPath $MyInvocation.MyCommand.Path $arguments -NoProfile -ExecutionPolicy Bypass -File $scriptPath try { Start-Process powershell.exe -Verb RunAs -ArgumentList $arguments exit } catch { Write-Host 请求管理员权限失败请右键点击 PowerShell 选择“以管理员身份运行”后重新执行脚本。 -ForegroundColor Red Read-Host 按 Enter 退出 exit 1 } } Write-Host 已获得管理员权限继续执行... -ForegroundColor Green # 2. 检查脚本是否位于系统保护目录 $scriptRoot Split-Path -Parent $MyInvocation.MyCommand.Path $protectedPaths ( $env:windir, $env:ProgramFiles, $env:ProgramFiles (x86), $env:windir\System32, $env:windir\SysWOW64 ) $isProtected $false foreach ($p in $protectedPaths) { if ($p -and $scriptRoot.StartsWith($p, [StringComparison]::OrdinalIgnoreCase)) { $isProtected $true break } } if ($isProtected) { Write-Host 错误脚本位于系统受保护目录中$scriptRoot -ForegroundColor Red Write-Host 请将脚本移动到一个普通用户目录如桌面或文档后重新运行。 -ForegroundColor Yellow Read-Host 按 Enter 退出 exit 1 } # 3. 配置输出路径使用桌面避免权限问题 $desktop [Environment]::GetFolderPath(Desktop) $logDir Join-Path $desktop SigcheckLogs_$((Get-Date -Format yyyyMMdd)) $reportDir $logDir # 日志和报告放在同一个目录下 $toolsDir Join-Path $scriptRoot tools $sigcheckExe Join-Path $toolsDir sigcheck.exe $sigcheckUrl https://live.sysinternals.com/sigcheck.exe $sigcheckUrlBackup https://download.sysinternals.com/files/Sigcheck.zip $timestamp Get-Date -Format yyyyMMdd_HHmmss $logFile Join-Path $logDir CheckLog_$timestamp.txt $reportFile Join-Path $reportDir VirusTotalReport_$timestamp.csv $script:results () $script:totalFiles 0 $script:checkedFiles 0 $script:errorFiles 0 $script:maliciousFiles 0 # 4. 辅助函数 function Write-Log { param([string]$Message, [string]$Level INFO) $logLine [$(Get-Date -Format yyyy-MM-dd HH:mm:ss)] [$Level] $Message switch ($Level) { ERROR { Write-Host $logLine -ForegroundColor Red } WARN { Write-Host $logLine -ForegroundColor Yellow } OK { Write-Host $logLine -ForegroundColor Green } default { Write-Host $logLine } } # 确保日志文件可写 try { Add-Content -Path $logFile -Value $logLine -Encoding UTF8 -ErrorAction Stop } catch { Write-Host 无法写入日志文件: $_ -ForegroundColor Red } } function Ensure-Sigcheck { if (-not (Test-Path $toolsDir)) { try { New-Item -ItemType Directory -Path $toolsDir -Force | Out-Null } catch { Write-Log 无法创建 tools 目录: $_ ERROR throw 请在脚本所在目录手动创建 tools 文件夹 } } if (Test-Path $sigcheckExe) { Write-Log Sigcheck 已存在: $sigcheckExe return $true } Write-Log Sigcheck 未找到正在下载... WARN try { Invoke-WebRequest -Uri $sigcheckUrl -OutFile $sigcheckExe -UseBasicParsing -ErrorAction Stop if (Test-Path $sigcheckExe) { Write-Log Sigcheck 下载成功 OK Unblock-File -Path $sigcheckExe -ErrorAction SilentlyContinue return $true } } catch { Write-Log 主地址下载失败: $_ WARN try { $zipPath Join-Path $toolsDir sigcheck.zip Invoke-WebRequest -Uri $sigcheckUrlBackup -OutFile $zipPath -UseBasicParsing -ErrorAction Stop Expand-Archive -Path $zipPath -DestinationPath $toolsDir -Force Remove-Item $zipPath -Force if (Test-Path $sigcheckExe) { Write-Log Sigcheck 下载成功 (备用地址) OK Unblock-File -Path $sigcheckExe -ErrorAction SilentlyContinue return $true } else { throw 解压后未找到 sigcheck.exe } } catch { Write-Log 备用地址下载失败: $_ ERROR throw 无法获取 sigcheck.exe请手动下载并放入 $toolsDir } } } function Get-FileListFromScannedTxt { # 查找当前目录下所有包含“扫描件”的 .txt 文件使用 .NET 方法避免 PowerShell 提供程序权限问题 $txtFiles () try { $allFiles [System.IO.Directory]::GetFiles($scriptRoot, *.txt) foreach ($file in $allFiles) { if ([System.IO.Path]::GetFileName($file) -like *扫描件*) { $txtFiles $file } } } catch { Write-Log 扫描 txt 文件失败: $_ ERROR return () } if ($txtFiles.Count -eq 0) { Write-Log 未找到包含“扫描件”的 .txt 文件 ERROR return () } $allPaths () foreach ($txt in $txtFiles) { Write-Log 读取文件列表: $([System.IO.Path]::GetFileName($txt)) try { $lines [System.IO.File]::ReadAllLines($txt, [System.Text.Encoding]::UTF8) } catch { Write-Log 读取文件 $txt 失败: $_ ERROR continue } for ($i 1; $i -lt $lines.Length; $i) { $line $lines[$i].Trim() if ([string]::IsNullOrEmpty($line)) { continue } $firstCommaIndex $line.IndexOf(,) if ($firstCommaIndex -ge 0) { $pathPart $line.Substring(0, $firstCommaIndex) } else { $pathPart $line } $pathPart $pathPart.Trim().Trim() if (-not [string]::IsNullOrEmpty($pathPart)) { $allPaths $pathPart } } } $allPaths $allPaths | Select-Object -Unique Write-Log 从 txt 文件中提取到 $($allPaths.Count) 个路径去重后 $existingFiles () foreach ($p in $allPaths) { $fullPath $p if (-not [System.IO.Path]::IsPathRooted($p)) { $fullPath [System.IO.Path]::Combine($scriptRoot, $p) } # 拒绝注册表等非文件系统路径 if ($fullPath -match ^[A-Za-z]:.* -and $fullPath -notmatch ^[A-Za-z]:[\\/]) { Write-Log 跳过非文件系统路径: $p WARN $script:results [PSCustomObject]{ FilePath $p Status Invalid path type Verified N/A Permalink Error Not a file system path } $script:errorFiles continue } # 使用 .NET 方法检查文件存在性不依赖 PowerShell 提供程序 if ([System.IO.File]::Exists($fullPath)) { $existingFiles $fullPath } else { $errMsg File does not exist if ([System.IO.Directory]::Exists($fullPath)) { $errMsg Is a directory Write-Log 路径指向目录跳过: $p WARN } else { Write-Log 文件不存在跳过: $p WARN } $script:results [PSCustomObject]{ FilePath $p Status File not found Verified N/A Permalink Error $errMsg } $script:errorFiles } } Write-Log 有效文件数: $($existingFiles.Count) return $existingFiles } function Invoke-SigcheckScan { param([string]$FilePath) $arguments -accepteula -nobanner -vt -vr $FilePath Write-Log 扫描文件: $FilePath -Level INFO try { $tempOut [System.IO.Path]::GetTempFileName() $tempErr [System.IO.Path]::GetTempFileName() $process Start-Process -FilePath $sigcheckExe -ArgumentList $arguments -NoNewWindow -Wait -PassThru -RedirectStandardOutput $tempOut -RedirectStandardError $tempErr $stdout [System.IO.File]::ReadAllText($tempOut, [System.Text.Encoding]::UTF8) $stderr [System.IO.File]::ReadAllText($tempErr, [System.Text.Encoding]::UTF8) [System.IO.File]::Delete($tempOut) [System.IO.File]::Delete($tempErr) if ($process.ExitCode -ne 0 -and -not [string]::IsNullOrEmpty($stderr)) { throw Sigcheck 执行错误 (ExitCode: $($process.ExitCode)): $stderr } $verified Unknown $status Unknown $permalink $lines $stdout -split rn foreach ($line in $lines) { if ($line -match Verified:\s*(.)) { $verified $matches[1].Trim() } elseif ($line -match VirusTotal:\s*(.)) { $vtText $matches[1].Trim() if ($vtText -eq No threat found) { $status Clean } elseif ($vtText -match Threat found:\s*(.)) { $threatName $matches[1].Trim() $status Malicious ($threatName) $script:maliciousFiles } else { $status $vtText } } elseif ($line -match Permalink:\s*(https?://.)) { $permalink $matches[1].Trim() } } if ($status -eq Unknown) { $status No VirusTotal result Write-Log 未获取到 VirusTotal 结果 WARN } Write-Log 结果 - Verified: $verified, Status: $status OK return [PSCustomObject]{ FilePath $FilePath Status $status Verified $verified Permalink $permalink Error } } catch { Write-Log 扫描文件失败 [$FilePath]: $_ ERROR $script:errorFiles return [PSCustomObject]{ FilePath $FilePath Status Scan failed Verified N/A Permalink Error $_.Exception.Message } } } # 5. 主流程 try { # 创建输出目录桌面 if (-not (Test-Path $logDir)) { try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null Write-Log 创建输出目录: $logDir OK } catch { Write-Log 无法创建输出目录 $logDir : $_ ERROR throw 无法创建输出目录请检查桌面写入权限 } } Write-Log 脚本开始 INFO Write-Log 脚本位置: $scriptRoot INFO Write-Log 输出目录: $logDir INFO Ensure-Sigcheck $fileList Get-FileListFromScannedTxt $script:totalFiles $fileList.Count Write-Log 待检查文件总数: $script:totalFiles if ($script:totalFiles -eq 0) { Write-Log 没有有效文件可检查脚本结束。 WARN exit 0 } $current 0 foreach ($file in $fileList) { $current Write-Progress -Activity VirusTotal 扫描 -Status 进度 $current / $script:totalFiles -PercentComplete (($current / $script:totalFiles) * 100) $result Invoke-SigcheckScan -FilePath $file $script:results $result if ($result.Status -like Malicious*) { $script:maliciousFiles } if ($result.Status -eq Scan failed -or $result.Error) { $script:errorFiles } $script:checkedFiles Start-Sleep -Milliseconds 500 } $script:results | Export-Csv -Path $reportFile -NoTypeInformation -Encoding UTF8 Write-Log 报告已导出: $reportFile OK $summary 执行摘要 总文件数去重后: $($script:totalFiles) 成功扫描: $($script:checkedFiles) 扫描失败: $($script:errorFiles) 检测到恶意文件: $($script:maliciousFiles) 报告位置: $reportFile 日志位置: $logFile Write-Log $summary Write-Host $summary -ForegroundColor Cyan Write-Log 脚本结束 OK } catch { Write-Log 脚本发生致命错误: $_ ERROR Write-Host 详细错误信息: $($_.Exception.ToString()) -ForegroundColor Red Write-Host 请尝试以下解决方案 -ForegroundColor Yellow Write-Host 1. 将脚本移动到桌面或文档目录后重新运行 -ForegroundColor Yellow Write-Host 2. 暂时关闭 Windows Defender 的“受文件夹保护”功能 -ForegroundColor Yellow Write-Host 3. 手动下载 sigcheck.exe 并放入脚本所在目录的 tools 文件夹 -ForegroundColor Yellow Read-Host 按 Enter 退出 exit 1 }介绍以下是对附件中 PowerShell 脚本的**详细解释**包括设计目标、工作流程、关键函数、错误处理机制及使用注意事项。## 一、脚本总体目标该脚本用于**批量检查文件的安全性**。它从当前目录下所有文件名包含 扫描件 的 .txt 文件中提取文件路径然后调用微软 Sysinternals 工具 **sigcheck.exe** 将每个文件上传至 VirusTotal 进行扫描最后生成 CSV 报告和 TXT 日志。脚本自动请求管理员权限并规避系统目录权限问题将输出保存到桌面同时使用 .NET 方法替代部分 PowerShell 文件操作以减少权限异常。---## 二、整体结构概览脚本分为五个主要部分1. **自动请求管理员权限**2. **检查脚本是否位于系统保护目录**3. **配置输出路径强制输出到桌面**4. **辅助函数定义**日志、sigcheck 下载、文件列表提取、扫描执行5. **主流程**执行扫描、输出报告---## 三、详细分块解释### 1. 管理员权限处理powershell$currentPrincipal New-Object Security.Principal.WindowsPrincipal(...)$isAdmin $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)if (-not $isAdmin) { ... }- 通过 Windows 安全主体检查当前进程是否具有管理员权限。- 如果没有则使用 Start-Process -Verb RunAs 以管理员身份重新启动脚本自身并传递 -NoProfile -ExecutionPolicy Bypass -File 参数确保脚本能够执行。- 原进程退出新进程获得提升的权限。**作用**许多文件操作尤其是访问某些系统目录或运行 sigcheck 的某些功能需要管理员权限提前提升可避免运行时“拒绝访问”错误。### 2. 保护系统目录检测powershell$protectedPaths ($env:windir, $env:ProgramFiles, ...)if ($scriptRoot.StartsWith($p, [StringComparison]::OrdinalIgnoreCase)) { ... }- 如果脚本本身位于 C:\Windows、C:\Program Files 等系统受保护目录则直接报错退出。- **原因**在这些目录下运行脚本可能触发 Windows 的“受保护文件夹”或 UAC 限制导致文件读写失败。提示用户将脚本移到桌面或文档目录。### 3. 输出路径配置避免权限问题powershell$desktop [Environment]::GetFolderPath(Desktop)$logDir Join-Path $desktop SigcheckLogs_$(Get-Date -Format yyyyMMdd)$toolsDir Join-Path $scriptRoot tools- **日志和报告**不再写入脚本所在目录而是强制写入**当前用户的桌面**下的 SigcheckLogs_YYYYMMDD 文件夹。桌面通常拥有完全写入权限避免因脚本所在目录如网络位置、只读介质权限不足而失败。- **sigcheck 工具**仍然保存在脚本所在目录的 tools 子文件夹中便于携带和管理。### 4. 辅助函数详解#### 4.1 Write-Log- 功能同时向控制台带颜色和日志文件输出消息。- 日志文件路径$logDir\CheckLog_时间戳.txt。- 写入失败时仅在控制台显示错误不中断脚本。#### 4.2 Ensure-Sigcheck- 检查 $toolsDir\sigcheck.exe 是否存在。- 若不存在依次尝试- 从 https://live.sysinternals.com/sigcheck.exe 直接下载。- 若失败下载 Sigcheck.zip 备用包并解压。- 下载成功后执行 Unblock-File 解除可能被系统标记的“来自互联网”锁定。- 最终若仍无 sigcheck.exe抛出终止脚本的异常。#### 4.3 Get-FileListFromScannedTxt这是核心的数据准备函数分为以下步骤1. **查找输入文件**使用 .NET 方法 [System.IO.Directory]::GetFiles 扫描脚本所在目录下的所有 .txt 文件筛选出文件名包含 扫描件 的文件。*为何不用 Get-ChildItem* 因为 .NET 方法绕过了 PowerShell 的文件系统提供程序在某些权限受限环境下更可靠。2. **解析文件内容**- 跳过第一行假定是标题行。- 每行取第一个逗号之前的内容并去除首尾双引号和空格。- 收集所有路径去重。3. **路径有效性检查**- 将相对路径转换为绝对路径基于脚本所在目录。- 使用正则拒绝注册表路径如 HKLM:\...或其他非文件系统路径。- 使用 [System.IO.File]::Exists 检查是否为**文件**自动排除目录。- 不存在的文件或目录被记录到 $script:results 中状态为 File not found并计入错误计数。4. **返回有效文件列表**绝对路径数组。#### 4.4 Invoke-SigcheckScan- **参数**-FilePath要扫描的文件路径。- **构造 sigcheck 命令**-accepteula -nobanner -vt -vr 文件路径- -accepteula自动接受许可协议避免交互。- -nobanner隐藏启动横幅。- -vt -vr提交文件到 VirusTotal 并获取详细报告。- **执行与输出捕获**- 使用 Start-Process 配合 -RedirectStandardOutput 和 -RedirectStandardError 将输出重定向到临时文件。- 临时文件通过 [System.IO.Path]::GetTempFileName() 生成避免命名冲突。- 读取输出后立即删除临时文件。- **解析输出**- 正则匹配 Verified:签名验证状态、VirusTotal:威胁检测结果、Permalink:VirusTotal 报告链接。- 若 VirusTotal: 值为 No threat found状态设为 Clean若包含 Threat found:则提取威胁名称状态设为 Malicious (...)。- **返回对象**包含文件路径、状态、签名验证结果、报告链接、错误信息若有。- **错误处理**任何执行或解析失败都会返回 Scan failed 状态并递增错误计数。### 5. 主流程powershelltry {# 创建桌面输出目录# 调用 Ensure-Sigcheck# 获取有效文件列表# 循环调用 Invoke-SigcheckScan显示进度条# 导出 CSV 报告# 输出执行摘要}catch {# 捕获顶层异常显示详细错误信息和解决建议}- **进度条**使用 Write-Progress 显示实时进度。- **报告导出**将所有结果对象通过 Export-Csv 保存为 UTF-8 编码的 CSV 文件。- **统计信息**总文件数、成功扫描数、失败数、恶意文件数并输出到控制台和日志。---## 四、安全性与健壮性设计| 设计点 | 目的 ||--------|------|| 管理员权限自动提升 | 避免因权限不足导致操作失败 || 禁止脚本位于系统目录 | 防止受保护路径触发权限错误 || 输出目录固定在桌面 | 保证写入权限避免因脚本所在目录只读或受限而失败 || 使用 .NET 方法替代部分 PowerShell cmdlet | 绕过 PowerShell 文件系统提供程序的额外权限检查提高兼容性 || 双重下载源 手动下载提示 | 确保 sigcheck 工具能够获取避免因网络问题阻塞 || 详细的错误捕获与日志 | 便于用户定位问题如某个文件扫描失败的原因 || 临时文件使用系统临时路径 | 避免在当前目录遗留垃圾文件 || 请求之间延迟 500ms | 防止短时间内发送大量文件到 VirusTotal 被限流 |---## 五、输入文件格式要求脚本期望的 .txt 文件文件名需包含 扫描件内容示例csvPath,CommentC:\Test\file1.exe,备注1..\relative\file2.dll,D:\Data\document.pdf- **第一行会被跳过**通常为标题行。- **每行第一个逗号之前的部分**被视为文件路径支持绝对路径或相对路径相对于脚本所在目录。- 路径可以用双引号包围脚本会自动去除。- 空行被忽略。---## 六、输出示例### 控制台输出部分[2026-04-03 08:00:00] [INFO] 脚本开始 [2026-04-03 08:00:00] [INFO] 脚本位置: C:\Users\Admin\Desktop\FileCheck[2026-04-03 08:00:00] [INFO] 输出目录: C:\Users\Admin\Desktop\SigcheckLogs_20260403[2026-04-03 08:00:01] [OK] Sigcheck 已存在: C:\Users\Admin\Desktop\FileCheck\tools\sigcheck.exe[2026-04-03 08:00:02] [INFO] 读取文件列表: 扫描件列表.txt[2026-04-03 08:00:02] [INFO] 从 txt 文件中提取到 10 个路径去重后[2026-04-03 08:00:02] [INFO] 有效文件数: 8[2026-04-03 08:00:03] [INFO] 扫描文件: C:\Test\file1.exe[2026-04-03 08:00:10] [OK] 结果 - Verified: Signed, Status: Clean... 执行摘要 总文件数去重后: 8成功扫描: 7扫描失败: 1检测到恶意文件: 0报告位置: C:\Users\Admin\Desktop\SigcheckLogs_20260403\VirusTotalReport_20260403_080003.csv日志位置: C:\Users\Admin\Desktop\SigcheckLogs_20260403\CheckLog_20260403_080003.txt### CSV 报告内容示例| FilePath | Status | Verified | Permalink | Error ||----------|--------|----------|-----------|-------|| C:\Test\file1.exe | Clean | Signed | https://... | || D:\bad.exe | Malicious (Trojan:Win32/Wacatac) | Unsigned | https://... | || ..\notexist.dll | File not found | N/A | | File does not exist |---## 七、注意事项与使用建议1. **网络要求**需要能够访问 live.sysinternals.com 和 VirusTotal API。如果处于企业内网或需要代理可能需要提前下载 sigcheck.exe 并放入 tools 文件夹。2. **VirusTotal 限制**免费 API 有速率限制每分钟最多 4 次查询。脚本中已加入 Start-Sleep -Milliseconds 500但若文件数量较多 100建议适当增加延迟或分批执行。3. **杀毒软件干扰**某些安全软件可能会阻止 sigcheck.exe 访问网络或上传文件。可临时添加排除项。4. **签名验证**Verified 字段可能的值包括 Signed、Unsigned、Expired、Unknown 等取决于文件数字签名状态。5. **错误处理**脚本遇到未捕获的异常如无法创建桌面目录时会提示手动解决建议不会自动重试。---## 八、总结该脚本是一个**生产级文件安全批量检查工具**充分利用了 PowerShell 的自动化能力与 Sysinternals sigcheck 的 VirusTotal 集成。其代码设计充分考虑了 Windows 权限模型、文件系统访问限制、网络不稳定等实际问题通过多层错误处理和降级方案提高了鲁棒性。对于需要定期对大量文件进行病毒扫描的安全运维人员该脚本可直接使用或稍作定制如修改输入文件前缀、输出路径、扫描频率等。

更多文章