170 lines
8.7 KiB
PowerShell
170 lines
8.7 KiB
PowerShell
#requires -Version 5.1
|
||||
|
|
<#
|
|||
|
|
.SYNOPSIS
|
|||
|
|
x86emu32-coverage — x86emu32 解释器覆盖率(docs/RE/test-cases/x86emu32-coverage.md)
|
|||
|
|
|
|||
|
|
.DESCRIPTION
|
|||
|
|
两层验证:
|
|||
|
|
|
|||
|
|
1) 静态枚举(源码侧):
|
|||
|
|
解析 CODE/UTSM/pe/x86emu32.c,统计 switch case 0xNN 标签数
|
|||
|
|
(= 已实现操作码处理分支),并校验 hello32.exe 头
|
|||
|
|
(machine=0x14C, optional magic=0x10B, 确为 PE32 目标样本)。
|
|||
|
|
|
|||
|
|
2) 动态执行(QEMU 侧):
|
|||
|
|
正常启动 firstInit="1\n0" → cmd.elf,AUTOEXEC.BAT 自动执行:
|
|||
|
|
pe HELLO32.EXE → PE32 走 x86emu32 解释器
|
|||
|
|
exit → 返回 DSK → desktop
|
|||
|
|
断言锚点(BUG-20260801-007 已修复,全链路硬断言):
|
|||
|
|
[cmd] PE run begin / [cmd] cmdline: HELLO32.EXE
|
|||
|
|
[PE] running PE32 via x86emu32 (解释器接管证据)
|
|||
|
|
Hello from PE32! (解释执行 + shim IAT 拦截证据)
|
|||
|
|
[cmd] PE run end / [DSK] cmd.elf returned
|
|||
|
|
已知系统侧阻塞(2026-08-04 monitor 取证定案):末锚点当前必缺失——
|
|||
|
|
cmd run_autoexec 共享 f32_data 缓冲被首行 pe 文件加载覆盖,exit 行
|
|||
|
|
丢失,cmd 停留交互主循环(无异常、无三重故障,guest 存活轮询键盘)。
|
|||
|
|
解释器与 shim 链路本身无恙;待 cmd 侧修复后末锚点自动转绿。
|
|||
|
|
注意:[PE] PE32 (32-bit) 仅 pe_load_image(PE32+ 原生路径)输出,
|
|||
|
|
解释器路径(pe32_load_into)不打印该行,不可作锚点。
|
|||
|
|
禁止锚点:
|
|||
|
|
[PE32] emulator error / [PE] unimplemented import /
|
|||
|
|
[PE] unimplemented API called / [PANIC]
|
|||
|
|
(出现 unimplemented 即说明 hello32 触到未实现指令/导入,覆盖率倒退)
|
|||
|
|
|
|||
|
|
历史:BUG-20260801-007(shim fn32 全缺,fn64 回退把 guest 指针
|
|||
|
|
0x401017 当 host 指针解引用 → #PF → 三重故障)曾截断于首个 shim
|
|||
|
|
调用;修复后全链路可断言。故障在 host 侧 shim 而非解释器:
|
|||
|
|
0x68 push / FF /2 call [disp32] / IAT 合成地址拦截链路均正确,
|
|||
|
|
故"无 emulator error"仍为有效覆盖证据。
|
|||
|
|
|
|||
|
|
指令级逐条覆盖(文档矩阵 test_arith/test_logic/test_control/test_string)
|
|||
|
|
依赖尚未存在的分类测试 PE,标 MANUAL。
|
|||
|
|
|
|||
|
|
WHPX 已知问题:guest 在 PE 兼容层执行阶段可能触发
|
|||
|
|
"Unexpected VP exit code 4" 直接终止 QEMU(WHPX 平台限制)。
|
|||
|
|
检测到该特征时自动以 -Accel tcg 重跑一轮(可用 -Accel tcg 直接指定)。
|
|||
|
|
|
|||
|
|
退出码:全部通过 0,任一 FAIL 1。
|
|||
|
|
#>
|
|||
|
|
param(
|
|||
|
|
[switch]$Build,
|
|||
|
|
[string]$Accel = 'whpx'
|
|||
|
|
)
|
|||
|
|
$ErrorActionPreference = 'Stop'
|
|||
|
|
. (Join-Path $PSScriptRoot 'lib\QemuTest.ps1')
|
|||
|
|
|
|||
|
|
Initialize-QemuTest -Build:$Build
|
|||
|
|
$result = New-TestCaseResult 'x86emu32-coverage'
|
|||
|
|
|
|||
|
|
# ================= 静态枚举 =================
|
|||
|
|
$emuSrc = Join-Path $script:RepoRoot 'CODE\UTSM\pe\x86emu32.c'
|
|||
|
|
$src = Get-Content $emuSrc -Raw
|
|||
|
|
$caseCount = ([regex]::Matches($src, 'case\s+0x[0-9A-Fa-f]+')).Count
|
|||
|
|
Add-TestCheck $result "静态: x86emu32.c 已实现操作码分支 case 0xNN × $caseCount" 'PASS' '仅枚举计数,供覆盖率分母参考'
|
|||
|
|
Add-TestNote $result "覆盖率分母: x86emu32.c 共 $caseCount 个 case 0xNN 操作码分支(含 0F 扩展页)"
|
|||
|
|
|
|||
|
|
$h32 = [System.IO.File]::ReadAllBytes((Join-Path $script:SystemDir 'bin\hello32.exe'))
|
|||
|
|
$peOff = [BitConverter]::ToInt32($h32, 0x3C)
|
|||
|
|
$mach = [BitConverter]::ToUInt16($h32, $peOff + 4)
|
|||
|
|
$optm = [BitConverter]::ToUInt16($h32, $peOff + 24)
|
|||
|
|
if ($mach -eq 0x14C -and $optm -eq 0x10B) {
|
|||
|
|
Add-TestCheck $result '静态: hello32.exe 为 PE32 (machine=0x14C, magic=0x10B)' 'PASS'
|
|||
|
|
} else {
|
|||
|
|
Add-TestCheck $result '静态: hello32.exe 为 PE32 (machine=0x14C, magic=0x10B)' 'FAIL' ("actual machine=0x{0:X} magic=0x{1:X}" -f $mach, $optm)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ================= 动态执行 =================
|
|||
|
|
# 单轮执行:正常启动 → cmd → AUTOEXEC(pe HELLO32.EXE / exit) → 返回 DSK
|
|||
|
|
# 返回 $true=完整跑完;$false=会话提前退出(WHPX 终止等)
|
|||
|
|
function Invoke-EmuRun {
|
|||
|
|
param([Parameter(Mandatory)][string]$AccelMode)
|
|||
|
|
Set-DeshabFirstInit -First 1 -DevMode 0
|
|||
|
|
Set-DeshabAutoexec -Lines @('pe HELLO32.EXE', 'exit')
|
|||
|
|
|
|||
|
|
$timeout = 180
|
|||
|
|
if ($AccelMode -eq 'tcg') { $timeout = 420 } # TCG 全模拟显著更慢
|
|||
|
|
$s = Start-QemuSession -Name "x86emu32-$AccelMode" -Accel $AccelMode -MonitorPort 45508
|
|||
|
|
try {
|
|||
|
|
$script:RunHit = Wait-QemuLog -Session $s -Patterns @('[DSK] cmd.elf returned', '[PANIC]') -TimeoutSeconds $timeout
|
|||
|
|
Start-Sleep -Seconds 2
|
|||
|
|
$script:RunLog = Read-QemuLogText $s.LogPath
|
|||
|
|
$script:RunLogPath = $s.LogPath
|
|||
|
|
if (-not $script:RunHit) { return $false }
|
|||
|
|
return $true
|
|||
|
|
} finally {
|
|||
|
|
Stop-QemuSession $s
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$script:RunLog = ''
|
|||
|
|
$script:RunLogPath = ''
|
|||
|
|
$script:RunHit = $null
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$usedAccel = $Accel
|
|||
|
|
$ok = Invoke-EmuRun -AccelMode $Accel
|
|||
|
|
if (-not $ok -and $Accel -eq 'whpx' -and $script:RunLog.Contains('WHPX: Unexpected VP exit')) {
|
|||
|
|
Add-TestNote $result "WHPX 在 PE 兼容层执行处终止 QEMU(Unexpected VP exit,WHPX 平台限制),自动以 TCG 重跑"
|
|||
|
|
$usedAccel = 'tcg'
|
|||
|
|
$ok = Invoke-EmuRun -AccelMode 'tcg'
|
|||
|
|
}
|
|||
|
|
$log = $script:RunLog
|
|||
|
|
$result.LogPath = $script:RunLogPath
|
|||
|
|
Add-TestNote $result "加速方案: $usedAccel"
|
|||
|
|
$hit = $script:RunHit
|
|||
|
|
|
|||
|
|
# ---- 解释器接管链(BUG-007 已修复,全链路可稳定断言) ----
|
|||
|
|
Assert-QemuLog $result $log -Ordered @(
|
|||
|
|
'[cmd] running AUTOEXEC.BAT',
|
|||
|
|
'[cmd] PE run begin',
|
|||
|
|
'[cmd] cmdline: HELLO32.EXE',
|
|||
|
|
'[PE] running PE32 via x86emu32'
|
|||
|
|
) -MustNotContain @(
|
|||
|
|
'[PANIC]',
|
|||
|
|
'[PE32] emulator error',
|
|||
|
|
'[PE32] emu init failed',
|
|||
|
|
'[PE] unimplemented import',
|
|||
|
|
'[PE] unimplemented API called'
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- shim 输出与完整返回链:BUG-007/006 已修复,转硬断言(回归守护) ----
|
|||
|
|
if ($log.Contains('Hello from PE32!')) {
|
|||
|
|
Add-TestCheck $result '动态: 解释执行 + shim IAT 拦截输出(Hello from PE32!)' 'PASS'
|
|||
|
|
} else {
|
|||
|
|
Add-TestCheck $result '动态: 解释执行 + shim IAT 拦截输出(Hello from PE32!)' 'FAIL' 'missing Hello from PE32!(BUG-20260801-007 回归?)'
|
|||
|
|
}
|
|||
|
|
if ($log.Contains('[cmd] PE run end') -and $log.Contains('[DSK] cmd.elf returned')) {
|
|||
|
|
Add-TestCheck $result '动态: ExitProcess 回 cmd,AUTOEXEC 跑完返回 DSK' 'PASS'
|
|||
|
|
} else {
|
|||
|
|
# 已知系统侧阻塞(2026-08-04 诊断定案,非 BUG-006/007 回归):
|
|||
|
|
# cmd run_autoexec 的 data/size 直指共享静态缓冲 f32_data(fat32_io.h),
|
|||
|
|
# 首行 `pe HELLO32.EXE` 加载 PE 即覆盖剩余脚本,exit 行丢失 → cmd 停留
|
|||
|
|
# 交互主循环(monitor 取证 RIP=cmd.elf 内 inb 0x60 轮询点,无异常投递)。
|
|||
|
|
# pe-compat 会话 C(pe NONEXIST.EXE + exit)通过反证:文件未找到不覆盖
|
|||
|
|
# 缓冲时 exit 返回链正常。待 cmd 侧修复(脚本行先拷入私有缓冲再执行)。
|
|||
|
|
Add-TestCheck $result '动态: ExitProcess 回 cmd,AUTOEXEC 跑完返回 DSK' 'FAIL' 'missing [DSK] cmd.elf returned(已知系统 bug:cmd run_autoexec 共享 f32_data 被 pe 文件加载覆盖,exit 行丢失)'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 解释器侧覆盖判定:hello32 全指令流(含 shim IAT 拦截与参数读取)
|
|||
|
|
# 均正确执行——无 emulator error / unimplemented 日志即解释器覆盖率
|
|||
|
|
# 不记倒退。
|
|||
|
|
if (-not $log.Contains('[PE] unimplemented') -and -not $log.Contains('[PE32] emulator') -and $log.Contains('[PE] running PE32 via x86emu32')) {
|
|||
|
|
Add-TestCheck $result '动态: hello32 已执行指令全部命中已实现集(无 unimplemented/emulator error)' 'PASS'
|
|||
|
|
} else {
|
|||
|
|
Add-TestCheck $result '动态: hello32 已执行指令全部命中已实现集(无 unimplemented/emulator error)' 'FAIL' 'unimplemented/emulator error anchor present or interpreter not reached'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ---- 指令级分类覆盖(依赖尚不存在的分类测试 PE) ----
|
|||
|
|
foreach ($t in @('数据传送簇 (MOV/PUSH/POP 逐操作数形态)', '算术簇 (ADD/SUB/INC/DEC/MUL/DIV 逐标志验证)',
|
|||
|
|
'逻辑簇 (AND/OR/XOR/NOT)', '控制流簇 (JMP/CALL/RET/Jcc)',
|
|||
|
|
'字符串簇 (MOVSB/STOSB/REP 前缀)', '堆栈帧 (ENTER/LEAVE) + INT3 断点路径')) {
|
|||
|
|
Add-TestCheck $result "指令级: $t" 'MANUAL' '需 CODE/tools/pe-samples/ 下分类测试 PE(test_arith.exe 等),当前未构建;hello32 仅综合冒烟'
|
|||
|
|
}
|
|||
|
|
Add-TestNote $result "pe-compat.ps1 已覆盖 PE32+ 原生对照组(HELLO64.EXE)与 PE 服务 ABI 锚点"
|
|||
|
|
} finally {
|
|||
|
|
Restore-DeshabScenario
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Write-TestCaseResult $result
|
|||
|
|
if ($result.Status -eq 'FAIL') { exit 1 } else { exit 0 }
|