Files
OpenFUT/tools/windows/openfut-client-preflight.ps1
T
funman300 9f1fc1b47c feat(windows): native client preflight + launch/RE docs
Add read-only preflight verifier and Windows-client documentation for the
reimaged native-Windows FIFA17 client host (10.10.0.105). No launcher script:
the native model is _fifa17.exe run as admin (RUNASADMIN + shortcut). Covers
routing (openfut.cfg -> 10.10.0.120), rollback (version.dll swap), and the
x64dbg RVA<->VA (ASLR) attach workflow (ImageBase 0x180000000 CardsDLL/powdll).
2026-08-20 18:43:19 +00:00

130 lines
7.5 KiB
PowerShell

<#
.SYNOPSIS
OpenFUT FIFA 17 Windows client preflight - READ ONLY.
.DESCRIPTION
Non-destructive verification of the native Windows FIFA 17 OpenFUT client on
this machine. It NEVER launches the game, never elevates, never writes to
game files, and never mutates any economy state. It only reads files,
registry, and performs TCP connect probes to the configured backend.
Exit code 0 = all PASS/WARN, 1 = one or more FAIL.
Native launch model (there is NO launcher script by design):
run C:\FIFA 17\_fifa17.exe as Administrator
(use the "FIFA 17 (OpenFUT)" shortcut, which carries the RunAsAdmin bit).
#>
[CmdletBinding()]
param(
[string]$FifaRoot = 'C:\FIFA 17'
)
$ProgressPreference = 'SilentlyContinue'
$ErrorActionPreference = 'SilentlyContinue'
# --- known-good fingerprints -------------------------------------------------
# Retail executable MUST NOT be modified/patched (Denuvo + anti-cheat sensitive).
$EXPECT_FIFA17_EXE_SHA256 = '29C31CEF12B0C3C2A7305220617C7B4FA139AB76B8C857851BDBE88987962899'
# Currently deployed OpenFUT hook (base-supply + VEH build). Update on redeploy.
$EXPECT_HOOK_SHA256 = '8844A6BCEE7BE37DD55B989246B312AF52FA711CCBC1220D25FEC64CBAD077D8'
$ROLLBACK_BAK = Join-Path $FifaRoot 'version.dll.stale-849k.bak'
$script:fail = 0
function Say([string]$level, [string]$msg) {
switch ($level) {
'PASS' { $c = 'Green' }
'WARN' { $c = 'Yellow' }
'FAIL' { $c = 'Red'; $script:fail++ }
default { $c = 'Gray' }
}
Write-Host ('[{0}] {1}' -f $level, $msg) -ForegroundColor $c
}
function Sha([string]$p) { if (Test-Path $p) { (Get-FileHash $p -Algorithm SHA256).Hash } else { $null } }
Write-Host '=== OpenFUT FIFA 17 Windows client preflight (read-only) ===' -ForegroundColor Cyan
Say 'INFO' ("host={0} user={1} {2}" -f $env:COMPUTERNAME, $env:USERNAME, (Get-CimInstance Win32_OperatingSystem).Caption)
# --- 1. FIFA install + retail exe integrity ---------------------------------
$exe = Join-Path $FifaRoot 'FIFA17.exe'
$loader = Join-Path $FifaRoot '_fifa17.exe'
if (Test-Path $exe) {
$h = Sha $exe
if ($h -eq $EXPECT_FIFA17_EXE_SHA256) { Say 'PASS' "FIFA17.exe present and unmodified ($($h.Substring(0,16))...)" }
else { Say 'FAIL' "FIFA17.exe hash MISMATCH - retail exe changed! got $($h.Substring(0,16))... expected $($EXPECT_FIFA17_EXE_SHA256.Substring(0,16))..." }
} else { Say 'FAIL' "FIFA17.exe missing at $exe" }
if (Test-Path $loader) { Say 'PASS' "native loader _fifa17.exe present ($((Sha $loader).Substring(0,16))...)" }
else { Say 'FAIL' "_fifa17.exe (native loader) missing - cannot launch" }
# --- 2. companion DLLs -------------------------------------------------------
$companions = 'CardsDLL_Win64_retail.dll','powdll_Win64_retail.dll','sysdll_Win64_retail.dll',
'FootballCompEng_Win64_retail.dll','stp-origin_emu.dll','stp-selector.exe'
foreach ($c in $companions) {
$p = Join-Path $FifaRoot $c
if (Test-Path $p) { Say 'PASS' "companion present: $c" } else { Say 'FAIL' "companion MISSING: $c" }
}
# --- 3. OpenFUT hook (version.dll) + rollback backup ------------------------
$hook = Join-Path $FifaRoot 'version.dll'
if (Test-Path $hook) {
$hh = Sha $hook
if ($hh -eq $EXPECT_HOOK_SHA256) { Say 'PASS' "hook version.dll deployed (expected build $($hh.Substring(0,16))...)" }
else { Say 'WARN' "hook version.dll present but hash differs from recorded build ($($hh.Substring(0,16))...) - may be a newer/older hook" }
$bytes = [IO.File]::ReadAllBytes($hook); $ascii = [Text.Encoding]::ASCII.GetString($bytes)
$markers = @('OpenFUT','fifa17','CardsDLL','SBC_DISPATCH') | Where-Object { $ascii -match [regex]::Escape($_) }
if ($markers.Count -ge 3) { Say 'PASS' "hook markers found: $($markers -join ', ')" }
else { Say 'WARN' "hook markers thin: $($markers -join ', ') - is this the OpenFUT hook?" }
} else { Say 'FAIL' "hook version.dll NOT deployed - client will run vanilla (no OpenFUT)" }
if (Test-Path $ROLLBACK_BAK) {
if ((Sha $ROLLBACK_BAK) -ne (Sha $hook)) { Say 'PASS' "rollback backup present and differs from live: $(Split-Path $ROLLBACK_BAK -Leaf)" }
else { Say 'WARN' "rollback backup equals live version.dll - rollback would be a no-op" }
} else { Say 'WARN' "no rollback backup ($(Split-Path $ROLLBACK_BAK -Leaf)) - keep one before redeploying the hook" }
# --- 4. routing config + backend reachability -------------------------------
$cfg = Join-Path $FifaRoot 'openfut.cfg'
$host120 = $null; $ports = @()
if (Test-Path $cfg) {
$kv = @{}; foreach ($l in Get-Content $cfg) { if ($l -match '^\s*([^=#]+)=(.+)$') { $kv[$matches[1].Trim()] = $matches[2].Trim() } }
$host120 = $kv['host']
Say 'PASS' "openfut.cfg routing: host=$($kv['host']) https=$($kv['https_port']) redirector=$($kv['blaze_redirector_port']) blaze=$($kv['blaze_main_port'])"
foreach ($k in 'https_port','blaze_redirector_port','blaze_main_port') { if ($kv[$k]) { $ports += [int]$kv[$k] } }
if ($kv['blaze_main_port'] -eq '42130') { Say 'WARN' 'blaze_main_port=42130 targets PRODUCTION - repoint to a staging port before match/economy testing' }
} else { Say 'WARN' "openfut.cfg absent - hook uses its baked-in default host" }
if ($host120) {
foreach ($p in $ports) {
$t = New-Object Net.Sockets.TcpClient
try {
$ar = $t.BeginConnect($host120, $p, $null, $null)
if ($ar.AsyncWaitHandle.WaitOne(2500) -and $t.Connected) { Say 'PASS' "backend reachable ${host120}:$p" }
else { Say 'FAIL' "backend UNREACHABLE ${host120}:$p" }
} catch { Say 'FAIL' "backend probe error ${host120}:$p - $($_.Exception.Message)" } finally { $t.Close() }
}
}
# --- 5. login persona (stp origin emulator) ---------------------------------
$ini = Join-Path $FifaRoot 'stp-origin_emu.ini'
if (Test-Path $ini) {
$persona = (Get-Content $ini | Select-String 'PersonaId|PersonaName') -join ' '
Say 'PASS' "login emulator config: $persona"
} else { Say 'FAIL' "stp-origin_emu.ini missing - no login persona" }
# --- 6. native launcher elevation setup -------------------------------------
$lk = 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers'
$layer = (Get-ItemProperty $lk).$loader
if ($layer -match 'RUNASADMIN') { Say 'PASS' "_fifa17.exe RUNASADMIN flag set ($layer)" }
else { Say 'WARN' '_fifa17.exe has no RUNASADMIN flag - launch must be manual "Run as administrator"' }
$sc = Join-Path $env:USERPROFILE 'Desktop\FIFA 17 (OpenFUT).lnk'
if (Test-Path $sc) { Say 'PASS' "desktop launcher shortcut present: $(Split-Path $sc -Leaf)" }
else { Say 'WARN' 'no desktop launcher shortcut' }
# --- 7. RE toolchain ---------------------------------------------------------
$x = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Recurse -Depth 4 -Include x64dbg.exe -Attributes !ReparsePoint -EA SilentlyContinue | Select-Object -First 1 -Expand FullName
if (-not $x) { $x = (Get-Command x64dbg.exe -EA SilentlyContinue).Source }
if ($x) { Say 'PASS' "x64dbg present: $x (v$((Get-Item $x).VersionInfo.FileVersion))" } else { Say 'WARN' 'x64dbg not located - install for runtime RE' }
$cargo = (Get-Command cargo -EA SilentlyContinue).Source
if ($cargo) { Say 'PASS' "rust toolchain: $cargo" } else { Say 'WARN' 'cargo not found - needed to rebuild the hook natively' }
# --- summary -----------------------------------------------------------------
Write-Host ''
if ($script:fail -eq 0) { Write-Host 'PREFLIGHT: PASS (no blocking failures)' -ForegroundColor Green; exit 0 }
else { Write-Host "PREFLIGHT: FAIL ($script:fail blocking issue(s))" -ForegroundColor Red; exit 1 }