WikifitaGitHub live67e8de5
pesquisa · fitdnuvo/fitdnuvo-handoff

FitDNuvo — Complete Handoff

Restoration, lessons, issues, architecture, next steps for Denuvo research

Baixar raw

FitDNuvo — Handoff Completo

Data: 2026-06-29 Status: Research paused — restored to original state Unachieved goal: Functional standalone with GPU


Origin: The Pain of a Savefile

It all started with a simple problem: F1 25 wouldn't save progress offline. The game, installed via Steam Family Sharing on the chicotv Windows PC, tried to save to the cloud and failed, resulting in "Save failed". Three careers lost.

What was a known Codemasters bug (savegame folder not created in Documents\My Games\F1 25\) turned into a complete investigation of the Denuvo/EA SPEAR protection architecture, and culminated in the vision of FitDNuvo — a complete pipeline for DRM removal and digital sovereignty restoration.


Perspective: Digital Sovereignty and Revolutionary Task

"It's not about piracy, it's about personal security." — Alefita

FitDNuvo is not a crack. It's a security research tool that:

  1. Validates that the user owns a legitimate license (Steam ownership via steam_api64.dll)
  2. Removes protection layers that prevent offline operation of purchased software
  3. Restores the ability to save locally — a basic consumer right
  4. Blocks communication with official servers (Ring0Guardkeeper) to maintain competitive integrity
  5. Enables community multiplayer via IPv6 P2P (Steamworks Fix) without depending on EA servers

The vision is an ecosystem where the legitimate owner of the game exercises total control over their copy, without depending on servers that can be shut down, without being treated as suspicious by anti-cheat systems that penalize offline play.


Discovered Architecture

The F1 25 Protection Stack

F1_25.exe (399MB, 23 PE sections)
│
├── TLS Callbacks (Windows executa antes do main)
│   ├── CB0 (0x8f1b5f0, .tls XRW) → Denuvo VM bootstrap, starts TCP server :47584
│   ├── CB1 (0x18e20,  .sbss XR)  → Engine EGO initialization (DO NOT TOUCH!)
│   └── CB2 (0x1355af90,.tls XRW) → License validation via preloader + SPEAR
│
├── preloader_l.dll (46KB) → Denuvo bootstrap + anti-debug
│   ├── DllMain → UD2 → SEH handler → init VM + launch SPEAR
│   └── preloader_link_func → RET (eax = license status)
│
├── EAAntiCheat.GameServiceLauncher.dll (48MB) → EA SPEAR AntiCheat
│   ├── 43 packer sections (custom EA packer, not UPX/VMProtect/Themida)
│   ├── Static analysis IMPOSSIBLE — packed with max entropy
│   └── Internal handshake protocol (port 47584)
│
├── steam_api64.dll (299KB original) → Steam DRM
│   ├── UserHasLicenseForApp, BIsSubscribed, GetAppOwner
│   └── Family Sharing: LastOwner=76561198774734292
│
└── EGO Engine → Original Codemasters engine

Denuvo Fingerprint (58 import signatures)

CategoryFunctionsMatch in preloader
Core VMNtAllocateVirtualMemory, NtCreateSection, NtMapViewOfSection, NtProtectVirtualMemory, RtlAllocateHeap5/5 (100%)
Anti-DebugZwQueryInformationProcess, GetProcessWindowStation, SetWindowLongPtrW, IsChild8/9 (89%)
LicenseZwCreateUserProcess, ZwCreateThreadEx (Denuvo creates child processes!)4/9
Process IntrospectionLdrGetProcedureAddress, LdrFindEntryForAddress, RtlPcToFileHeader9/9 (100%)
Red FlagsPie (GDI32 anti-analysis filler), MessageBoxW5/5 (100%)

Fundamental Discovery: Denuvo Is Turing-Complete

