Tuesday, December 20, 2022

CrossARK Modification for Windows 10 UWP Cross-Platform Dedicated Servers

 



Introduction

A few years ago I posted a question on the official ARK: Survival Evolved forums (ARK Forum Post) asking if there were any interest in researching ARK: Survival Evolved to determine if it was possible to mod the game and add CrossARK functionality back into the game for XBOX/Win10 UWP servers.  At the same time I asked if there were any objections from Studio Wildcard.  At the time there was no easy way to do this, and anyone that wanted CrossARK play had to either host/play the Steam version or rent a server from Nitrado.

There were no responses, so I assumed there was no interest.  I did not mention, however, that by the time I had posted on the forum I had already done all of the research and found a way to make it work.  I had a PowerPoint write-up that I presented to my local group San Antonio Hackers Association (SAHA), but didn't want to write up a blog post if there was no interest.  Also, at the time I wasn't sure how Studio Wildcard would respond even though they are normally quite receptive to mods to their game.  

It's been a couple of years now, and with ARK 2 on the horizon, I decided to go ahead and post the steps as a blog entry for anyone still interested in adding this functionality to their private, dedicated ARK Win10 UWP servers.  I'll try to get around to cleaning up and posting the code to the tool I use to make the modifications in the near future.  Not knowing it there is any interest, this post is, for the most part, just a posting of the original PowerPoint presentation contents.  If there is interest, I may do a more in-depth writeup that explains the process a bit further.

Use this at your own risk! Back up your game files periodically.


What is ARK: Survival Evolved?

  • ARK: Survival Evolved is a survival game released by Studio Wildcard
  • ARK supports multiple platforms including Steam, Epic, XBOX, Playstation, Nintendo Switch, Windows 10 UWP, and Mobile
  • ARK supports cross-platform play between Steam and Epic as well as between XBOX and Windows 10 UWP.
  • Players typically begin the game with nothing and must battle other players either directly (PvP) or indirectly (PvE) as well as the environment to include roaming, tamable dinosaurs
  • The game includes combat, resource gathering, dinosaur taming, exploration, crafting, and building
  • Players are able to play in single player mode or online with others through official servers, hosting provider servers (such as Nitrado), and player-hosted dedicated servers (the subject of this presentation)


What is CrossARK?

  • Typically, each server hosts a different map containing unique resources, dinosaurs, and storylines (for storyline maps)
  • CrossARK is the capability in the game to allow players to transfer their character, inventory, and tamed dinosaurs between different servers via an in-game transport mechanism 
  • Players have the ability to move back and forth between servers at will once they have fulfilled certain requirements (such as locating the transporting beacons on the new server)
  • Groups of servers connected via CrossARK are known as clusers, each with a unique ClusterID
  • Each cluster enables transfers between the servers via a shared or synced cluster folder where the transfer contents are placed


Steam/Epic and XBOX/Windows UWP Comparisons

Steam and Epic

  • Built with the Unreal Engine for PC - both Windows and Linux (Steam only)
  • Purchased on the appropriate platform
  • Downloaded from the Steam or Epic stores
  • Cross-play supported between Steam and Epic versions of the game on PC
  • Not cross-play compatible with the Windows 10 UWP and XBOX One versions of the game
  • DOES support running multiple headless dedicated server instances on the same machine
  • DOES support CrossARK Up to 40 players on dedicated servers

XBOX and Windows 10 UWP

  • Win10 UWP (Universal Windows Platform) is a port of the Steam version wrapped inside of the UWP Bridge
  • Play Anywhere - purchased once on either XBOX or Windows 10 to make it available on both platforms
  • Downloaded from the XBOX and Windows 10 stores
  • Win10 UWP created primarily to support cross-platform play between PC and XBOX
  • Does NOT support running headless dedicated servers or multiple instances on the same machine
  • Does NOT support CrossARK  on dedicated non-hosted servers
  • Properly supports 4k and HDR10 graphics
  • XBOX Series X - Up to 100 players on dedicated servers


