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).
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# OpenFUT FIFA 17 - native Windows client
|
||||
|
||||
The FIFA 17 client host (`10.10.0.105`, Windows 11 Pro) runs FIFA 17 **natively**
|
||||
(no Wine/Proton/umu). This directory holds the read-only preflight verifier and
|
||||
documents the native launch/routing/rollback model.
|
||||
|
||||
## Install layout (`C:\FIFA 17`)
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `FIFA17.exe` | retail game exe (sha256 `29C31CEF…`). **Never modify/patch.** ImageBase `0x140000000`. |
|
||||
| `_fifa17.exe` | native crack loader (Chemicalflood). This is what you launch. `asInvoker` manifest -> must be elevated externally. |
|
||||
| `version.dll` | **OpenFUT hook** (in-process via the version.dll load-order hijack). ImageBase `0x180000000`. |
|
||||
| `version.dll.stale-849k.bak` | **rollback** copy of the previous hook. |
|
||||
| `CardsDLL_Win64_retail.dll` | FUT card/SBC/kit logic. ImageBase `0x180000000`. |
|
||||
| `powdll_Win64_retail.dll` | Pack-Opening-World (EASFC store). ImageBase `0x180000000`. |
|
||||
| `sysdll_Win64_retail.dll` | EA networking / ProtoSSL (cert, ea.com). |
|
||||
| `stp-origin_emu.dll` + `stp-origin_emu.ini` | Origin/LSX login emulator (in-process; opens LSX `:4216` locally at runtime). Persona configured in the `.ini`. |
|
||||
| `stp-selector.exe` | ssl/LSX selector companion. |
|
||||
| `openfut.cfg` | operator-facing routing override (see below). |
|
||||
|
||||
## Launch (native - there is NO launcher script by design)
|
||||
|
||||
Run `C:\FIFA 17\_fifa17.exe` **as Administrator**. The correct, reproducible way:
|
||||
|
||||
- Double-click the **"FIFA 17 (OpenFUT)"** shortcut (Desktop and Start Menu).
|
||||
It targets `_fifa17.exe`, working dir `C:\FIFA 17`, with the RunAsAdmin bit set.
|
||||
- `_fifa17.exe` is also flagged `RUNASADMIN` in
|
||||
`HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers`,
|
||||
so any launch (Explorer double-click included) elevates via UAC.
|
||||
|
||||
On launch the Windows loader maps `version.dll` from the game directory (hijack),
|
||||
`stp-origin_emu.dll` emulates Origin login for the configured persona, and the
|
||||
hook redirects EA endpoints to the OpenFUT backend.
|
||||
|
||||
> Do **not** wrap the launch in a script. The elevation + shortcut is the
|
||||
> supported mechanism. FIFA under native Windows also ignores synthetic input,
|
||||
> so in-game steps are performed by the operator one at a time.
|
||||
|
||||
## Routing (`openfut.cfg`)
|
||||
|
||||
```
|
||||
host=10.10.0.120
|
||||
https_port=8443
|
||||
blaze_redirector_port=42127
|
||||
blaze_main_port=42130
|
||||
```
|
||||
|
||||
The hook carries `10.10.0.120` as its baked-in default; `openfut.cfg` is the
|
||||
override. `10.10.0.120` hosts **both** production and staging.
|
||||
|
||||
> **Production safety:** `blaze_main_port=42130` is the **production** Blaze.
|
||||
> Before any match/economy exercise, repoint `blaze_main_port` (and the matching
|
||||
> UTAS/HTTPS route) to the staging port so no traffic reaches the prod container.
|
||||
> The preflight raises a WARN whenever `42130` is configured.
|
||||
|
||||
## Rollback
|
||||
|
||||
The hook is a single file swap; no installer state.
|
||||
|
||||
```powershell
|
||||
# disable OpenFUT hook (restore previous DLL)
|
||||
Copy-Item 'C:\FIFA 17\version.dll' 'C:\FIFA 17\version.dll.disabled.bak' -Force
|
||||
Copy-Item 'C:\FIFA 17\version.dll.stale-849k.bak' 'C:\FIFA 17\version.dll' -Force
|
||||
# re-arm: copy the desired hook build over version.dll again
|
||||
```
|
||||
|
||||
Always keep a `*.bak` of the live hook before redeploying (the preflight checks
|
||||
that a rollback backup exists and differs from the live DLL).
|
||||
|
||||
## Preflight
|
||||
|
||||
`openfut-client-preflight.ps1` is **read-only**: it never launches the game,
|
||||
never elevates, never writes game files, never mutates economy state. It verifies
|
||||
the retail exe hash, companion DLLs, hook + rollback, routing + backend
|
||||
reachability, login persona, launcher elevation, and the RE toolchain
|
||||
(x64dbg, cargo). Exit 0 = OK, 1 = blocking failure.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\openfut-client-preflight.ps1
|
||||
```
|
||||
|
||||
## Runtime RE (x64dbg)
|
||||
|
||||
See the Vault note **`02 Reverse Engineering/FIFA 17/Windows Client Runtime & x64dbg.md`**
|
||||
for the attach workflow and the RVA<->VA (ASLR) math. In short: these modules'
|
||||
preferred ImageBase is `0x180000000` (`0x140000000` for `FIFA17.exe`); in x64dbg
|
||||
a module name evaluates to its runtime (ASLR) base, so a Ghidra address maps to a
|
||||
breakpoint as `bp CardsDLL_Win64_retail.dll+<RVA>` where
|
||||
`RVA = ghidra_addr - 0x180000000`.
|
||||
@@ -0,0 +1,129 @@
|
||||
<#
|
||||
.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 }
|
||||
Reference in New Issue
Block a user