Tutorial PoC: how to decrypt (and unpack) SRPGStudio data.dts if it is encrypted

notamuted

Newbie
May 12, 2018
76
71
Thanks, but I can't find the download link for Crusader Princess. There's an Anime-sharing link on that forum, but neither of the download links there works (either blocked by firewall or requires premium account etc.) Does anyone have a meganz, gofile, pixeldrain etc. link?
Actually, you can download it from anime-sharing. I recommend mexashare, since it has the fastest download speed without account. But you need to pass through wait-1-one-minute crap (adblock recommended) and captcha.
 

justaplayer69

Member
Nov 29, 2023
294
341
Actually, you can download it from anime-sharing.
Nope, I'm saying that I can't, neither of the links on anime-sharing work for me.
But you need to pass through wait-1-one-minute crap (adblock recommended) and captcha.
This involves enabling spyware... My firewall blocks these, and the captcha doesn't work at all (gives JS error). Is there a meganz, gofile, pixeldrain etc. download? Those share sites work even if their spyware part is blocked. Torrent would do too as long as no login required.

BTW, I've managed to get some game samples. Netoria Tactics isn't encrypted, and all the other use the default "key" key. Also I've checked SRPG's manual and there seems to be no option on the user interface to specify the key at all (it's just a single checkbox, that's it). So I have no samples with a custom key, hence I'm not sure which file stores it. I could rule out system.dat though.
 

notamuted

Newbie
May 12, 2018
76
71
Is there a meganz, gofile, pixeldrain etc. download?
Not that I know of

Also I've checked SRPG's manual and there seems to be no option on the user interface to specify the key at all (it's just a single checkbox, that's it).
Oh, then I guess it generetes a random key if the checkbox is set, and the key is not neccecarily a recognizable text.

and all the other use the default "key" key
I am pretty sure oathbreaker does not use the default key. Did you check that game?
 

justaplayer69

Member
Nov 29, 2023
294
341
Oh, then I guess it generetes a random key if the checkbox is set
Nope, no random, it seems to use the default "key" key, always. I don't know where the key could be set in the editor (is this a special extra price feature maybe?).

I am pretty sure oathbreaker does not use the default key. Did you check that game?
Nope, because you haven't given a download link (your link just leads to a game info page without an actual download link).

The games I could find (not necessarily on f95zone, some are from itchio) and checked are:
- Heelfall
- Netoria Tactics Revolution Carwyn Triumphant
- Azure Orphanage
- GNDH
- FEZG
- Seas of Novis
- Snowbreak
- Tournament of Kings

No custom key among these.
 

justaplayer69

Member
Nov 29, 2023
294
341
Oh, come on! It literrally has "Downloads" section with multiple links
Dude, patreon links! Those are not free, need an account, and patreon site doesn't even work with spyware disabled...

This is how patreon looks like when its telemetry is blocked (throws JS errors of course, and no links working, you can't click anywhere, not even the search box nor the "Log in" works):
patreon_shit.png
So no, I do not consider this a proper download link, sorry.
 

notamuted

Newbie
May 12, 2018
76
71
Those are not free, need an account, and patreon site doesn't even work with spyware disabled...
Its free and you don't need an account.
Well, okay, nvm. So I see 3 choices for you: 1) Search the game in other places (it should be somewhere in itch.io if that site works for you) 2) Use a different browser\device 3) Find an another game that does not use the default key (I dont think there are many out there)
 

uneasy_speedy

Newbie
Feb 22, 2021
70
133
Nope, no random, it seems to use the default "key" key, always. I don't know where the key could be set in the editor (is this a special extra price feature maybe?).

Nope, because you haven't given a download link (your link just leads to a game info page without an actual download link).

The games I could find (not necessarily on f95zone, some are from itchio) and checked are:
- Heelfall
- Netoria Tactics Revolution Carwyn Triumphant
- Azure Orphanage
- GNDH
- FEZG
- Seas of Novis
- Snowbreak
- Tournament of Kings

No custom key among these.
Just asking, you're looking for any game with a non-default key? If you tell me how to encrypt the game, I have srpg studio so I can just make a shitty default project and encrypt it.
 

justaplayer69

Member
Nov 29, 2023
294
341
Just asking, you're looking for any game with a non-default key? If you tell me how to encrypt the game, I have srpg studio so I can just make a shitty default project and encrypt it.
Wow, that would be really awesome!