XBOX, Windows 10 UWP, and CrossARK

  • Due to the mechanisms in which the XBOX and Windows 10 UWP games are created, neither version supports the CrossARK capability when running as a dedicated server
  • Since CrossARK is enabled via a shared or synced folder between multiple instances of ARK, there is no method of duplicating that functionality on the XBOX which only runs one instance of the game at a time and does not give access to the underlying file system
  • For the Windows 10 UWP version, UWP programs historically did not allow command-line parameters to be provided nor did they allow multiple instances to be running concurrently unless the publisher requested that capability specifically.
  • While the last bullet has changed (UWP programs and bridged programs can be built to allow command-line parameters and multiple running instances), ARK has not been built to support either as they require non-trivial changes to the build
  • Another issue with running multiple instances of the Windows 10 UWP version is that it relies on the XBOX Live gaming service on Windows 10 to register and accept incoming connections (vs specifying separate ports on the Steam and Epic versions).  As such, only one instance can connect to the service at a time to receive players.


Goals

  • Reverse engineer the game to the point of identifying if the CrossARK functionality exists in the Windows 10 UWP version of the game
    • It does exist but through various checks it is disabled
  • Reverse engineer the game to the point of identifying if it is possible, through configuration change or program modification to enable CrossARK
    • It is possible to enable CrossARK to a point via in-memory modifications at runtime
  • Perform necessary steps to activate and test CrossARK
    • The modifications were successful in enabling CrossARK between multiple servers with a few caveats


Reverse Engineering

Preliminary Steps

UWP Protections

  • UWP Applications are typically protected by Windows 10 and encrypted on the disk
  • Change permissions of the data folders for the game, typically located at C:\Users\<user>\AppData\Local\Packages\StudioWildcard.4558480580BB9_1w2mm55455e38
  • Use an open source tool named UWPDumper in order to dump the files from memory (while decrypted) - https://github.com/Wunkolo/UWPDumper

Project Prep

  • Created a new project in Ghidra, importing ShooterGame.exe
  • Imported the symbols from the included symbol file
  • Performed a headless automated analysis
    • D:\Tools\ghidra_9.1.2_PUBLIC\support>analyzeHeadless.bat d:\Projects\RE\ARK\ARKGenesis_806_2 "ARK Genesis 806 2.gpr" -process ShooterGame.exe


Process

  • Identify the path of execution into the dedicated server potion of the game
    • There were at least 2 or 3 main functions nested due to the manner in which the port was built. None of these passed command-line arguments deeper into the engine
  • Determine if CrossARK functionality exists in the Windows 10 UWP build
    • The checks and execution portion were found in the binary
  • Locate any checks for different versions of the game that may disable CrossARK
    • One particular check was found that modified operations based on whether it was the console version (which seemed to include the Windows 10 UWP version) vs the Steam version
  • Identify a method for passing the ClusterID information to the dedicated server section
    • This was somewhat more difficult as passing the information from the command-line was not an option.
  • Obtain address offsets for the important bits of information that would need to be used and/or modified later
    • Identify the base address first for offsets - base 0x140000000


Console Check

  • Within the function AShooterGameMode::InitGameState, a check was made to determine if the console version of the game was running.  If so, then it would skip trying to determine if there was a ClusterID present.
  • Simply changing the variable specifying whether it was a console build was insufficient as other pieces of the game would also use it later, causing a crash.
  • It was determined that the best way forward was just to modify and reverse that single comparison while leaving the variable as it was for the other potions

  • Locate AShooterGameMode::InitGameState

  • Locate the GIsConsoleServer comparison, approximately 0x6e2 offset from the beginning of the function
  • Note: This offset will change with each build but should remain nearby
  • Record the address and offset from the program base
    • offsetConsoleCheck 0x1406caeff ( 6caeff) - (AShooterGameMode::InitGameState/+0x6e2)


Command-Line String Location

  • Since we have no ability to pass command-line arguments down the pipe to where they need to be processed, we need to manually populate the string at the end
  • To do this the piece of logic that accesses the command-line string needs to be found
  • Finally that string needs to be manually populated with the command-line needed for the ClusterID argument

  • Locate the FCommandLine::CmdLine variable, still within the AShooterGameMode::InitGameState function
  • The variable address should be found approximately 0x3cd offset from the beginning of the function
  • Note: This offset will change with each build but should remain nearby
  • Follow the variable address to its source

  • Record the address and offset from the program base
    • offsetCmdLine 0x143fe2fd0 (3fe2fd0)


