DomainPasswordSpray日志IO异常处理:寻求Try/Catch替代方案
解决DomainPasswordSpray日志写入冲突的三种实用方案
先明确:之前脚本耗时翻倍是无效用户名引发AD查询延迟,和Try/Catch、错误重定向无关,当前核心问题是高频日志写入导致的文件占用异常。以下是比Try/Catch更高效的解决办法:
1. 异步批量日志队列(最优解)
用线程安全队列暂存日志,后台线程定期批量写入,彻底避免前台写文件的冲突:
# 初始化线程安全日志队列 $logQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new() # 启动后台写入线程 $logThread = [System.Threading.Thread]::new({ while ($true) { if ($logQueue.Count -gt 0) { $batchLogs = @() # 一次性取出队列中所有日志 while ($logQueue.TryDequeue([ref]$entry)) { $batchLogs += $entry } # 批量追加到文件,减少IO次数 Add-Content -Path "password_spray.log" -Value $batchLogs -ErrorAction SilentlyContinue } # 每500ms检查一次队列,可按需调整间隔 Start-Sleep -Milliseconds 500 } }) $logThread.IsBackground = $true $logThread.Start() # 喷洒逻辑中只需把日志丢进队列,不用直接写文件 $logMsg = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - 尝试用户:$targetUser,密码:$testPass" $logQueue.Enqueue($logMsg)
前台线程只做入队操作,几乎不占性能,后台批量写入彻底解决文件占用问题。
2. 带重试的独占文件锁
针对写入操作加独占锁,失败后短暂重试,比全局Try/Catch性能损耗小:
function Write-SafeLog { param( [string]$Message, [string]$LogPath ) $maxRetries = 3 $retryWait = 100 # 毫秒间隔 $writeSuccess = $false for ($i=0; $i -lt $maxRetries; $i++) { try { # 申请文件独占访问权,无共享 $fileStream = [System.IO.File]::Open($LogPath, [System.IO.FileMode]::Append, [System.IO.FileAccess]::Write, [System.IO.FileShare]::None) $streamWriter = [System.IO.StreamWriter]::new($fileStream) $streamWriter.WriteLine($Message) $streamWriter.Close() $fileStream.Close() $writeSuccess = $true break } catch [System.IO.IOException] { # 仅捕获文件占用异常,短暂重试 Start-Sleep -Milliseconds $retryWait } catch { # 其他异常直接抛出,不吞错 throw } } if (-not $writeSuccess) { Write-Warning "日志写入失败:$Message" } } # 调用示例 Write-SafeLog -Message "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - 尝试用户:$targetUser" -LogPath "password_spray.log"
只有写入失败时才重试,不会像全局Try/Catch那样拖慢整个脚本流程。
3. 线程独立日志文件(快速改造方案)
给每个并发线程分配单独的日志文件,脚本结束后合并:
# 每个线程用自身ID生成唯一日志文件名 $threadId = [System.Threading.Thread]::CurrentThread.ManagedThreadId $threadLogPath = "spray_log_thread_$threadId.txt" # 直接写入线程专属日志,无竞争 Add-Content -Path $threadLogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - 尝试用户:$targetUser" # 脚本收尾阶段合并所有日志 Get-ChildItem "spray_log_thread_*.txt" | Sort-Object CreationTime | Get-Content | Add-Content "spray_log_merged.txt" # 删除临时日志文件 Remove-Item "spray_log_thread_*.txt" -Force
这种方法无需复杂逻辑,快速解决冲突,合并日志的操作放在脚本最后,不影响喷洒流程性能。
内容的提问来源于stack exchange,提问作者Stumpy Jones
相关产品推荐
相关产品推荐

