IMPORTANT: Supersedes incomplete SSL analysis Key findings: - Server URLs hardcoded in SynergyEnvironmentImpl.smali - Requires Smali bytecode modification, not just SSL bypass - Native code (libRealRacing3.so) handles HTTP callbacks - ALLOW_ALL_HOSTNAME_VERIFIER already set (good news!) - cc_server_env in strings.xml controls environment Thanks to Discord community for the correction! Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
15 KiB
🌐 RR3 Custom Server Configuration - Complete Guide
Problem: Someone is concerned about SSL/certificate validation AND hardcoded server URLs
Reality: They're absolutely right - this is the real challenge!
Solution: Multiple Smali + XML modifications required to redirect game to custom servers
⚠️ IMPORTANT CORRECTION
My previous SSL-CERTIFICATE-BYPASS.md was INCOMPLETE!
While SSL validation is indeed disabled for basic TrustManager checks, the real challenge is:
- Hardcoded server URLs in compiled bytecode
- Native code (libRealRacing3.so) that handles network communication
- Configuration passing from Java → Native layer
The person questioning Part 3 was 100% correct! ✅
🔍 The Real Technical Reality
What We Found
1. Hardcoded EA Server URLs (In Java/Smali)
File: smali_classes2/com/ea/nimble/SynergyEnvironmentImpl.smali
# Line 19
.field private static final SYNERGY_INT_SERVER_URL:Ljava/lang/String; = "https://director-int.sn.eamobile.com"
# Line 21
.field private static final SYNERGY_LIVE_SERVER_URL:Ljava/lang/String; = "https://syn-dir.sn.eamobile.com"
# Line 23
.field private static final SYNERGY_STAGE_SERVER_URL:Ljava/lang/String; = "https://director-stage.sn.eamobile.com"
These are COMPILED INTO THE BYTECODE - not in a config file!
2. Server Environment Configuration (In XML)
File: res/values/strings.xml
Line 137:
<string name="cc_server_env">live</string>
This selects which hardcoded URL to use:
"live"→ Usessyn-dir.sn.eamobile.com"stage"→ Usesdirector-stage.sn.eamobile.com"int"→ Usesdirector-int.sn.eamobile.com
Line 350-353 (Nimble API Keys):
<string name="nimble_api_key_live">1cd0dfa4-c34c-4b0a-b444-aca954c96d50</string>
<string name="nimble_api_key_stage">aea852db-02b4-42f1-8a4a-7c167953b46e</string>
<string name="nimble_api_secret_live">4757e3d6-bb9e-4766-92bd-fd6a9e97eca6</string>
<string name="nimble_api_secret_stage">76ec9d8a-fbb1-448d-99d0-27f5ddcd664a</string>
These authenticate with EA's Nimble SDK backend.
3. Native Code Integration
Java HTTP wrapper: com/firemint/realracing/Http.smali
Native callback methods (Lines 119-129):
.method private native completeCallback(J)V
.end method
.method private native dataCallback(J[BI)V
.end method
.method private native errorCallback(J)V
.end method
.method private native headerCallback(JI)V
.end method
Key Point:
- Java code makes HTTP requests
- Results are passed to native C++ code via JNI callbacks
- Native code (
libRealRacing3.so) processes responses
This means:
- URL comes from Java (we can change)
- SSL verification happens in Java (already bypassed)
- BUT native code validates responses and might check domain/data format
🛠️ How to Redirect to Custom Server
Method 1: Change Hardcoded URL (Recommended)
Modify: smali_classes2/com/ea/nimble/SynergyEnvironmentImpl.smali
Original (Line 21):
.field private static final SYNERGY_LIVE_SERVER_URL:Ljava/lang/String; = "https://syn-dir.sn.eamobile.com"
Modified:
.field private static final SYNERGY_LIVE_SERVER_URL:Ljava/lang/String; = "https://your-custom-server.com:5555"
Also change Line 19 (int) and Line 23 (stage) to the same URL for consistency.
Method 2: Add Custom Environment Option
Option A: Add to strings.xml
File: res/values/strings.xml
Add new entry:
<string name="cc_server_env">custom</string>
<string name="cc_custom_server_url">https://your-server.com:5555</string>
Then modify SynergyEnvironmentImpl to read custom URL.
Option B: Use existing "int" environment
Simpler approach - just change the "int" URL:
# Change line 19
.field private static final SYNERGY_INT_SERVER_URL:Ljava/lang/String; = "https://your-server.com:5555"
Then in strings.xml:
<string name="cc_server_env">int</string>
Method 3: Network Injection (Advanced)
If you can't modify APK bytecode, intercept at OS level:
DNS Spoofing
# /etc/hosts on rooted Android
127.0.0.1 syn-dir.sn.eamobile.com
127.0.0.1 director-int.sn.eamobile.com
127.0.0.1 director-stage.sn.eamobile.com
Run local proxy on 127.0.0.1 to forward to your server.
VPN Tunnel
# Use VPN app to redirect EA domains to custom server
# Tools: Packet Tunnel, NetGuard, AdGuard (with custom DNS rules)
Note: This still requires SSL bypass since certificate won't match!
🔒 SSL Certificate Reality Check
What I Got Wrong Before
My previous doc said:
"SSL validation is disabled, custom servers work out-of-the-box"
What I SHOULD have said:
"SSL validation bypasses certificate expiry checks, BUT you still need to handle domain mismatches and native code expectations"
The Truth About SSL in RR3
Java Layer SSL (What We Analyzed)
Http.smali Line 179:
sget-object v0, Lorg/apache/http/conn/ssl/SSLSocketFactory;->ALLOW_ALL_HOSTNAME_VERIFIER:Lorg/apache/http/conn/ssl/X509HostnameVerifier;
invoke-static {v0}, Ljavax/net/ssl/HttpsURLConnection;->setDefaultHostnameVerifier(Ljavax/net/ssl/HostnameVerifier;)V
This line is CRITICAL:
ALLOW_ALL_HOSTNAME_VERIFIER- Disables hostname verification!- This means Java layer accepts ANY domain (e.g., your-server.com instead of ea.com)
- ✅ Good news for custom servers!
Http$1.smali (TrustManager):
.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
return-void # Does nothing = accepts all certificates
.end method
Result:
- ✅ Java layer accepts self-signed certificates
- ✅ Java layer accepts wrong domain names
- ✅ Java layer doesn't pin certificates
Native Layer SSL (Unknown Territory)
What we DON'T know:
- Does
libRealRacing3.soperform additional SSL validation? - Does native code check response signatures?
- Does native code validate server responses format?
What we CAN'T easily check:
- Native library is compiled C++ (not decompilable to readable code)
- Would need reverse engineering tools (IDA Pro, Ghidra)
- Or runtime testing with custom server
🧪 Testing Strategy
Phase 1: Java Layer Only
Goal: Confirm URL redirection works
Steps:
- Modify
SYNERGY_LIVE_SERVER_URLto point to your server - Rebuild APK, sign, install
- Monitor network traffic:
adb logcat | grep -i "http" - Check if game connects to your server
Expected Result:
- ✅ Game makes HTTP requests to your server
- ❓ Native code might reject responses
Phase 2: Response Validation
Goal: Determine what responses native code expects
Steps:
- Set up proxy (mitmproxy, Charles, Burp Suite)
- Intercept EA's server responses (if still accessible)
- Document response format, headers, JSON structure
- Replicate exact format on custom server
Key Things Native Code Might Check:
- Response HTTP status codes
- JSON structure/schema
- Cryptographic signatures (HMAC, JWT)
- Response headers (X-EA-, EAM-)
- Timing/sequence of responses
Phase 3: Native Code Validation
Goal: Bypass/understand native checks
Options:
A. Frida Hooking (Advanced)
// Hook native callback functions
Interceptor.attach(Module.findExportByName("libRealRacing3.so", "Java_com_firemint_realracing_Http_dataCallback"), {
onEnter: function(args) {
console.log("Native callback called with data:", args[2]);
}
});
B. Runtime Analysis
# Use strace to monitor native system calls
adb shell
strace -f -p $(pidof com.ea.games.r3_row) -e trace=network
C. Library Patching (Nuclear Option)
- Decompile
libRealRacing3.sowith Ghidra - Find SSL validation functions
- Patch to always return success
- Recompile library
Warning: This is VERY complex and error-prone!
📋 Complete Modification Checklist
Required Changes for Custom Server
1. Server URL Redirection
Files to modify:
✅ smali_classes2/com/ea/nimble/SynergyEnvironmentImpl.smali
- Line 19: SYNERGY_INT_SERVER_URL
- Line 21: SYNERGY_LIVE_SERVER_URL
- Line 23: SYNERGY_STAGE_SERVER_URL
❓ res/values/strings.xml
- Line 137: cc_server_env (set to "live" or "custom")
2. SSL/TLS Configuration
Already bypassed by default:
✅ smali_classes2/com/firemint/realracing/Http.smali
- Line 179: ALLOW_ALL_HOSTNAME_VERIFIER (already set)
✅ smali_classes2/com/firemint/realracing/Http$1.smali
- Line 38-40: checkServerTrusted (empty method)
✅ smali_classes2/com/firemonkeys/cloudcellapi/HttpRequest.smali
- Line 47: m_bSSLCheck = false (disabled)
No changes needed here! ✅
3. API Key Configuration (Optional)
If your server validates Nimble API keys:
❓ res/values/strings.xml
- Line 350: nimble_api_key_live (change to your key)
- Line 352: nimble_api_secret_live (change to your secret)
If your server ignores API keys, skip this.
🎯 Simplified Build Script
# RR3-Custom-Server.ps1 - Automated URL replacement
param(
[string]$ServerURL = "https://your-server.com:5555"
)
# Decompile APK
apktool d realracing3.apk -o rr3-custom
# Replace server URLs
$smaliFile = "rr3-custom\smali_classes2\com\ea\nimble\SynergyEnvironmentImpl.smali"
(Get-Content $smaliFile) `
-replace 'https://syn-dir\.sn\.eamobile\.com', $ServerURL `
-replace 'https://director-int\.sn\.eamobile\.com', $ServerURL `
-replace 'https://director-stage\.sn\.eamobile\.com', $ServerURL `
| Set-Content $smaliFile
Write-Host "✅ Server URLs updated to: $ServerURL"
# Rebuild APK
apktool b rr3-custom -o rr3-custom-server.apk
# Align & Sign
zipalign -f -P 16 -v 16 rr3-custom-server.apk rr3-aligned.apk
java -jar uber-apk-signer.jar --apks rr3-aligned.apk
Write-Host "✅ APK built: rr3-aligned-signed.apk"
Usage:
.\RR3-Custom-Server.ps1 -ServerURL "https://rr3.mydomain.com:5555"
🧩 What Your Custom Server Needs
Minimum Requirements
1. Match EA's API Endpoints
Director API (Primary):
GET /director/api/android/getDirectionByPackage
POST /synergy/api/user/login
POST /synergy/api/user/register
GET /synergy/api/game/config
POST /synergy/api/game/saveProgress
Content API (Assets):
GET /content/api/manifest
GET /content/api/assets/{path}
2. Replicate Response Format
Example: getDirectionByPackage response:
{
"appUpgrade": 0,
"serverURL": {
"synergy.product": "https://your-server.com:5555",
"synergy.user": "https://your-server.com:5555",
"synergy.tracking": "https://your-server.com:5555"
},
"version": "14.0.1",
"minimumVersion": "14.0.0"
}
Key Points:
appUpgrade: 0- Bypass killswitchserverURLobject contains secondary endpoints- If native code validates JSON structure, match it exactly!
3. Handle Authentication Headers
RR3 sends these headers:
EAM-SESSION: <session-token>
EAM-USER-ID: <user-id>
EA-SELL-ID: <device-id>
SDK-VERSION: <nimble-version>
X-EA-GAME: RealRacing3
X-EA-PLATFORM: Android
Your server should:
- Accept these headers (don't reject unknown headers)
- Validate session tokens if implementing auth
- Return appropriate JSON responses
⚠️ Known Challenges
Challenge 1: Native Code Validation
Risk: Native code rejects responses from custom server
Symptoms:
- APK connects to your server (visible in logs)
- No error messages
- Game stuck at loading screen
- Native code silently fails
Solution:
- Test with exact EA response format
- Monitor native callbacks with Frida
- May require native library patching
Challenge 2: Cryptographic Signatures
Risk: Responses might be signed with EA's private key
Evidence:
- Nimble SDK has crypto capabilities
- API keys/secrets exist in config
- Native code could validate HMAC signatures
Solution:
- Try without signatures first (might not be enforced)
- If required, remove signature validation from native code
- Or generate valid signatures (if algorithm is known)
Challenge 3: Asset Downloads
Risk: Assets have MD5 checksums that must match
File: AssetsController.cs already handles this:
// Calculate MD5 on upload
using var md5 = MD5.Create();
var hash = md5.ComputeHash(fileStream);
asset.MD5Hash = BitConverter.ToString(hash).Replace("-", "").ToLower();
Your manifest MUST return matching MD5s or game rejects files! ✅
🎓 Learning from Discord Community
What We Know Works (Community Reports)
From Discord "airplane mode trick":
- Users start game normally
- Enable airplane mode during loading screen
- Game switches to "offline mode"
- Progression works locally
This proves:
- ✅ Game has offline capability
- ✅ Native code doesn't REQUIRE server validation for gameplay
- ✅ Server is primarily for cloud saves and multiplayer
What Needs Testing
Questions for community:
- Has anyone successfully redirected to custom server?
- What responses does native code expect?
- Are there signature validations?
- Does changing URL work without native code changes?
📚 Related Documentation
- KILLSWITCH-REMOVAL-TECHNICAL.md - Bypass appUpgrade check
- SSL-CERTIFICATE-BYPASS.md - Java layer SSL bypass (INCOMPLETE, read this doc instead)
- GETTING-STARTED.md - General APK building guide
- RR3-ULTIMATE-EDITION-COMPLETE.md - Complete v14 build process
🙏 Credits & Corrections
Original Analysis: Copilot CLI (me)
Correction Provided By: Discord community member (thank you!)
Finding: Part 3 of SSL analysis was incomplete - native code and hardcoded URLs are the real challenge
This document supersedes SSL-CERTIFICATE-BYPASS.md for custom server setup.
🚀 Next Steps
For Community Members
If you're testing custom servers:
- ✅ Easy: Change hardcoded URLs in Smali
- ✅ Easy: Build and sign APK
- ✅ Easy: Install and test connection
- ❓ Unknown: Test if native code accepts responses
- ❓ Unknown: Debug response format issues
- ❓ Hard: Patch native code if validation fails
Share your findings on Discord!
For Server Developers
Your server should:
- ✅ Must: Match EA's endpoint paths
- ✅ Must: Return valid JSON with correct structure
- ✅ Must: Calculate MD5 hashes for assets
- ❓ Maybe: Handle authentication headers
- ❓ Maybe: Sign responses (if native code checks)
ASP.NET Core server template already implements 1-3! ✅
📞 Community Support
Questions? Testing results?
Share on Discord: Project-Real-Resurrection-3
Found what responses work?
- Document JSON structure
- Share HTTP traffic captures
- Test different response formats
Got custom server working?
- Write detailed steps
- Share server code
- Help others replicate
Last Updated: February 20, 2026
Status: ⚠️ Theoretical - Requires community testing
Priority: High - This is the real challenge for custom servers!
🏎️💨 Let's figure this out together!