Quote:
Originally Posted by Apourtartt
Oh sorry, did not noticed that the user agent value was changing ! mb
Same about the guid, I thought it had to be registered because of my first assumption.
Well, so here we go, I reversed it I found it out :
get platformGameUserId, sha1 it, store it
get installationid (also known as gfuid), sha256 it and store it
get chromeversion (the "C2.1.22.784"), sha1 it and store it
get the encryption key (currently : "edd76c5219499d00da2c5a9e2b703c03d5aaf67d1f16c7ecd 3165869921148f7", will maybe change, or maybe it's generated, will take a look later)
then mix it all up together :
pre_code = encryptionKey + chromeversion + install + platform
pre_code = sha256(pre_code)
code = 2 first character of platformGameUserId not crypted + 8 first character from pre_code
here you go
if you want a poc :
btw razzort, you're receiving "not allowed to create code" because the request has a lifetime
|
You are almost right but not quite, with Xeno's help I've reverse-engineered the entire function and the first thing to point out is that it does not use the chrome version but in fact the gameforge client version - you can extract it from the exe. The second thing is that the string which you called the encryption key is actually a hash of a portion of the gameforge client certificate which is embedded in the exe. You can obtain it by using Xeno's

(but you need to extract it by hand since the tool doesn't have an extract function yet). After you got the certificate you need to decrypt it using this password:
Then you take the first certificate from the exported p12 (there are 2 of them but only the first is used here) save it to the file and set the line endings to "LF" (it's important). This file is your "hashCertificate" used to generate the account hash. The third thing is that the algorithm in some cases actually swaps the sha1 for sha256 and vice versa. That depends on the first number from the installation id.
To generate the hash itself you need to do this:
Code:
const firstNumber = getFirstNumberFromString(installationID);
if (firstNumber == undefined || firstNumber % 2 === 0) {
return (
accountID.substr(0, 2) +
getStringFromLeft(
sha256(
sha256(hashCertificate) +
sha1(getCharFromType(CharType.RETURN_C) + clientVersion.version) +
sha256(installationID) +
sha1(accountID)
),
8
)
);
} else {
return (
accountID.substr(0, 2) +
getStringFromRight(
sha256(
sha1(hashCertificate) +
sha256(getCharFromType(CharType.RETURN_C) + clientVersion.version) +
sha1(installationID) +
sha256(accountID)
),
8
)
);
}
Where getCharFromType simply returns "C" on production. This will generate you a correct hash but... that's not all. The endpoint that generates codes is also protected and requires tricking gameforge into thinking that you have the launcher open, otherwise it won't work. We can do that fairly simple, just send a POST request to

with this body:
Code:
{
client_installation_id: installationId,
client_locale: "usa_eng",
client_session_id: random_uuid(),
client_version_info: {
branch: clientVersion.branch,
commit_id: clientVersion.commitId,
version: clientVersion.version,
},
id: 1,
localtime: Date.now().toFormat("yyyy-MM-dd'T'HH:mm:ssZZZ"),
start_count: 1,
start_time: 7000,
type: "start_time",
}
You can notice here that the client_version_info object has things like branch and commitId. You can easily extract them from the gfclient.exe itself.
Note that unlocking the endpoint only works for the same IP address as the request origin so both event and code request need to be sent from the same host.