Denuvo's VM must be Turing-complete to execute arbitrary protection logic within any game. This implies:

  1. The ISA cannot be random — it must have LOAD, STORE, BRANCH, ARITH, SYSCALL
  2. Each version remaps opcodes deterministically (Denuvo GmbH's internal toolchain)
  3. The remapping is predictable — if we find the RNG seed, we can predict the entire ISA
  4. The seed probably derives from game metadata (AppID, timestamp, binary hash)

Consequence: Denuvo needs a ring-0 anti-cheat (SPEAR) to protect the VM. Without SPEAR, the VM is vulnerable to:

  • Bytecode extraction
  • ISA decompilation
  • Native code substitution

No one in the cracking community seems to have articulated this explicitly. The "cat and mouse race" between Denuvo and crackers is fundamentally about: crackers map the ISA of each version, Denuvo changes the remapping in the next version. It's a battle of reverse engineering against deterministic obfuscation.

The Injection Vector: DLSS / sl.interposer.dll

The sl.interposer.dll (NVIDIA Streamline, 541KB, signed by NVIDIA) is a complete graphics API proxy that intercepts D3D11 + D3D12 + DXGI + Vulkan. It's called every frame. It exports:

  • slInit(), slSetTag(), slGetFeature(), slSetConstants(), slEvaluateFeature()
  • D3D12CreateDevice, vkCreateInstance, CreateDXGIFactory

Why this is the perfect vector:

  1. Signed by NVIDIA (valid certificate chain)
  2. Loaded every frame (reliable hook point)
  3. Denuvo validates GPU chain integrity through it
  4. Replacing with proxy allows observation/injection in real time

Built Tools

FitDNuvo Scanner (/tmp/fitdnuvo_scanner.py)

  • Zero-dependency PE parser
  • 62 Denuvo fingerprint signatures in 9 categories
  • 6 structural heuristics (massive TLS, high entropy, XRW sections, suspicious names)
  • JSON output with Engine vs Denuvo classification
  • Result: 71% Denuvo score on preloader, 14.5% + structural confirmation on F1_25.exe (393MB Denuvo VM payload)

FitDNuvo Stripper (/tmp/fitdnuvo_stripper.py)

  • Removes Denuvo sections from PE
  • Rebuilds headers, section table, TLS directory
  • Result: 399MB → 15.7MB (96.1% reduction)

Preloader V4 Patch

  • 6 bytes modified out of 46,840
  • preloader_link_func: mov eax, 1; ret (returns valid license)
  • Original UD2 traps preserved (normal DllMain init)

SPEAR DLL Stub (spear_dll_stub.dll, 103KB)

  • Compiled with Visual Studio 2026 on chicotv
  • DllMain returns TRUE, zero imports, zero functionality
  • Deployed as EAAntiCheat.GameServiceLauncher.dll

SPEAR Heartbeat Stub v2 (spear_hb2.exe, 104KB)

  • Compiled with Visual Studio 2026 on chicotv
  • Connects to 127.0.0.1:47584 with retry (30 attempts, 1s interval)
  • Sends preloader MD5 token (16 bytes)
  • Keeps TCP connection alive indefinitely (heartbeat)

chicotv Relay (chicotv_relay.py)

  • TCP server on port 19999
  • Command bridge to avoid SSH escape hell
  • Connection via SSH tunnel (port blocked by Windows firewall)

Problems Encountered

SSH + Windows = Escape Hell

ProblemCauseAttempted Solution
$var interpreted by bashSSH passes through local shell before remotePowerShell -EncodedCommand (base64)
" double quote brokenbash vs cmd vs PowerShell different rulesHeredoc with single quotes
\ backslash eatenUnix vs Windows path separatorsForward slashes on Windows (doesn't always work)
&& chaining failscmd /c vs bash different semantics.bat files written via relay
%VAR% expanded by bashWindows environment variablesSet inside .bat
PowerShell $_ expandedbash interprets $_Scripts in file, not inline
cmd /c start doesn't returnSSH waits for process to finishPsExec for GUI processes
Session 0 vs Session 1SSH creates processes in Session 0 (no GPU!)PsExec -i 1

Windows Firewall

  • Port 19999 (relay) needed netsh advfirewall to open
  • Even with rule, external connection failed — resolved with SSH tunnel -L 19999:127.0.0.1:19999

Ghidra on Windows

  • Initial download corrupted (554MB truncated, missing 19MB)
  • PowerShell Expand-Archive and .NET ZipFile.ExtractToDirectory failed on zip >500MB
  • Python zipfile worked (6708 files extracted)
  • analyzeHeadless.bat timed out even with -noanalysis — likely JAVA_HOME issue
  • MCP extension deployed but never tested due to headless timeout

The Final Blocker: Port 47584

  • TCP server starts on port 47584 (Denuvo VM in .tls)
  • Accepts connections but never responds to any tested format:
    • Raw binary (1-1024 bytes, all patterns)
    • HTTP GET/POST/CONNECT
    • WebSocket upgrade (all paths)
    • UDP (all payloads)
    • TLS ClientHello
    • Hash tokens (MD5, SHA256, export hash)
    • Length-prefixed TLV
    • Delayed connection (timing attack)
  • 16-byte connection with sequential pattern (0x00-0xFF) permanently closes the server
  • Internal connection (PID→PID via 172.25.128.1 Hyper-V) stays ESTABLISHED but stuck
  • Conclusion: Proprietary protocol with runtime-generated token authentication, impossible to replicate externally

Progress with Session 1 (PsExec)

  • Biggest advance: The game OPENED and showed "Error" when launched via PsExec in Session 1
  • This proved the "live process without window" problem was Session 0
  • But the error was invalid license (original preloader_link_func returning 0)
  • With V4 patch, the game stayed alive for 60+ seconds, 432MB, but without GPU
  • GPU never activated in any tested standalone configuration

Final State (Restored)

chicotv (D:\SteamLibrary\steamapps\common\F1 25\):
  ✅ preloader_l.dll — ORIGINAL (46,840 bytes)
  ✅ EAAntiCheat.GameServiceLauncher.dll — ORIGINAL (48,396,024 bytes)
  ⚠️ steam_api64.dll — Goldberg (22MB) → needs "Verify integrity" in Steam
  ⚠️ EAAntiCheat.GameServiceLauncher.exe — stub (104KB) → needs "Verify integrity"
  ✅ F1_25.exe — ORIGINAL (nunca foi modificado com sucesso pelo SCP)

Backups preservados:
  ✅ preloader_l.dll.original (46,840 bytes)
  ✅ EAAntiCheat.GameServiceLauncher.dll.original (48,396,024 bytes)

To fully restore: In Steam → F1 25 → Properties → Installed Files → Verify integrity of game files. Steam will detect steam_api64.dll (Goldberg 22MB instead of ~299KB) and EAAntiCheat.GameServiceLauncher.exe (104KB stub instead of ~17MB) and re-download.


Lessons Learned

  1. Never touch a file without backup — the backup of steam_api64.dll and SPEAR .exe was not made before deploying Goldberg/stub
  2. SSH + Windows is fragile — each layer (bash→ssh→cmd/PowerShell) introduces different escaping
  3. The Python relay solved the escape hell — recommended approach for future remote Windows interactions
  4. Session 0 vs Session 1 is critical — GPU/D3D doesn't work in session 0. PsExec -i 1 solves it
  5. Ghidra on Mac worked well — preloader and interposer analysis completed successfully
  6. Ghidra on Windows didn't work — analyzeHeadless.bat timed out, likely configuration issue
  7. Goldberg Steam Emulator was useful — game started without Steam, but didn't reach GPU
  8. Binary patching is delicate — the 18-byte patch on the 3 WSARecv calls was never effectively deployed (SCP failed with file lock)
  9. The port 47584 protocol is the final bottleneck — without dynamic analysis (Frida/API Monitor) or complete reverse engineering of the Denuvo VM in .tls, there's no way to satisfy the handshake
  10. The "external stub" approach doesn't work for internal handshake — the protocol is between components of the same process, not externally accessible

Next Steps (for the future harness)

  1. Dynamic analysis with Frida/WinDbg — hook WSARecv, WSAWaitForMultipleEvents, ZwCreateUserProcess to capture the real handshake
  2. Ghidra on Mac with F1_25.exe — complete import with patience (hours of analysis), map the Denuvo VM flow
  3. Validated binary patch — modify the 3 WSARecv calls in .sbss and verify with checksum
  4. DLL injection — inject code into F1_25.exe via AppInit_DLLs or SetWindowsHookEx to intercept the handshake from inside the process
  5. SPEAR protocol via emulation — run the original SPEAR in a controlled environment (sandbox) and capture the handshake

See Also