Last active 1618884604

Life Is Strange stats server investigation

Revision 8739f79de5343416f40d60d6827b296db02be888

docs.md Raw

On game start

The game sends several requests in the opening videos. In the below documentation, text in curly braces ({}) is shorthand for { "d": {"os_Ping": ... } } where os_Ping is the current endpoint being accessed and ... is the contents of the curly braces.

Necessary headers for each request (may vary, untested):

  • Accept: application/json, otherwise the API returns XML (obviously if you're fine with that you can omit this)
  • OS-AuthProvider: 6, unsure of the purpose of this
  • OS-AuthTicketData: [string], begins with CAEQ on my copy of the game, unsure if variable
  • OS-AuthTicketSize: 44, the length of the above string
  • OS-UID: [string], steam user ID, or some other form of identification in standalone versions probably
  • OS-Platform: steam, the user's platform, needs more testing to get options
  • OS-System: linux, ditto
  1. /game/os_Ping

    • Likely confirming that the service is up, needs to succeed otherwise the game marks itself as offline
    • Returns { 6739 }, different number each time
  2. /game/os_GetServiceInfo

    • This returns information about the IP address used for connection: city name, country code, ip, lat, long, phone region code, zip code if available. Also returns a list of metadata, e.g client config and list of endpoints. Example output here.
  3. /game/os_Ping again

  4. /game/SEM_Login

    • Probably for the Square Enix account login
    • Query params: s_type is 'UID' here; s_value is my steam user ID, a long string of digits
    • Returns { "__metadata": { "type": "game.SEMSubmit" }, "b_confirmed": true, "s_SEMID": "string of numbers", "s_email": "email address", "s_facebookID": "probably facebook user identifier", "s_longTermToken": "string of numbers and letters" }
  5. It sends the above request twice, unclear why

  6. /game/CreateUserProfile

    • Square Enix login again? Probably making sure that there's an account for the given steam ID for stats collection even if it isn't linked to a SEM account
    • Query params: s_uid is my steam user ID
    • Returns { true } and a header OS-AuthResponse containing the new OS-AuthTicketData to be used for subsequent requests
  7. /game/GetTodaysInfocast

    • Returns a list of messages to be scrolled at the bottom of the main menu
    • Idling on the menu calls this periodically
    • Query params: b_digital, a boolean, probably whether it's digital or a physical disc; i16_episode, the episode number of the current save game; s_locale, language code e.g 'en'
    • Example response: https://gist.github.com/blha303/d00b1c34728cc21101b7fc198bbf3371

Occasionally

  1. /game/AddMetrics

On opening the choices screen (from the main menu or after each episode)

  1. /game/CommunityFactsGetEpisode

  2. /game/GetFriendsProfileStats

    • Apparently would return the same as /game/CommunityFactsGetEpisode with additional info on friend stats, but currently returns nothing
    • Query params: i16_episode, the given episode number; s_uid, the steam/other user id
    • Currently returns { "d": {"results": [] } }

On finishing an episode

Making me listen to Obstacles again >_<

  1. /game/UpdateUserProfileGameSpecific

No API requests upon completing an achievement.

revlis.md Raw

Hello! Today I thought I'd take on the task of reverse engineering the stats screen in Life Is Strange so I could easily request and display the data without needing to start the game. I'm documenting it here for future use, since nobody else seems to have tried this yet.

First, you'll need a copy of Life Is Strange on Steam. Second, you'll need to have it working on Linux. The Linux Steam version of LIS has a startup script that sets the ssl certificate locations, you'll need to modify that to be able to MITM the server connections. The game makes requests to https://lis.os.eidos.com, but doesn't verify the certificate in the game. (requests to Feral Interactive, the company that ported LIS to OSX and Linux, do seem to be verified, but they don't matter much)

    #HAS_CURL=$( command -v curl-config )
    #if [ -n "$...
    #    SSL_CERT_FILE=...
    #else
    #    if ...
    #        SSL_CERT...
    #    elif
    #        SSL_CERT...
    #    elif
    #        SSL_CERT...
    #    fi
    #fi
    export SSL_CERT_FILE="/home/user/Downloads/mitmproxy-ca-cert.pem"
  • Set the second computer as the default gateway on the first computer by running sudo ip route add default via <ip>. Get the ip address of the second computer by running ip addr | grep inet.
  • Close other programs (except Terminal and Steam) to minimize noise
  • Run mitmdump -w output.mitmdump in Terminal, then start Life Is Strange through Steam
  • I had to alt-tab out and into the game to get it to work, you may need to do the same
  • You should see requests to /game/os_Ping start showing up. mitmdump will record all game communication. I loaded up the main menu, clicked on Choices, waited for the stats to show up, then closed the game and killed mitmdump with ctrl-c
  • Run mitmproxy --host -r output.mitmdump to view the requests.

If only the stats server communications weren't encrypted, would have been a lot easier.

I'm not sure how initial authentication works, the game sends an OS-AuthTicketData header with a string of characters, maybe each copy of the game has a unique auth ticket. It also requests user location data via an IP info lookup (/game/os_GetServiceInfo) and sends your friends list in the form of a list of user IDs (/game/SetUserProfileFriends), presumably for the friend stats response (/game/GetFriendsProfileStats) but this currently returns nothing. The main thing I'm interested in is /game/CommunityFactsGetEpisode, which takes a query param of i16_episode={episode number} and returns json:

"d" : {
    "results" : [
        {
            "__metadata" : {
                "uri" : "https://lis.os.eidos.com/game/communityfactreturns('BirdDead')",
                "type" : "game.communityfactreturn"
            },
            "f_rate" : "0.638969873663751",
            "i64_totalCount" : 2058,
            "i64_trueCount" : 1315,
            "i16_ep" : 1,
            "s_factID" : "BirdDead"
        },
        {
            "__metadata" : {
                "uri" : "https://lis.os.eidos.com/game/communityfactreturns('BirdSaved')",
                "type" : "game.communityfactreturn"
            },
            "f_rate" : "0.361030126336249",
            "i64_totalCount" : 2058,
            "i64_trueCount" : 743,
            "i16_ep" : 1,
            "s_factID" : "BirdSaved"
        }, ...

Which is exactly what I was looking for ^_^

whats-needed.md Raw

Logging tests for what headers are necessary.

$ curl https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
{"s_ErrorsCodes":"501","s_ErrorsMessages":"Invalid","s_ErrorsDetails":"Missing mandatory authentication headers: (OS-AuthProvider)."}
$ curl -H "Accept: application/json" -H "OS-AuthProvider: 6" https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
{"s_ErrorsCodes":"501","s_ErrorsMessages":"Invalid","s_ErrorsDetails":"Missing mandatory authentication headers: (OS-AuthTicketSize, OS-AuthTicketData|OS-AuthTokenData)."}
$ curl -H "Accept: application/json" -H "OS-AuthProvider: 6" -H "OS-AuthTicketData: [redacted]" -H "OS-AuthTicketSize: 44" https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
{"s_ErrorsCodes":"501","s_ErrorsMessages":"Invalid","s_ErrorsDetails":"No UID header found."}
$ curl -H "Accept: application/json" -H "OS-AuthProvider: 6" -H "OS-AuthTicketData: [redacted]" -H "OS-AuthTicketSize: 44" -H "OS-UID: [redacted]" https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
{"s_ErrorsCodes":"801","s_ErrorsMessages":"Missing Header","s_ErrorsDetails":"HTTP header OS-Platform is required."}
$ curl -H "Accept: application/json" -H "OS-AuthProvider: 6" -H "OS-AuthTicketData: [redacted]" -H "OS-AuthTicketSize: 44" -H "OS-UID: [redacted]" -H "OS-Platform: steam"  https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
{"s_ErrorsCodes":"800","s_ErrorsMessages":"Invalid Header","s_ErrorsDetails":"Header OS-Platform=steam and header OS-System=null are not compatible. Either neither value or both values must be specified. If both values are specified, they must be compatible with eachother."}
$ curl -H "Accept: application/json" -H "OS-AuthProvider: 6" -H "OS-AuthTicketData: [redacted]" -H "OS-AuthTicketSize: 44" -H "OS-UID: [redacted]" -H "OS-Platform: steam" -H "OS-System: linux" https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
{"d" : {"results" : [{"__metadata" : {"uri" : "https://lis.os.eidos.com/game/communityfactreturns('AddedNameToVortex')", "type" : "game.communityfactreturn"}, "f_rate" : "0.610894941634241", "i64_totalCount" : 514, "i64_trueCount" : 314, "i16_ep" : 3, "s_factID" : "AddedNameToVortex"}, ...