If you could create 4 variants of the default project game that would be invaluable help for reverse engineering how the custom key is stored!
1. one without encryption (for a baseline)
2. one with the default key
3. one with an "abcde" key
4. and finally one with "bcdef" key

The first is needed to establish a baseline (get the plain text version of encryption for that particular SRPG version). The second is needed to rule out false positives, and finally comparing the third and fourth would allow me to locate where and how is the custom key stored in the exported game. For this I would need two known custom keys encrypting the same data.

If you could really create these 4 and upload it on meganz, gofile, pixeldrain (or just attach it here if possible), that would help me a lot and I would be extremely grateful!

About encrypting the game, there's a checkbox on the "Release Project" window. About specifying the key, I have no clue, I couldn't find that in the SRPG manual.
 

uneasy_speedy

Newbie
Feb 22, 2021
70
133
Wow, that would be really awesome!

If you could create 4 variants of the default project game that would be invaluable help for reverse engineering how the custom key is stored!
1. one without encryption (for a baseline)
2. one with the default key
3. one with an "abcde" key
4. and finally one with "bcdef" key

The first is needed to establish a baseline (get the plain text version of encryption for that particular SRPG version). The second is needed to rule out false positives, and finally comparing the third and fourth would allow me to locate where and how is the custom key stored in the exported game. For this I would need two known custom keys encrypting the same data.

If you could really create these 4 and upload it on meganz, gofile, pixeldrain (or just attach it here if possible), that would help me a lot and I would be extremely grateful!

About encrypting the game, there's a checkbox on the "Release Project" window. About specifying the key, I have no clue, I couldn't find that in the SRPG manual.
I only figured out the first two, I couldn't find any way to set a custom key. I don't think there is a native way to do it. That said, I can always send more projects for testing as needed.
 

justaplayer69

Member
Nov 29, 2023
294
341
I only figured out the first two, I couldn't find any way to set a custom key. I don't think there is a native way to do it. That said, I can always send more projects for testing as needed.
Thank you very much! Yeah, I couldn't find the custom key input either.

Anyway, I've implemented what I could, from now on GAME RIPPER can rip SRPG Studio games too!
 

notamuted

Newbie
May 12, 2018
76
71
I only figured out the first two, I couldn't find any way to set a custom key. I don't think there is a native way to do it. That said, I can always send more projects for testing as needed.
I've checked these...
1) Can you next time add into the projects some images? It seems these projects contain only *.js script files and 'Project.srpg' file. From which only the latter is encrypted.
2) Assuming 'unencrypt' is indeed not encrypted and for both these projects 'Project.srpg' files are identical - I conclude that 'encrypt' project is encrypted using non-default key.

I suspect it has something to do with srpg studio version. They might have changed the encryption key from known "key", to another one (possibly randomly generated)
 

uneasy_speedy

Newbie
Feb 22, 2021
70
133
Thank you very much! Yeah, I couldn't find the custom key input either.

Anyway, I've implemented what I could, from now on GAME RIPPER can rip SRPG Studio games too!
I assume it would be possible via plugin similar to rpg maker. I dont know much about scripting I just have SRPG studio. Thanks to the thingie you linked I was actually able to open and edit Heelfall and Loyal Demise, though the latter is only in Chinese for some reason? (even if I load the English translated version)


I've checked these...
1) Can you next time add into the projects some images? It seems these projects contain only *.js script files and 'Project.srpg' file. From which only the latter is encrypted.
2) Assuming 'unencrypt' is indeed not encrypted and for both these projects 'Project.srpg' files are identical - I conclude that 'encrypt' project is encrypted using non-default key.

I suspect it has something to do with srpg studio version. They might have changed the encryption key from known "key", to another one (possibly randomly generated)
If you're more specific I can do that, justaplayer asked me to do it with the default project.
 

justaplayer69

Member
Nov 29, 2023
294
341
Again, thank you guys so much for all your help!
I've checked these...
1) Can you next time add into the projects some images? It seems these projects contain only *.js script files and 'Project.srpg' file. From which only the latter is encrypted.
2) Assuming 'unencrypt' is indeed not encrypted and for both these projects 'Project.srpg' files are identical - I conclude that 'encrypt' project is encrypted using non-default key.
I have also run into a data.dts file in which the "encrypted flag" is set, yet "Project.srpg" seems to be clear text (same as in an unencrypted data.dts). The file version is 0x4DE, so relatively new.