ClusterID String Slack Space

  • When Unreal Engine parses the command-line, it places the separate arguments within an Unreal Engine String object.
  • The structure of the object is approximately as follows:
    • {LPVOID, unsigned char, unsigned char, char[] str}
  • Typically this object would be created and initialized in memory via the appropriate UE call
  • Instead of locating and making the initialization call, a suitable space within memory that’s not being used is found and the object is created manually. 
  • Cleanup is not important in this case since it is only created once, upon startup.

  • The function that parses the CluserID from the command-line needs an initialized Unreal Engine String object to place it
  • Instead of calling the appropriate initializer function, find a location to manually create the object – in this case a 256 byte section within the FXAudio2RadioEffect module
  • Record the address and offset of the CopyrightInfo wchar[256] structure which will be re-purposed after it has been used
    • strSlackSpaceOffset 0x143db7620 (3db7620)


Copy and Set ClusterID Logic

  • The final piece of this part of the process is the logic within the game that parses the ClusterID variable from the command-line and places it into an Unreal Engine String for use later during startup.
  • Normally, the command-line has already been parsed and the command-line is stored in a structure.  However, since the command-line cannot be passed all the way through the program when wrapped in a UWP bridge, a modification to this logic needs to be made.
  • In order for this to be done properly with the hacked command-line, an LEA needs to be modified that will instead point to the manually populated command-line.

  • Locate AShooterGameSession::RegisterServer

  • Locate the copy logic within the AShooterGameSession::RegisterServer function
  • The are aof interest should be found approximately 0x14ef offset from the beginning of the function
  • Note: This offset will change with each build but should remain nearby
  • Record the address and offset of both the initial LEA from 0x8c0 as well as the address and offset of the next instruction
    • offsetSetClusterID 0x1406ed84e ( 6ed84e) - (AShooterGameSession::RegisterServer/+0x14ef)
    • offsetSetClusterIDNextInstr 0x1406ed855 ( 6ed855) - (AShooterGameSession::RegisterServer/+0x14f6)


Modifying ARK

Preliminary Steps

UWP Protections

  • Since ARK is UWP protected and signed, all modifications need to be made in-memory while the game is running
  • As with the original dumping process, the UWPDumper open source tool was heavily used (with heavy modification) to modify the in-memory binary

Project Prep

  • Create a new project in Visual Studio with UWPDumper as the base

Source Code

  • The entire source code will be placed on github once it has been cleaned up, but for the purpose of this presentation only snippets of code will be shown


Set Offsets

  • Populate the variables for each of the items that were discovered during the reverse engineering process
  • For these variables, use the OFFSET FROM BASE and not the full address
    • 806.2
    • base 140000000
    • offsetConsoleCheck 0x6caeff
    • uintptr_t offsetCmdLine 0x3fe2fd0
    • uintptr_t strSlackSpaceOffset 0x3db7620
    • uintptr_t offsetSetClusterID 0x6ed84e
    • uintptr_t offsetSetClusterIDNextInstr 0x6ed855


Console Check


  • This logic modifies the comparison by changing it from 0x0 to 0x1, thus effectively reversing the call without modifying the variable itself.


Create Command-Line Arguments


  • Using the location of the CmdLine variable, this code populates it with the appropriate command-line.
  • Typically this will be –CluserId=blahblahblah, but it can also add the –ClusterDirOverrideDir parameter if a tool such as DropBox is used for syncing server instances. 
  • Be sure to change ‘blahblahblah’ to a valid clustered as found in the ARK unofficial server list located at http://arkdedicated.com/xbox/cache/unofficialserverlist.json so that the CrossARK transfer list will be populated. Use any valid ClusterID.


Setup ClusterID


  • Calculate all of the addresses and offsets needed for setting the ClusterID

  • Create and initialize the Unreal Engine String in the slack space manually.
    • Place the address of the beginning of the string in the first LPVOID
    • Place the length of the string in the first two LPVOIDs
    • Copy the contents of the actual ClusterID string into the wchar[] location

  • Modify the LEA so that it loads the address of manually created Unreal Engine String to place the ClusterID data


