NachoCheese

Newbie
Dec 10, 2017
96
139
Is it just me or is it a slow download?? if it is i can give you a faster one ;)
If you've downloaded the client and let it update, you should already have all of the images (they are downloaded before authenticating through Patreon). Those files should be available at `%LOCALAPPDATA%Low\Sandlust Games Ltd\Glamour\images` as PNGs with no file suffix.
 

Maid Lain

Well-Known Member
Modder
Game Developer
Apr 4, 2018
1,888
16,598
If one of you is a patreon who is certain they have the patreon exclusive content, if you want to help could you do this please:
1. Type and open 'regedit' in windows search
2. Copy and paste this in the top search bar: Computer\HKEY_CURRENT_USER\Software\Sandlust Games Ltd\Glamour
3. Tell me what the numbers you have in these two parentheses are

Lain is god.png



My first approach is modifying the PlayerPrefs.SetInt("pledge", this.pledge); statement with PlayerPrefs.SetInt("pledge", 1);, but the compiler complaints about "PlayerPrefs" name not existing. I'm not sure what to do, even tried installing Unity with VisualStudio2017, but it stays the same.
There were more places that you have to change than just that one place in the code. You have to load the dependency UnityEngine.CoreModule.dll

When that failed, I tried modifying the registry, so the PlayerPrefs settings should get another values, but somehow it doesn't work for now. Any clue where to check any of my leads? Am I lost (probably...).
Did I make a post talking about the registry? I thought I did but I guess not. The registry keys get reset every time you start the game, and you can't change them after the game starts. Also setting the Glamour registry keys to read only will cause the game to throw an exception and be stuck loading at the menu. You just have to change the code that sets the keys.


I actually said that the part you download is effectively just a glorified web browser, which is why I never bothered digging into scripts.dll. I observed the game state loop going out over HTTP and recognized that event, display and dialogue decisions were being made remotely (and passed via JSON to the client).
I don't think I saw any posts you made, I just saw others that posted saying they looked into the Assembly dll and they just assumed that was the only code stored client side which was wrong. I don't really know much about networking though and how to intercept packets. Would reading the actual contents of the requests/responses/JSON files be easy?

Clearly I don't know anything. With that out of the way, how has your investigation gone in seeing if your modifications were successful at unlocking the latest version?

Game events newer than two months will be available in the game. I don't know anything, but I suspect those would be working at the hotel, and something to do with a café. I have no idea how to trigger those events.
I wasn't being specific when I said that. =P It's just that I read the first couple pages of this thread and saw people saying that the game would never be cracked and was impossible to modify because of the online stuff but it's not really any different than every other Unity game so idk why people were saying that.
And lol, I still haven't confirmed if I actually have the new content or if the game only says I do.. I don't really want to play the game for 2 hours just to find out. xP
 

XxMikaelxX