I suspect it has something to do with srpg studio version. They might have changed the encryption key from known "key", to another one (possibly randomly generated)
That's quite possible. Could you please change your C++ code so that in section "3. Getting the key/bitstream" it would save not just the keystream but the original key (the 16 bytes long, an MD5 of something) as well? The thing is, I've already implemented this in GR, you could rip a game with a custom key "gameripper.exe -k (arc4 key)" where that arc4 key is 16 bytes of hex values.

Your how to only extracts the keystream (required by the java app), and not the 16 bytes long key itself (required by GR), would be great to have that too.

I assume it would be possible via plugin similar to rpg maker. I dont know much about scripting I just have SRPG studio. Thanks to the thingie you linked I was actually able to open and edit Heelfall and Loyal Demise, though the latter is only in Chinese for some reason? (even if I load the English translated version)
Not sure about Chinese, but it seems that newer SRPG has some sort of localization support. I guess this is buggy? FYI, only the texts are localized, so if you extract a game with GR, the filenames and the comments in scripts won't change, no matter the locale.

If you're more specific I can do that, justaplayer asked me to do it with the default project.
Yes, because I did not know then that Project.srpg isn't always encrypted.

So I second noamuted, It would be great if you could add one asset file to the project, so that data.dts would surely contain an encrypted file. It doesn't matter if it's a mapchip PNG or a charachip PNG or a UI window PNG or a sound effect OGG or whatever.
 

notamuted

Newbie
May 12, 2018
76
71
Okay, I think I cracked the code!

The 'Project.srpgs' file stored in data.dts is encrypted using the key "_dyn" (the first half from "_dynamic"). And the actual resources are encrypted using a different key. That key is the first 16 bytes of decrypted/unencrypted "Project.srpgs" file. These bytes are directly fed to MD5 algorithm like this:
Python:
def decrypt_asset(input_buf:bytes,_:str)->bytes:
    h = MD5.new()
    pwd= bytes([0x90, 0x51, 0xCA, 0xE7, 0x42, 0xF3, 0x95, 0xFC, 0x5C, 0x5A, 0xAE, 0x6F, 0xAF, 0x83, 0x01, 0xF8])
    h.update(pwd)
    key = h.digest()
    cipher = ARC4.new(key)
    decrypted_data = cipher.decrypt(input_buf)
    return decrypted_data
I tested this approach on oathbreaker game and it seems to be working. But I want test it on more srpgs games to be 100% sure.

I've already implemented this in GR, you could rip a game with a custom key "gameripper.exe -k (arc4 key)" where that arc4 key is 16 bytes of hex values.
Also I tested it with your tool and it seems to work as well. I took `hashlib.md5(pwd).hexdigest()` and inserted it into the custom key field (F2 button).

If you're more specific I can do that, justaplayer asked me to do it with the default project.
So, I too kindly ask you to provide a srpg project with stored assets (images, etc).

Could you please change your C++ code so that in section "3. Getting the key/bitstream" it would save not just the keystream but the original key (the 16 bytes long, an MD5 of something) as well?
View attachment advpro32_dumps_md5input.zip I've attached "advpro32_dumps_md5input.zip" to this comment. It dumps the data passed to 'CryptHashData' call with 2x length as `dumphashinput_31.bin`. (i.e. it will dump fill "keyset" instead of the actually used "key").
 

justaplayer69

Member
Nov 29, 2023
294
341
The 'Project.srpgs' file stored in data.dts is encrypted using the key "_dyn" (the first half from "_dynamic").
Nice!
And the actual resources are encrypted using a different key. That key is the first 16 bytes of decrypted/unencrypted "Project.srpgs" file. These bytes are directly fed to MD5 algorithm like this:
The first 16 bytes of Project.srpg are 100% the same as the last 16 bytes of system.dat, and I've also tried these without luck (I couldn't test Oathbraker sadly). This was my first though too btw.

