diff --git a/.gitea/workflows/windows-drivers.yml b/.gitea/workflows/windows-drivers.yml index 4c8a12f..fdaec36 100644 --- a/.gitea/workflows/windows-drivers.yml +++ b/.gitea/workflows/windows-drivers.yml @@ -131,7 +131,7 @@ jobs: run: ../../../scripts/ci/provision-windows-wdk.ps1 - name: cargo build wdk-probe (windows-drivers-rs) run: cargo build -p wdk-probe -v - - name: Inspect the produced DLL's /INTEGRITYCHECK bit + - name: Inspect /INTEGRITYCHECK (before) — expect FORCE_INTEGRITY set by wdk-build run: | # explicit --target (.cargo/config.toml) -> output under the triple subdir. $dll = "target\x86_64-pc-windows-msvc\debug\wdk_probe.dll" @@ -139,11 +139,9 @@ jobs: $b = [IO.File]::ReadAllBytes($dll) $pe = [BitConverter]::ToInt32($b, 0x3c) $dllchar = [BitConverter]::ToUInt16($b, $pe + 0x5e) # OptionalHeader.DllCharacteristics - $forceIntegrity = ($dllchar -band 0x0080) -ne 0 # IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY Write-Host ("wdk_probe.dll built OK ({0:N0} bytes)" -f (Get-Item $dll).Length) - Write-Host ("DllCharacteristics = 0x{0:X4}; FORCE_INTEGRITY(/INTEGRITYCHECK) = $forceIntegrity" -f $dllchar) - if ($forceIntegrity) { - Write-Host '==> /INTEGRITYCHECK IS set -> self-signed load needs the flag suppressed or the PE bit cleared (M0 task).' - } else { - Write-Host '==> /INTEGRITYCHECK NOT set -> a self-signed driver should load with no PE patch.' - } + Write-Host ("BEFORE: DllCharacteristics = 0x{0:X4}; FORCE_INTEGRITY = {1}" -f $dllchar, (($dllchar -band 0x0080) -ne 0)) + - name: Clear FORCE_INTEGRITY (self-signed-load fix) + verify + # wdk-build sets /INTEGRITYCHECK unconditionally -> a self-signed driver won't load. Clear the PE + # bit deterministically (the reusable packaging step; signing/.cat happen later for real drivers). + run: ../clear-force-integrity.ps1 -Path target\x86_64-pc-windows-msvc\debug\wdk_probe.dll diff --git a/packaging/windows/clear-force-integrity.ps1 b/packaging/windows/clear-force-integrity.ps1 new file mode 100644 index 0000000..98a0f7d --- /dev/null +++ b/packaging/windows/clear-force-integrity.ps1 @@ -0,0 +1,42 @@ +# Clear the PE FORCE_INTEGRITY bit (IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080) from a driver DLL. +# +# windows-drivers-rs / wdk-build links UMDF drivers with /INTEGRITYCHECK (sets the bit) UNCONDITIONALLY +# (wdk-build configure_binary_build → cargo::rustc-cdylib-link-arg=/INTEGRITYCHECK; no opt-out). With the +# bit set, Windows Code Integrity refuses to load a binary whose signature doesn't chain to a Microsoft +# root (errors 3004/3089) — so a SELF-SIGNED driver won't load. Clearing the bit (then re-signing) lets a +# self-signed driver load under Secure Boot — the same recipe the punktfunk gamepad drivers use, here as a +# deterministic, idempotent, reusable step instead of a hand-run patch. +# +# Order in the packaging flow: cargo build -> THIS -> signtool (sign .dll) -> Inf2Cat (.cat) -> sign .cat. +# (Clearing AFTER signing would invalidate the signature; clear FIRST, then sign.) +# +# DllCharacteristics lives at OptionalHeader+0x46 = (e_lfanew + 24) + 0x46 = e_lfanew + 0x5E for both PE32 +# and PE32+ (the fields up to it share offsets). +[CmdletBinding()] +param([Parameter(Mandatory)][string]$Path) +$ErrorActionPreference = 'Stop' + +if (-not (Test-Path $Path)) { throw "clear-force-integrity: file not found: $Path" } +$b = [IO.File]::ReadAllBytes($Path) +if ($b.Length -lt 0x40 -or $b[0] -ne 0x4D -or $b[1] -ne 0x5A) { throw "not a PE (no 'MZ'): $Path" } +$pe = [BitConverter]::ToInt32($b, 0x3C) +if ($pe -le 0 -or $pe + 0x60 -ge $b.Length -or $b[$pe] -ne 0x50 -or $b[$pe + 1] -ne 0x45) { + throw "no 'PE\0\0' signature at e_lfanew=$pe in $Path" +} +$off = $pe + 0x5E +$FORCE_INTEGRITY = 0x0080 +$dllchar = [BitConverter]::ToUInt16($b, $off) + +if (($dllchar -band $FORCE_INTEGRITY) -eq 0) { + Write-Host ("clear-force-integrity: already clear (DllCharacteristics=0x{0:X4}) — no change: $Path" -f $dllchar) +} else { + $new = [uint16]($dllchar -band (-bnot $FORCE_INTEGRITY)) + [BitConverter]::GetBytes($new).CopyTo($b, $off) + [IO.File]::WriteAllBytes($Path, $b) + Write-Host ("clear-force-integrity: cleared FORCE_INTEGRITY 0x{0:X4} -> 0x{1:X4} in $Path" -f $dllchar, $new) +} + +# Verify on disk (re-read) — the assertion. +$v = [BitConverter]::ToUInt16([IO.File]::ReadAllBytes($Path), $off) +if (($v -band $FORCE_INTEGRITY) -ne 0) { throw ("FORCE_INTEGRITY still set after clear (0x{0:X4})" -f $v) } +Write-Host ("clear-force-integrity: verified DllCharacteristics=0x{0:X4}, FORCE_INTEGRITY clear." -f $v)