Newbie
Jul 1, 2018
90
39
If you've downloaded the client and let it update, you should already have all of the images (they are downloaded before authenticating through Patreon). Those files should be available at `%LOCALAPPDATA%Low\Sandlust Games Ltd\Glamour\images` as PNGs with no file suffix.
i was talking about the cg`s Mega link, it was like 2 mb/s download for me and i think my link is faster
 

NachoCheese

Newbie
Dec 10, 2017
96
139
I don't really know much about networking though and how to intercept packets. Would reading the actual contents of the requests/responses/JSON files be easy?
DS took no effort in protecting the content of the messages (no encryption or obfuscation, it's just JSON over HTTP). I have about 75% of the notes required to draw up a protocol sequence diagram to document the interactions between client and server, but stopped working on that after perceiving little interest or benefit in doing so. Based on your interest, I'll try to finish that this week some time.

it's not really any different than every other Unity game so idk why people were saying that.
As far as a running program goes I'm sure it's like the vast majority of other Unity games out there. The difference is in where game state is kept and game decisions are made. Specifically, it appears that the game client connects to a server to which it passes instructions about user actions while the server responds with game state updates.

And lol, I still haven't confirmed if I actually have the new content or if the game only says I do.. I don't really want to play the game for 2 hours just to find out. xP
That's basically where I ended up as well.
 

goobdoob

Conversation Conqueror
Modder
Respected User
Dec 17, 2017
7,426
9,700
I don't really know much about networking though and how to intercept packets. Would reading the actual contents of the requests/responses/JSON files be easy?
Back in the day I used .
 

gisanaj

New Member
Oct 21, 2017
3
5
There were more places that you have to change than just that one place in the code. You have to load the dependency UnityEngine.CoreModule.dll

Did I make a post talking about the registry? I thought I did but I guess not. The registry keys get reset every time you start the game, and you can't change them after the game starts. Also setting the Glamour registry keys to read only will cause the game to throw an exception and be stuck loading at the menu. You just have to change the code that sets the keys.
I actually worked on the network side as well (as I am more used to it than reversing/compiling), and noticed that everytime the game launches, it sends the client ID to the game server, which then replies with the account information (version, pledge, patreon) that is dumped in the registry. I've modified that information client-side, so the game thinks that the pledge is made, but, as @NachoCheese say, all of the processing is done server-side, so the events associated with patreon accounts won't happen.

I don't think I saw any posts you made, I just saw others that posted saying they looked into the Assembly dll and they just assumed that was the only code stored client side which was wrong. I don't really know much about networking though and how to intercept packets. Would reading the actual contents of the requests/responses/JSON files be easy?
I made a python script to intercept the requests, print them out, and change some server replies so the client get the values I want, but the only meaningful thing sent to the server is the client ID (as far as I know), and it is a long, hash-type string, hard to bruteforce (and doing so would mess with other people's saved games).

If you want the script, I can send it to you, is not complicated to set it up, and would allow you to see the traffic in real time (actually, is work in progress, but works well as it is, it's just not pretty).

If one of you is a patreon who is certain they have the patreon exclusive content, if you want to help could you do this please:
1. Type and open 'regedit' in windows search
2. Copy and paste this in the top search bar: Computer\HKEY_CURRENT_USER\Software\Sandlust Games Ltd\Glamour
3. Tell me what the numbers you have in these two parentheses are

View attachment 142192
Actually, it would be useful to get one or two active client ID's, maybe via private message, and keeping in mind that it could alter the saved games (there isn't much to play, though, so...). I've already tried some values, and for the time being I would keep "6" as value, it has given me the full patreon access screen:

Capture.PNG
 

Maid Lain

Well-Known Member
Modder
Game Developer
Apr 4, 2018
1,888
16,598
I actually worked on the network side as well (as I am more used to it than reversing/compiling), and noticed that everytime the game launches, it sends the client ID to the game server, which then replies with the account information (version, pledge, patreon) that is dumped in the registry. I've modified that information client-side, so the game thinks that the pledge is made, but, as @NachoCheese say, all of the processing is done server-side, so the events associated with patreon accounts won't happen.

I made a python script to intercept the requests, print them out, and change some server replies so the client get the values I want, but the only meaningful thing sent to the server is the client ID (as far as I know), and it is a long, hash-type string, hard to bruteforce (and doing so would mess with other people's saved games).

If you want the script, I can send it to you, is not complicated to set it up, and would allow you to see the traffic in real time (actually, is work in progress, but works well as it is, it's just not pretty).

Actually, it would be useful to get one or two active client ID's, maybe via private message, and keeping in mind that it could alter the saved games (there isn't much to play, though, so...). I've already tried some values, and for the time being I would keep "6" as value, it has given me the full patreon access screen
Yeah I saw that the account info is stored in the registry and I assumed the game used those keys to check if it should send the patreon exclusive content but found out that wasn't the case and I think it only uses the registry values for the menu screen.

I also figured out how to run the game with the .Net debugger so I can read requests/responses using the locales window. Indeed the server still knows what pateron tier a user is without checking the registry and I thought changing the login response to always set pledge to 40 would unlock the patreon content but no luck.

And is 6 the value you're using for the game version? I was hoping that patron's just had a different version # for the patreon content but I think it's still set to version 5 for them as well.
 

Maid Lain

Well-Known Member
Modder
Game Developer
Apr 4, 2018
1,888
16,598
It turns out that my assumption of only needing to change the registry keys would unlock the patreon content was wrong, so I'm just going to post everything I have. I still think it's crackable but I don't really have the time to figure it out. I also still believe that someone smarter than me could crack this game pretty fast.

along with a list of changes I've made.


Assembly-CSharp.dll
Code:
Assembly assembly = Assembly.LoadFrom(Application.persistentDataPath + "/lain.dll");
Only thing I've modified is the file that the game uses for the scripts.dll which I've changed to lain.dll because any changes to scripts.dll get reset when the game is started. This allows you to make any modifications to the game code.


mono.dll
Patched mono library so that dnSpy can run the program in debug mode which allows you to read responses/requests/JSON files in the locales window.


scripts.dll (lain.dll)
LoginResponse:
Code:
public static LoginResponse Parse(string source)
{
    source = source.Replace("\"pledge\":0", "\"pledge\":40");
    Debug.Log("source: " + source);
    return JsonConvert.DeserializeObject<LoginResponse>(source);
}
Login responses from the server have the pledge amount changed from 0 to 40 whenever they are parsed.

Code:
public void UpdatePlayerPrefs()
{
    PlayerPrefs.SetInt("patron", 1);
    PlayerPrefs.SetInt("pledge", 40);
}
The registry values are changed to always update to these but I think the registry values are only used for the menu not for deciding to send the patreon content.


LoginScene:
Pretty sure I made some small change here but I don't remember what it was but this is just for the menu anyways so don't think it matters.


NetworkHelper:
Code:
    public static IEnumerator SendStatusRequest(string id)
    {
        WWWForm wwwform = new WWWForm();
        wwwform.AddField("id", id);
        WWW request = new WWW(PlayerPrefs.GetString("ServerUrl") + "/api/status", wwwform);
        yield return request;
        JObject jobject = JObject.Parse(request.text);
        PlayerPrefs.SetInt("patron", 1);
        PlayerPrefs.SetInt("pledge", 40);
        PlayerPrefs.SetInt("version", jobject["version"].ToObject<int>());
        yield break;
    }
Same changes as UpdatePlayerPrefs.


Also when the new update comes out I'd assume there will be changes to the scripts.dll so these changes will have to be added again to the new .dll in order to get the rest of the changes to the scripts dll.
 

gisanaj

New Member
Oct 21, 2017
3
5
Yeah I saw that the account info is stored in the registry and I assumed the game used those keys to check if it should send the patreon exclusive content but found out that wasn't the case and I think it only uses the registry values for the menu screen.

I also figured out how to run the game with the .Net debugger so I can read requests/responses using the locales window. Indeed the server still knows what pateron tier a user is without checking the registry and I thought changing this to always set pledge to 40 would unlock the patreon content but no luck.
Code:
public static LoginResponse Parse(string source)
{
    source = source.Replace("\"pledge\":0", "\"pledge\":40");
    Debug.Log("source: " + source);
    return JsonConvert.DeserializeObject<LoginResponse>(source);
}
And is 6 the value you're using for the game version? I was hoping that patron's just had a different version # for the patreon content but I think it's still set to version 5 for them as well.
I set 6 as the pledge value, as 40 froze my game, so I tried adding one per level, and 6 gave me "the most", but it could be wrong.
 

Maid Lain

Well-Known Member
Modder
Game Developer
Apr 4, 2018
1,888
16,598
I set 6 as the pledge value, as 40 froze my game, so I tried adding one per level, and 6 gave me "the most", but it could be wrong.
Ah, well 40 definitely works but those values are only used for displaying when you get updates on the main menu I think so I don't think it really matters what it is set to. If you want it set to 40 though you can download my files in the post above.
 

NachoCheese

Newbie
Dec 10, 2017
96
139
Back in the day I used .
I still do.

Actually, it would be useful to get one or two active client ID's, maybe via private message, and keeping in mind that it could alter the saved games (there isn't much to play, though, so...). I've already tried some values, and for the time being I would keep "6" as value, it has given me the full patreon access screen:
I'd be careful about using those. If I were as paranoid about piracy as DS apparently is, I'd have something in place to automatically blacklist Patreons whos' IDs appear to be logged in from two or more places at once.

It turns out I don't have the time this week for a full protocol document, so I'll just throw something together on here in a follow-up post.
 
  • Like
Reactions: Hlextor

NachoCheese

Newbie
Dec 10, 2017
96
139
Rough Protocol Documentation: Glamour v0.5.

Note: These notes are based on a client that had already been bootstrapped, and with no Patreon account. Subsequent runs were intended to determine differences between authenticated and non-authenticated operation, but it doesn't look like much will change.

Client communicates with API endpoint at http:// sandlustgames.com /

Upon startup, client issues a GET request for /resources/game-data
API responds with a JSON object representing key-value pairs of filenames and what appear to be MD5 hashes of those filenames:

Code:
{
  "scripts.dll": "27b58bc01180293c88f72855c96616e8",
  "content": "b2e176d524e644cf511b415468b2df35",
  "scenes": "84dd8df8994c2b44c935b149ea76dd5a"
}
Client issues a GET request for /resources
API responds with a JSON object representing key-value pairs of filenames and what appear to be file sizes (number of bytes) of each file:

Code:
{
...
  "wardrobe/casual-3-big":1220652,
  "wardrobe/casual-3":22633,
  "wardrobe/evening-1-big":1019972,
  "wardrobe/evening-1":13460,
  "wardrobe/evening-2-big":1052070,
...
}
For missing (or presumably the wrong size) files, the client issues a GET request for the resource (ie: GET /images/backgrounds/cafe-hall)
The API responds with the resource (interestingly enough, still passed from Express, and not as a static resource from nginx)

After all resources have been loaded, client issues a POST request for /api/status with the following keys:
  • id: a UUID. I'm assuming this is used to look up the user's information through the Patreon API.
The API responds with a JSON object with the following keys:
  • patron: 0 (Assuming boolean: if this user is a Patron or not)
  • pledge: 0 (Assuming pledge value)
  • version: 5 (Assuming minor version of the client)
The client issues a POST request for /api/login with the following keys:
  • id: a UUID, same as the one previously seen.
  • lang: en the language mode in which the client should operate.
The API responds with a JSON object with the following keys:
  • status: ok
  • token: (a 164 byte string that appears to have several Base64 encoded segments separated by the '.' character)
    • Segment 1: a 36 byte Base64 encoded JSON object with the following keys:
      • alg: HS256
      • typ: JWT
      • Note:
    • Segment 2: an 83 byte Base64 encoded JSON object with the following keys:
      • id: The previously seen UUID
      • iat: The number of seconds since the UNIX epoch.
    • Segment 3: a 43 byte string that appears to be random data. (this base64 decodes to 32 bytes, so this is quite likely)
  • saves: an array (empty)
  • patron: false (if the user is a patron or not)
  • pledge: 0 (the pledge level of the patron)
The client issues a POST request for /api/start with the following keys:
  • token: the previously seen token
The API responds with a JSON object with the following keys:
  • state: event
  • simpleText: A JSON object with the following keys:
    • x: 890 (x offset in pixels)
    • y: 267 (y offset in pixels)
    • width: 700 (width value in pixels)
    • color: #f8dcab (HTML color code)
    • text: <size=34><color=#ffffff>Hi!</color></size>\nMy name is Kate, what's yours? Wait, don't tell me. I'm not supposed to know about you... (Literal HTML)
  • sprites: Array containing one or more JSON objects with asset names and x,y offsets
  • buttons: Array containing one or more JSON objects with asset names and x,y offsets
  • leftButtons: Array containing one or more JSON objects with asset names and x,y offsets
  • continue: boolean
When the user clicks on anything, the client issues a POST request for /api/update with the following keys:
  • token: previously seen token
  • action: doAction, dialogueAction (named method on the server?)
  • parameter: next, dialogue:intro-doubt, dialogue:intro-principal-2, sign, intro-after-college, event:intro-12, (additional parameters to pass to that method?)
The API responds with a JSON object with the same keys as seen above (to start)

At some point along the line, the API responds to POST requests for /api/update with the following keys:
  • state: location
  • day: 1 (or 0?)
  • datetime: 14:00 SA 29 (literal string representing the date and time)
  • location: background image name
  • locationDescription: LIVING ROOM (literal name of the location)
  • money: 0 (money on the character)
  • energy: 81 (energy of the character)
  • hunger: 81 (hunger of the character)
  • arousal: 5 (arousal of the character)
  • items: array containing JSON objects with the following keys
    • id: name of object
    • visible: true
    • state: string value (seen: :closed, )
  • movements: array containing JSON objects representing where the character can move to (left location menu)
  • places: array containing JSON objects representing sub spaces within the current area (right location menu)
  • characters: array containing JSON objects representing characters who are present I'm assuming?
  • sprites: array containing JSON objects representing sprites and where/how to display them on the screen.
  • activeAreas: array containing JSON objects representing active areas and where/how to display them.
 
2.70 star(s) 187 Votes