Final Setup and Execution

Preliminary Steps

  • Each instance of Windows 10 UWP ARK needs to be run on a separate machine. These machines may be physical or virtual.
  • Since each instance needs to be run on a separate machine, a method of syncing the cluster folder (see ARK wiki/documentation) must be synced.
  • Cluster folder is typically
    • C:\Users\<username>\AppData\Local\Packages\StudioWildcard.4558480580BB9_1w2mm55455e38\LocalState\Saved\clusters\<blahblahblah>
  • Dropbox, OneDrive, Google Drive, Box, etc 
    • can be used to sync via the cloud.  For these, be sure to take into account the lag in sync time before logging into another server once transfer has commenced. In these cases, the OverrideClusterDir parameter will need to be enabled, and ARK will need to be added to the list of UWP applications given permission to write outside of their sandbox.
  • Dropbox LAN (a local syncing version of Dropbox), Resilio, SyncThing, etc
    • One of these can be used to avoid cloud syncing if all of the machines are on the same local network.  In the case of Resilio, the existing clustered subdirectory within the app on each machine can be pointed at so that sandbox permissions do not need to be modified. These methods are also near instantaneous, lowering the risk of logging into another server after a transfer before the sync is complete


Execution

  • Start ARK
  • Navigate to Dedicated Server via Host/Local
  • Configure (if not already done so) the Dedicated Server screen
  • Click Run Dedicated Server
  • Fill out Passwords and Session Name as needed
  • Execute the injector tool
  • Ensure the execution completes successfully
    • If it does not (either no output or it hangs) close the injector and ARK and try again. In some cases it may be a timing issue



Transfer Process

  • Because the CrossARK server selection screen only displays Nitrado-hosted servers, a workaround needs to happen in order to transfer servers. As mentioned above, any valid Nitrado-hosted ClusterID must be selected, which will then cause that cluster to show up in the selection list.   
  • Choose ANY of those servers to begin the transfer.  
  • The game will automatically connect to the chosen server – be sure to disconnected after it does. This isn't actually your server but the Nitrado hosted one we are using for the ClusterID.
  • After disconnecting, find the server you wish to connect (in your cluster) via either the XBOX Live Friends list or the Dedicated (not unofficial) Servers list. Once you connect the process will complete.


Conclusion

(No Money Shot)

I've used this process for some time now in order to jump between my own private servers.  It works pretty well considering the steps required.  One of the benefits of using a sync too that keeps versions/backups of the cluster files is to be able to recover from a bad load. Sometimes the player will load faster than the area in the world, causing the player to fall beneath the ground and lose everything.  This is easy to fix just by re-syncing the last player cluster file and having them log back in to the server.

While there are certainly a few things that Wildcard could do to help (see the Addendum), for the most part this works as an alternative.  The real cost is the time it takes every build to find the new offsets for the various bits. It's probably that a tool can be created that can automate that process, but for now, hopefully, this will provide a useful way for people still hosting private ARK servers to make do until ARK 2 arrives.

Addendum

Studio Wildcard

While breaking down the XBOX Live protocol, I discovered that the clusterid does get placed into the session identifier string, which indicates that if the studio modified the CrossARK transfer screen to be switchable to the Dedicated Servers list (vs stuck on the Unofficial Servers list) hosts would no longer be stuck using an existing Nitrado ClusterID, allowing players to directly transfer instead of having to disconnect and re-connect.

By moving the location within program execution where the engine loads the config file, it should be possible to pass the ClusterID via the file instead of the command-line which would negate the problem of not being able to provide it on the command-line


Intercept ARK Server Browser Calls

An option I personally employed was to intercept the DNS requests to the ARK server list (unofficial servers), providing my own json server instead.  What this did was gave me the ability to use a unique ClusterID and display my own servers instead of an existing Nitrado server. The disconnect workaround was still necessary, though. Unfortunately, this will only work for players that use your private DNS server so it's not really a great option.


Windows 11 UWP

One thing to note is that Microsoft changed things up with Windows 11 UWP.  From what I've read, it no longer locks down the folders (no need to change permissions), and it may have relaxed the sandbox restrictions as well. Some of these steps may no longer be necessary.