Last active 1618884604

Life Is Strange stats server investigation

blha303 revised this gist 1470227968. Go to revision

1 file changed, 15 insertions, 2 deletions

docs.md

@@ -1,3 +1,5 @@
1 + The server returns `X-Powered-By: Lara Croft` for each response, cool easter egg.
2 +
1 3 ### On game start
2 4
3 5 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.
@@ -13,11 +15,12 @@ Necessary headers for each request (may vary, untested):
13 15 * `OS-System: linux`, ditto
14 16
15 17 1. `/game/os_Ping`
16 - * Likely confirming that the service is up, needs to succeed otherwise the game marks itself as offline
18 + * Likely confirming that the service is up
17 19 * Returns `{ 6739 }`, different number each time
18 20
19 21 2. `/game/os_GetServiceInfo`
20 - * 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](https://gist.github.com/blha303/8c5b925f95c23c08197ac3a82e1bee15).
22 + * Returns information about the IP address used for connection and a list of metadata and endpoints.
23 + * Example output: https://gist.github.com/blha303/8c5b925f95c23c08197ac3a82e1bee15
21 24
22 25 3. `/game/os_Ping` again
23 26
@@ -39,6 +42,16 @@ Necessary headers for each request (may vary, untested):
39 42 * 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'
40 43 * Example response: https://gist.github.com/blha303/d00b1c34728cc21101b7fc198bbf3371
41 44
45 + 8. `/game/SetUserProfileFriends`
46 + * Sends list of UIDs for user's friends list
47 + * POST requests, `["UID", ...]`
48 + * Returns `{ 0 }`, friends service probably disabled
49 +
50 + 9. `/game/GetSeasonPassOfferIdList`
51 + * No clue what this is for, you'd think it would show information on the episodes but it just returns the same thing five times.
52 + * Query params: `s_region`, mine says 'na', not applicable?
53 + * Returns https://gist.github.com/blha303/cba709bc7b9db5e8122c46133fdedbcb
54 +
42 55 ### Occasionally
43 56
44 57 1. `/game/AddMetrics`

blha303 revised this gist 1470226404. Go to revision

1 file changed, 8 insertions, 6 deletions

docs.md

@@ -39,6 +39,13 @@ Necessary headers for each request (may vary, untested):
39 39 * 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'
40 40 * Example response: https://gist.github.com/blha303/d00b1c34728cc21101b7fc198bbf3371
41 41
42 + ### Occasionally
43 +
44 + 1. `/game/AddMetrics`
45 + * Is called every few minutes regardless of the client configuration returned by `os_GetServiceInfo`
46 + * POST request, https://gist.github.com/blha303/b79ba499a2ea61880d3fc8dc812f7eb4
47 + * Returns nothing at all
48 +
42 49 ### On opening the choices screen (from the main menu or after each episode)
43 50
44 51 1. `/game/CommunityFactsGetEpisode`
@@ -55,12 +62,7 @@ Necessary headers for each request (may vary, untested):
55 62
56 63 Making me listen to Obstacles again >_<
57 64
58 - 1. `/game/AddMetrics`
59 - * Unsure of significance given that UpdateUserProfileGameSpecific gives more succinct information
60 - * POST request, https://gist.github.com/blha303/b79ba499a2ea61880d3fc8dc812f7eb4
61 - * Returns nothing at all
62 -
63 - 2. `/game/UpdateUserProfileGameSpecific`
65 + 1. `/game/UpdateUserProfileGameSpecific`
64 66 * POST request, summarizes choices made. Called when credits start
65 67 * JSON data, https://gist.github.com/blha303/144499eeb67dface9a83371c17f9513b
66 68 * Returns `{ 0 }`, maybe means profile isn't updated when the given UID has already completed the given chapter

blha303 revised this gist 1470226273. Go to revision

1 file changed, 20 insertions, 3 deletions

docs.md

@@ -7,7 +7,7 @@ Necessary headers for each request (may vary, untested):
7 7 * `Accept: application/json`, otherwise the API returns XML (obviously if you're fine with that you can omit this)
8 8 * `OS-AuthProvider: 6`, unsure of the purpose of this
9 9 * `OS-AuthTicketData: [string]`, begins with CAEQ on my copy of the game, unsure if variable
10 - * `OS-AuthTicketSize: 44`, not the length of the above auth ticket as the initial ticket is much longer
10 + * `OS-AuthTicketSize: 44`, the length of the above string
11 11 * `OS-UID: [string]`, steam user ID, or some other form of identification in standalone versions probably
12 12 * `OS-Platform: steam`, the user's platform, needs more testing to get options
13 13 * `OS-System: linux`, ditto
@@ -31,14 +31,15 @@ Necessary headers for each request (may vary, untested):
31 31 6. `/game/CreateUserProfile`
32 32 * 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
33 33 * Query params: `s_uid` is my steam user ID
34 - * Returns `{ true }`
34 + * Returns `{ true }` and a header `OS-AuthResponse` containing the new `OS-AuthTicketData` to be used for subsequent requests
35 35
36 36 7. `/game/GetTodaysInfocast`
37 37 * Returns a list of messages to be scrolled at the bottom of the main menu
38 + * Idling on the menu calls this periodically
38 39 * 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'
39 40 * Example response: https://gist.github.com/blha303/d00b1c34728cc21101b7fc198bbf3371
40 41
41 - ### On opening the choices menu item
42 + ### On opening the choices screen (from the main menu or after each episode)
42 43
43 44 1. `/game/CommunityFactsGetEpisode`
44 45 * Returns a list of choices for a given episode, and the percentage and raw numbers for each choice
@@ -49,3 +50,19 @@ Necessary headers for each request (may vary, untested):
49 50 * Apparently would return the same as `/game/CommunityFactsGetEpisode` with additional info on friend stats, but currently returns nothing
50 51 * Query params: `i16_episode`, the given episode number; `s_uid`, the steam/other user id
51 52 * Currently returns `{ "d": {"results": [] } }`
53 +
54 + ### On finishing an episode
55 +
56 + Making me listen to Obstacles again >_<
57 +
58 + 1. `/game/AddMetrics`
59 + * Unsure of significance given that UpdateUserProfileGameSpecific gives more succinct information
60 + * POST request, https://gist.github.com/blha303/b79ba499a2ea61880d3fc8dc812f7eb4
61 + * Returns nothing at all
62 +
63 + 2. `/game/UpdateUserProfileGameSpecific`
64 + * POST request, summarizes choices made. Called when credits start
65 + * JSON data, https://gist.github.com/blha303/144499eeb67dface9a83371c17f9513b
66 + * Returns `{ 0 }`, maybe means profile isn't updated when the given UID has already completed the given chapter
67 +
68 + No API requests upon completing an achievement.

blha303 revised this gist 1470224832. Go to revision

1 file changed, 51 insertions, 3 deletions

docs.md

@@ -1,3 +1,51 @@
1 - * `/game/CommunityFactsGetEpisode`
2 - * Query param `i16_episode`: episode number (e.g 1,2,3,4,5)
3 - * Returns xml unless header `Accept: application/json` is specified
1 + ### On game start
2 +
3 + 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.
4 +
5 + Necessary headers for each request (may vary, untested):
6 +
7 + * `Accept: application/json`, otherwise the API returns XML (obviously if you're fine with that you can omit this)
8 + * `OS-AuthProvider: 6`, unsure of the purpose of this
9 + * `OS-AuthTicketData: [string]`, begins with CAEQ on my copy of the game, unsure if variable
10 + * `OS-AuthTicketSize: 44`, not the length of the above auth ticket as the initial ticket is much longer
11 + * `OS-UID: [string]`, steam user ID, or some other form of identification in standalone versions probably
12 + * `OS-Platform: steam`, the user's platform, needs more testing to get options
13 + * `OS-System: linux`, ditto
14 +
15 + 1. `/game/os_Ping`
16 + * Likely confirming that the service is up, needs to succeed otherwise the game marks itself as offline
17 + * Returns `{ 6739 }`, different number each time
18 +
19 + 2. `/game/os_GetServiceInfo`
20 + * 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](https://gist.github.com/blha303/8c5b925f95c23c08197ac3a82e1bee15).
21 +
22 + 3. `/game/os_Ping` again
23 +
24 + 4. `/game/SEM_Login`
25 + * Probably for the Square Enix account login
26 + * Query params: `s_type` is 'UID' here; `s_value` is my steam user ID, a long string of digits
27 + * 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" }`
28 +
29 + 5. It sends the above request twice, unclear why
30 +
31 + 6. `/game/CreateUserProfile`
32 + * 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
33 + * Query params: `s_uid` is my steam user ID
34 + * Returns `{ true }`
35 +
36 + 7. `/game/GetTodaysInfocast`
37 + * Returns a list of messages to be scrolled at the bottom of the main menu
38 + * 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'
39 + * Example response: https://gist.github.com/blha303/d00b1c34728cc21101b7fc198bbf3371
40 +
41 + ### On opening the choices menu item
42 +
43 + 1. `/game/CommunityFactsGetEpisode`
44 + * Returns a list of choices for a given episode, and the percentage and raw numbers for each choice
45 + * Query params: `i16_episode`, the given episode number
46 + * Example response for episode 1: https://gist.github.com/blha303/cec90d1d2e351c33d39ddddd880cd252
47 +
48 + 2. `/game/GetFriendsProfileStats`
49 + * Apparently would return the same as `/game/CommunityFactsGetEpisode` with additional info on friend stats, but currently returns nothing
50 + * Query params: `i16_episode`, the given episode number; `s_uid`, the steam/other user id
51 + * Currently returns `{ "d": {"results": [] } }`

Steven Smith revised this gist 1470204122. Go to revision

1 file changed, 16 insertions

whats-needed.md(file created)

@@ -0,0 +1,16 @@
1 + Logging tests for what headers are necessary.
2 +
3 + ```
4 + $ curl https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
5 + {"s_ErrorsCodes":"501","s_ErrorsMessages":"Invalid","s_ErrorsDetails":"Missing mandatory authentication headers: (OS-AuthProvider)."}
6 + $ curl -H "Accept: application/json" -H "OS-AuthProvider: 6" https://lis.os.eidos.com/game/CommunityFactsGetEpisode?i16_episode=3
7 + {"s_ErrorsCodes":"501","s_ErrorsMessages":"Invalid","s_ErrorsDetails":"Missing mandatory authentication headers: (OS-AuthTicketSize, OS-AuthTicketData|OS-AuthTokenData)."}
8 + $ 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
9 + {"s_ErrorsCodes":"501","s_ErrorsMessages":"Invalid","s_ErrorsDetails":"No UID header found."}
10 + $ 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
11 + {"s_ErrorsCodes":"801","s_ErrorsMessages":"Missing Header","s_ErrorsDetails":"HTTP header OS-Platform is required."}
12 + $ 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
13 + {"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."}
14 + $ 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
15 + {"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"}, ...
16 + ```

Steven Smith revised this gist 1470203518. Go to revision

1 file changed, 3 insertions

docs.md(file created)

@@ -0,0 +1,3 @@
1 + * `/game/CommunityFactsGetEpisode`
2 + * Query param `i16_episode`: episode number (e.g 1,2,3,4,5)
3 + * Returns xml unless header `Accept: application/json` is specified

Steven Smith revised this gist 1470202445. Go to revision

1 file changed, 12 insertions, 12 deletions

revlis.md

@@ -7,18 +7,18 @@ First, you'll need a copy of Life Is Strange on Steam. Second, you'll need to ha
7 7 * Edit `.steam/steam/steamapps/common/Life Is Strange/LifeIsStrange.sh` on the first machine, and edit the lines below `HAS_CURL` like so:
8 8
9 9 ```
10 - \#HAS_CURL=$( command -v curl-config )
11 - \#if [ -n "$...
12 - \# SSL_CERT_FILE=...
13 - \#else
14 - \# if ...
15 - \# SSL_CERT...
16 - \# elif
17 - \# SSL_CERT...
18 - \# elif
19 - \# SSL_CERT...
20 - \# fi
21 - \#fi
10 + #HAS_CURL=$( command -v curl-config )
11 + #if [ -n "$...
12 + # SSL_CERT_FILE=...
13 + #else
14 + # if ...
15 + # SSL_CERT...
16 + # elif
17 + # SSL_CERT...
18 + # elif
19 + # SSL_CERT...
20 + # fi
21 + #fi
22 22 export SSL_CERT_FILE="/home/user/Downloads/mitmproxy-ca-cert.pem"
23 23 ```
24 24

Steven Smith revised this gist 1470202421. Go to revision

1 file changed, 2 insertions

revlis.md

@@ -6,6 +6,7 @@ First, you'll need a copy of Life Is Strange on Steam. Second, you'll need to ha
6 6 * Set up transparent proxying http://docs.mitmproxy.org/en/stable/transparent/linux.html
7 7 * Edit `.steam/steam/steamapps/common/Life Is Strange/LifeIsStrange.sh` on the first machine, and edit the lines below `HAS_CURL` like so:
8 8
9 + ```
9 10 \#HAS_CURL=$( command -v curl-config )
10 11 \#if [ -n "$...
11 12 \# SSL_CERT_FILE=...
@@ -19,6 +20,7 @@ First, you'll need a copy of Life Is Strange on Steam. Second, you'll need to ha
19 20 \# fi
20 21 \#fi
21 22 export SSL_CERT_FILE="/home/user/Downloads/mitmproxy-ca-cert.pem"
23 + ```
22 24
23 25 * 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`.
24 26 * Close other programs (except Terminal and Steam) to minimize noise

Steven Smith revised this gist 1470202400. Go to revision

1 file changed, 12 insertions, 12 deletions

revlis.md

@@ -6,18 +6,18 @@ First, you'll need a copy of Life Is Strange on Steam. Second, you'll need to ha
6 6 * Set up transparent proxying http://docs.mitmproxy.org/en/stable/transparent/linux.html
7 7 * Edit `.steam/steam/steamapps/common/Life Is Strange/LifeIsStrange.sh` on the first machine, and edit the lines below `HAS_CURL` like so:
8 8
9 - #HAS_CURL=$( command -v curl-config )
10 - #if [ -n "$...
11 - # SSL_CERT_FILE=...
12 - #else
13 - # if ...
14 - # SSL_CERT...
15 - # elif
16 - # SSL_CERT...
17 - # elif
18 - # SSL_CERT...
19 - # fi
20 - #fi
9 + \#HAS_CURL=$( command -v curl-config )
10 + \#if [ -n "$...
11 + \# SSL_CERT_FILE=...
12 + \#else
13 + \# if ...
14 + \# SSL_CERT...
15 + \# elif
16 + \# SSL_CERT...
17 + \# elif
18 + \# SSL_CERT...
19 + \# fi
20 + \#fi
21 21 export SSL_CERT_FILE="/home/user/Downloads/mitmproxy-ca-cert.pem"
22 22
23 23 * 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`.

Steven Smith revised this gist 1470202361. Go to revision

1 file changed, 59 insertions

revlis.md(file created)

@@ -0,0 +1,59 @@
1 + 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.
2 +
3 + 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)
4 +
5 + * Set up mitmproxy on a laptop or other second computer that won't be running the game: http://docs.mitmproxy.org/en/stable/install.html
6 + * Set up transparent proxying http://docs.mitmproxy.org/en/stable/transparent/linux.html
7 + * Edit `.steam/steam/steamapps/common/Life Is Strange/LifeIsStrange.sh` on the first machine, and edit the lines below `HAS_CURL` like so:
8 +
9 + #HAS_CURL=$( command -v curl-config )
10 + #if [ -n "$...
11 + # SSL_CERT_FILE=...
12 + #else
13 + # if ...
14 + # SSL_CERT...
15 + # elif
16 + # SSL_CERT...
17 + # elif
18 + # SSL_CERT...
19 + # fi
20 + #fi
21 + export SSL_CERT_FILE="/home/user/Downloads/mitmproxy-ca-cert.pem"
22 +
23 + * 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`.
24 + * Close other programs (except Terminal and Steam) to minimize noise
25 + * Run `mitmdump -w output.mitmdump` in Terminal, then start Life Is Strange through Steam
26 + * I had to alt-tab out and into the game to get it to work, you may need to do the same
27 + * 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
28 + * Run `mitmproxy --host -r output.mitmdump` to view the requests.
29 +
30 + If only the stats server communications weren't encrypted, would have been a lot easier.
31 +
32 + 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:
33 +
34 + "d" : {
35 + "results" : [
36 + {
37 + "__metadata" : {
38 + "uri" : "https://lis.os.eidos.com/game/communityfactreturns('BirdDead')",
39 + "type" : "game.communityfactreturn"
40 + },
41 + "f_rate" : "0.638969873663751",
42 + "i64_totalCount" : 2058,
43 + "i64_trueCount" : 1315,
44 + "i16_ep" : 1,
45 + "s_factID" : "BirdDead"
46 + },
47 + {
48 + "__metadata" : {
49 + "uri" : "https://lis.os.eidos.com/game/communityfactreturns('BirdSaved')",
50 + "type" : "game.communityfactreturn"
51 + },
52 + "f_rate" : "0.361030126336249",
53 + "i64_totalCount" : 2058,
54 + "i64_trueCount" : 743,
55 + "i16_ep" : 1,
56 + "s_factID" : "BirdSaved"
57 + }, ...
58 +
59 + Which is exactly what I was looking for \^_\^
Newer Older