Could you please confirm that Oathbreaker's system.dat file is indeed contains 0x53, 0x44, 0x41, 0x54, 0x00, 0x00, 0x00, 0x00, 0x90, 0x51, 0xCA, 0xE7, 0x42, 0xF3, 0x95, 0xFC, 0x5C, 0x5A, 0xAE, 0x6F, 0xAF, 0x83, 0x01, 0xF8? (That's 4 bytes "SDAT" magic, 4 bytes zeros, and 16 bytes same as the decrypted Project.srpg's first 16 bytes).

I tested this approach on oathbreaker game and it seems to be working. But I want test it on more srpgs games to be 100% sure.
I have also tried this, but didn't work for me. Admittedly I couldn't get my hands on any custom key games at all, so this is promising!

Also I tested it with your tool and it seems to work as well. I took `hashlib.md5(pwd).hexdigest()` and inserted it into the custom key field (F2 button).
Thank you for your feedback! I'm glad it worked! So you are certain that the custom key is MD5(creator uuid)? Awesome!

I've attached "advpro32_dumps_md5input.zip" to this comment. It dumps the data passed to 'CryptHashData' call with 2x length as `dumphashinput_31.bin`. (i.e. it will dump fill "keyset" instead of the actually used "key").
I don't understand, this seems pointless. It should output the hKey (or maybe the hHash?) argument to ARC4 function CryptDecrypt (so the same that you've tried with GR's F2 and worked). We are interested in the ARC4's key, regardless of how it is constructed (MD5 of "key", "_dyn", creator uuid or whatever). We need the key as it is passed to CryptDecrypt, because knowing that we can generate the entire keystream.
 

notamuted

Newbie
May 12, 2018
76
71
Could you please confirm that Oathbreaker's system.dat file is indeed contains 0x53, 0x44, 0x41, 0x54, 0x00, 0x00, 0x00, 0x00, 0x90, 0x51, 0xCA, 0xE7, 0x42, 0xF3, 0x95, 0xFC, 0x5C, 0x5A, 0xAE, 0x6F, 0xAF, 0x83, 0x01, 0xF8? (That's 4 bytes "SDAT" magic, 4 bytes zeros, and 16 bytes same as the decrypted Project.srpg's first 16 bytes).
I confirm. These are exactly the same bytes
1741447932806.png

Thank you for your feedback! I'm glad it worked! So you are certain that the custom key is MD5(creator uuid)? Awesome!
I am not sure about creator uuid (I don't know what these bytes are), but the custom key is indeed is md5 from these 16 bytes.

I don't understand, this seems pointless. It should output the hKey (or maybe the hHash?) argument to ARC4 function CryptDecrypt (so the same that you've tried with GR's F2 and worked). We are interested in the ARC4's key, regardless of how it is constructed (MD5 of "key", "_dyn", creator uuid or whatever). We need the key as it is passed to CryptDecrypt, because knowing that we can generate the entire keystream.
It outputs pbData passed to CryptHashData (md5 input - i.e. these 16 bytes, the default 'key' key, etc...).
hKey and hHash are handles of uknown type, so they can't be dumped. I believe the thing you want is stored inside wincrypt and cannot be accessed by regular means.
 

uneasy_speedy

Newbie
Feb 22, 2021
70
133


I added 8 random images from heelfall here. I also included what the project folder looks like before deploying it.
 
  • Like
Reactions: notamuted

notamuted

Newbie
May 12, 2018
76
71


I added 8 random images from heelfall here. I also included what the project folder looks like before deploying it.
Thanks. I succesfully decrypted and extracted all 8 stored images.
Now we can with certainty say that everything said above is true.
 

justaplayer69

Member
Nov 29, 2023
294
341
I am not sure about creator uuid (I don't know what these bytes are), but the custom key is indeed is md5 from these 16 bytes.
Well, that's what the SRPG manual says ( )
system.dat
This file contains a value that uniquely identifies the creator of the project.

And because it's 16 bytes long, it's safe to call it UUID (Universally Unique IDentifier, but MS likes to call it GUID, Globally Unique IDentifier).
hKey and hHash are handles of uknown type, so they can't be dumped. I believe the thing you want is stored inside wincrypt and cannot be accessed by regular means.
Not sure about that, I think the "handle" is just a simple struct pointer. But okay.

Anyway, I've implemented your method in GR, it should work work with Oathbreaker too. Could you give it a spin and report back? I double checked the code, but I couldn't test it myself.