mirror of
https://github.com/NaC-L/Mergen.git
synced 2026-05-12 09:40:34 +00:00
205 lines
7.5 KiB
PowerShell
205 lines
7.5 KiB
PowerShell
param(
|
|
[string]$LifterPath = '',
|
|
[string[]]$Filter = @(),
|
|
[switch]$Validate
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$repo = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
$simpleDir = Join-Path $repo 'simple'
|
|
$diagPath = Join-Path $repo 'output_diagnostics.json'
|
|
$outputLl = Join-Path $repo 'output.ll'
|
|
$outputNoOptsLl = Join-Path $repo 'output_no_opts.ll'
|
|
|
|
function Resolve-LifterPath {
|
|
param([string]$Candidate)
|
|
|
|
if ($Candidate) {
|
|
if (!(Test-Path $Candidate)) {
|
|
throw "Lifter not found at '$Candidate'"
|
|
}
|
|
return $Candidate
|
|
}
|
|
|
|
$preferred = @(
|
|
(Join-Path $repo 'build_iced\lifter.exe'),
|
|
(Join-Path $repo 'build_zydis\lifter.exe')
|
|
)
|
|
|
|
foreach ($path in $preferred) {
|
|
if (Test-Path $path) {
|
|
return $path
|
|
}
|
|
}
|
|
|
|
throw "No lifter build found. Expected build_iced\\lifter.exe or build_zydis\\lifter.exe"
|
|
}
|
|
|
|
$lifter = Resolve-LifterPath $LifterPath
|
|
|
|
$targets = @(
|
|
@{ name = 'simple_unprotected'; exe = (Join-Path $simpleDir 'simple_target.exe'); addr = '0x1400113CA'; timeoutSec = 30; required = $false },
|
|
@{ name = 'simple_vmp381_one_vm'; exe = (Join-Path $simpleDir 'protected381\simple_target_one_vm.vmp38.exe'); addr = '0x1400113CA'; timeoutSec = 60; required = $true },
|
|
@{ name = 'simple_vmp381_full'; exe = (Join-Path $simpleDir 'protected381\simple_target.vmp.exe'); addr = '0x1400113CA'; timeoutSec = 60; required = $true },
|
|
@{ name = 'simple_vmp36'; exe = (Join-Path $simpleDir 'protected\simple_target_protected.vmp.exe'); addr = '0x14009E2E1'; timeoutSec = 90; required = $false }
|
|
)
|
|
|
|
if ($Filter.Count -gt 0) {
|
|
$targets = @(
|
|
$targets | Where-Object {
|
|
$target = $_
|
|
($Filter | Where-Object { $target.name -like ('*' + $_ + '*') }).Count -gt 0
|
|
}
|
|
)
|
|
}
|
|
|
|
if ($targets.Count -eq 0) {
|
|
throw 'No VMP targets matched the requested filter.'
|
|
}
|
|
|
|
if ($Validate -and (@($targets | Where-Object { $_.required })).Count -eq 0) {
|
|
throw 'Validation requires at least one required VMP 3.8.x target in the filtered target set.'
|
|
}
|
|
|
|
|
|
function Run-Target($target) {
|
|
if (!(Test-Path $target.exe)) {
|
|
return [pscustomobject]@{
|
|
Name = $target.name; Required = [bool]$target.required; Status = 'missing'; WallMs = $null; TotalMs = $null
|
|
PeSetup = $null; SignatureSearch = $null; Lift = $null; WriteUnoptIr = $null
|
|
Optimization = $null; WriteOptIr = $null; Errors = $null; Warnings = $null
|
|
BlocksAttempted = $null; BlocksCompleted = $null; Note = 'binary not found'
|
|
}
|
|
}
|
|
|
|
Remove-Item $diagPath, $outputLl, $outputNoOptsLl -Force -ErrorAction SilentlyContinue
|
|
|
|
$job = Start-Job -ScriptBlock {
|
|
param($repoPath, $lifterPath, $exePath, $addr)
|
|
Set-Location $repoPath
|
|
& $lifterPath $exePath $addr *> $null
|
|
return $LASTEXITCODE
|
|
} -ArgumentList $repo, $lifter, $target.exe, $target.addr
|
|
|
|
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
|
$completed = Wait-Job $job -Timeout $target.timeoutSec
|
|
$sw.Stop()
|
|
|
|
if ($null -eq $completed) {
|
|
Stop-Job $job | Out-Null
|
|
Remove-Job $job | Out-Null
|
|
return [pscustomobject]@{
|
|
Name = $target.name; Required = [bool]$target.required; Status = 'timeout'; WallMs = $sw.Elapsed.TotalMilliseconds; TotalMs = $null
|
|
PeSetup = $null; SignatureSearch = $null; Lift = $null; WriteUnoptIr = $null
|
|
Optimization = $null; WriteOptIr = $null; Errors = $null; Warnings = $null
|
|
BlocksAttempted = $null; BlocksCompleted = $null; Note = ('timed out after ' + $target.timeoutSec + 's')
|
|
}
|
|
}
|
|
|
|
$exitCode = Receive-Job $job
|
|
Remove-Job $job | Out-Null
|
|
|
|
if ($exitCode -ne 0) {
|
|
return [pscustomobject]@{
|
|
Name = $target.name; Required = [bool]$target.required; Status = 'failed'; WallMs = $sw.Elapsed.TotalMilliseconds; TotalMs = $null
|
|
PeSetup = $null; SignatureSearch = $null; Lift = $null; WriteUnoptIr = $null
|
|
Optimization = $null; WriteOptIr = $null; Errors = $null; Warnings = $null
|
|
BlocksAttempted = $null; BlocksCompleted = $null; Note = ('exit code ' + $exitCode)
|
|
}
|
|
}
|
|
|
|
if (!(Test-Path $diagPath) -or !(Test-Path $outputLl) -or !(Test-Path $outputNoOptsLl)) {
|
|
return [pscustomobject]@{
|
|
Name = $target.name; Required = [bool]$target.required; Status = 'missing_output'; WallMs = $sw.Elapsed.TotalMilliseconds; TotalMs = $null
|
|
PeSetup = $null; SignatureSearch = $null; Lift = $null; WriteUnoptIr = $null
|
|
Optimization = $null; WriteOptIr = $null; Errors = $null; Warnings = $null
|
|
BlocksAttempted = $null; BlocksCompleted = $null; Note = 'missing output_diagnostics.json/output.ll/output_no_opts.ll'
|
|
}
|
|
}
|
|
|
|
$json = Get-Content $diagPath -Raw | ConvertFrom-Json
|
|
$errorCount = 0
|
|
$warningCount = 0
|
|
if ($json.summary) {
|
|
$errorCount = [int]$json.summary.error
|
|
$warningCount = [int]$json.summary.warning
|
|
}
|
|
|
|
$note = ''
|
|
if ($json.lift_stats.blocks_completed -eq 0) {
|
|
$note = '0 completed blocks; inspect output.ll before trusting result'
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
Name = $target.name
|
|
Required = [bool]$target.required
|
|
Status = 'ok'
|
|
WallMs = $sw.Elapsed.TotalMilliseconds
|
|
TotalMs = [double]$json.total_ms
|
|
PeSetup = [double]$json.profile.pe_setup
|
|
SignatureSearch = [double]$json.profile.signature_search
|
|
Lift = [double]$json.profile.lift
|
|
WriteUnoptIr = [double]$json.profile.write_unopt_ir
|
|
Optimization = [double]$json.profile.optimization
|
|
WriteOptIr = [double]$json.profile.write_opt_ir
|
|
Errors = $errorCount
|
|
Warnings = $warningCount
|
|
BlocksAttempted = [int]$json.lift_stats.blocks_attempted
|
|
BlocksCompleted = [int]$json.lift_stats.blocks_completed
|
|
Note = $note
|
|
}
|
|
}
|
|
|
|
$results = foreach ($target in $targets) {
|
|
Write-Host ('Profiling ' + $target.name + ' @ ' + $target.addr)
|
|
Run-Target $target
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host '========== SIMPLE TARGET PROFILE =========='
|
|
foreach ($r in $results) {
|
|
$tier = if ($r.Required) { 'gate' } else { 'best-effort' }
|
|
if ($r.Status -eq 'ok') {
|
|
$noteSuffix = ''
|
|
if ($r.Note) {
|
|
$noteSuffix = (' note=' + $r.Note)
|
|
}
|
|
Write-Host ('{0,-22} tier={1,-11} wall={2,10:F3} ms total={3,10:F3} ms opt={4,10:F3} ms lift={5,8:F3} ms sig={6,8:F3} ms write_u={7,8:F3} ms write_o={8,8:F3} ms errs={9} warns={10} blocks={11}/{12}{13}' -f $r.Name, $tier, $r.WallMs, $r.TotalMs, $r.Optimization, $r.Lift, $r.SignatureSearch, $r.WriteUnoptIr, $r.WriteOptIr, $r.Errors, $r.Warnings, $r.BlocksCompleted, $r.BlocksAttempted, $noteSuffix)
|
|
} else {
|
|
Write-Host ('{0,-22} tier={1,-11} status={2} note={3} wall={4}' -f $r.Name, $tier, $r.Status, $r.Note, $r.WallMs)
|
|
}
|
|
}
|
|
|
|
if ($Validate) {
|
|
$hasHardRegression = {
|
|
param($result)
|
|
return $result.Status -ne 'ok' -or $result.Errors -gt 0 -or
|
|
($result.Status -eq 'ok' -and $result.BlocksCompleted -le 0)
|
|
}
|
|
$failures = @(
|
|
$results | Where-Object { $_.Required -and (& $hasHardRegression $_) }
|
|
)
|
|
$bestEffortIssues = @(
|
|
$results | Where-Object { -not $_.Required -and (& $hasHardRegression $_) }
|
|
)
|
|
if ($bestEffortIssues.Count -gt 0) {
|
|
Write-Host ''
|
|
Write-Host 'Best-effort VMP targets with issues:'
|
|
foreach ($issue in $bestEffortIssues) {
|
|
Write-Host ('- {0}: status={1} errors={2} note={3}' -f $issue.Name, $issue.Status, $issue.Errors, $issue.Note)
|
|
}
|
|
}
|
|
if ($failures.Count -gt 0) {
|
|
Write-Host ''
|
|
Write-Host 'VMP validation failed for required targets:'
|
|
foreach ($failure in $failures) {
|
|
Write-Host ('- {0}: status={1} errors={2} note={3}' -f $failure.Name, $failure.Status, $failure.Errors, $failure.Note)
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host ('VMP validation passed for {0} required target(s).' -f (@($results | Where-Object { $_.Required })).Count)
|
|
}
|