MacOS save file AES256 decryption fallback, otherwise error
This commit is contained in:
51
Cargo.lock
generated
51
Cargo.lock
generated
@ -8,6 +8,17 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.9.2"
|
||||
@ -217,6 +228,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.5.1"
|
||||
@ -272,6 +292,16 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@ -378,6 +408,15 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "ecb"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
@ -545,8 +584,10 @@ dependencies = [
|
||||
name = "gd_save_browser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64",
|
||||
"dirs",
|
||||
"ecb",
|
||||
"flate2",
|
||||
"phf",
|
||||
"quick-xml 0.37.0",
|
||||
@ -743,6 +784,16 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
|
@ -12,3 +12,5 @@ quick-xml = "0.37.0"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
phf = { version = "0.11.2", features = ["macros"] }
|
||||
aes = "0.8.4"
|
||||
ecb = "0.1.2"
|
||||
|
@ -5,6 +5,10 @@ pub const TRUNCATE_LEVEL_DATA: bool = true;
|
||||
pub const YOUTUBE_CHANNEL_BASE_URL: &str = "https://www.youtube.com/channel/";
|
||||
|
||||
pub const XOR_KEY: u8 = 0xB;
|
||||
pub const AES_KEY: [u8; 32] = [
|
||||
0x69, 0x70, 0x75, 0x39, 0x54, 0x55, 0x76, 0x35, 0x34, 0x79, 0x76, 0x5d, 0x69, 0x73, 0x46, 0x4d,
|
||||
0x68, 0x35, 0x40, 0x3b, 0x74, 0x2e, 0x35, 0x77, 0x33, 0x34, 0x45, 0x32, 0x52, 0x79, 0x40, 0x7b,
|
||||
];
|
||||
// pub const PASSWORD_SALT: &str = "mI29fmAnxgTs";
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -105,10 +109,12 @@ pub static GAMESAVE_KEYS: Map<&'static str, &'static str> = phf_map! {
|
||||
"GJA_005" => "hashed_password",
|
||||
"LLM_01" => "local_levels",
|
||||
"LLM_02" => "binary_version",
|
||||
"LLM_03" => "local_lists",
|
||||
"MDLM_001" => "song_info",
|
||||
"MDLM_002" => "song_priority",
|
||||
"MDLM_003" => "unknown_4",
|
||||
"KBM_001" => "keybinds",
|
||||
"KBM_002" => "keybinds2",
|
||||
"KBM_002" => "keybinds_2",
|
||||
"texQuality" => "texture_quality",
|
||||
"customObjectDict" => "custom_objects",
|
||||
"playerUserID" => "player_id",
|
||||
@ -267,7 +273,7 @@ pub static LEVEL_KEYS: Map<&'static str, &'static str> = phf_map! {
|
||||
"k43" => "two_player",
|
||||
"k45" => "custom_song_id",
|
||||
"k46" => "revision",
|
||||
"k47" => "edited",
|
||||
"k47" => "edited_outside_editor",
|
||||
"k48" => "objects",
|
||||
"k50" => "binary_version",
|
||||
"k60" => "account_id",
|
||||
|
21
src/main.rs
21
src/main.rs
@ -3,6 +3,7 @@ mod util;
|
||||
|
||||
use constants::XOR_KEY;
|
||||
use serde_json::Value;
|
||||
use std::error::Error;
|
||||
use std::process::exit;
|
||||
use util::decrypt::*;
|
||||
use util::file::*;
|
||||
@ -10,8 +11,24 @@ use util::xml::*;
|
||||
|
||||
fn main() {
|
||||
if let Some(data_encrypted) = get_dat_data() {
|
||||
let data: Vec<u8> =
|
||||
gz_decompress(&decode_urlsafe_base64(&xor_decrypt(&data_encrypted, XOR_KEY)).unwrap());
|
||||
let decrypt_data_windows = || -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
Ok(gz_decompress(&decode_urlsafe_base64(&xor_decrypt(
|
||||
&data_encrypted,
|
||||
XOR_KEY,
|
||||
))?))
|
||||
};
|
||||
|
||||
let decrypt_data_mac =
|
||||
|| -> Result<Vec<u8>, Box<dyn Error>> { Ok(decode_aes(&data_encrypted)) };
|
||||
|
||||
let data: Vec<u8> = if let Ok(d) = decrypt_data_windows() {
|
||||
d
|
||||
} else if let Ok(d) = decrypt_data_mac() {
|
||||
d
|
||||
} else {
|
||||
println!("Decryption failed, corrupted save file?");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
match validate_xml(&data) {
|
||||
Ok(_) => {
|
||||
|
@ -1,10 +1,17 @@
|
||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyInit};
|
||||
use aes::Aes256;
|
||||
use base64::{
|
||||
engine::general_purpose::{STANDARD, URL_SAFE},
|
||||
DecodeError, Engine,
|
||||
};
|
||||
use ecb::Decryptor;
|
||||
use flate2::read::GzDecoder;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::constants::AES_KEY;
|
||||
|
||||
type Aes256Decryptor = Decryptor<Aes256>;
|
||||
|
||||
// Step 1
|
||||
pub fn xor_decrypt(data: &[u8], key: u8) -> Vec<u8> {
|
||||
data.iter().map(|byte| byte ^ key).collect()
|
||||
@ -41,3 +48,11 @@ pub fn decode_base64(data: &str) -> String {
|
||||
.map(|&byte| byte as char)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn decode_aes(data: &[u8]) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = data.to_vec();
|
||||
Aes256Decryptor::new(&AES_KEY.into())
|
||||
.decrypt_padded_mut::<Pkcs7>(&mut buf)
|
||||
.unwrap();
|
||||
buf
|
||||
}
|
||||
|
@ -122,7 +122,8 @@ fn parse_dict(
|
||||
_ => Some(key.to_string()),
|
||||
}
|
||||
}
|
||||
key if parent_key.unwrap_or_default() == "amount"
|
||||
key if ["amount", "local_levels", "local_lists"]
|
||||
.contains(&parent_key.unwrap_or_default())
|
||||
&& key.starts_with("k_") =>
|
||||
{
|
||||
let replaced_key = key.replace("k_", "");
|
||||
|
Reference in New Issue
Block a user