Windows decryption no GUI

This commit is contained in:
Ahmed Al-Taiar
2024-11-06 16:11:46 -05:00
commit fae2ad25e5
9 changed files with 2048 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1935
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "gd_save_browser"
version = "0.1.0"
edition = "2021"
[dependencies]
rfd = "0.15.0"
base64 = "0.22.1"
flate2 = "1.0.34"
dirs = "5.0.1"

1
src/constants.rs Normal file
View File

@ -0,0 +1 @@
pub const XOR_KEY: u8 = 0xB;

22
src/main.rs Normal file
View File

@ -0,0 +1,22 @@
mod constants;
mod util;
use constants::XOR_KEY;
use util::decrypt::*;
use util::file::*;
use util::xml::*;
fn main() {
let data: Vec<u8> =
gz_decompress(&base64_decode(&xor_decrypt(&get_dat_data(), XOR_KEY)).unwrap());
match validate_xml(&data) {
Ok(_) => {
println!("Decryption successful");
write_decrypted_data(&data, prompt_output_path());
}
Err(e) => {
println!("Error: {e}");
}
}
}

31
src/util/decrypt.rs Normal file
View File

@ -0,0 +1,31 @@
use base64::{engine::general_purpose::URL_SAFE, DecodeError, Engine};
use flate2::read::GzDecoder;
use std::io::Read;
// Step 1
pub fn xor_decrypt(data: &[u8], key: u8) -> Vec<u8> {
data.iter().map(|byte| byte ^ key).collect()
}
// Step 2
pub fn base64_decode(data: &[u8]) -> Result<Vec<u8>, DecodeError> {
let url_safe_data: Vec<u8> = data
.iter()
.filter(|&&byte| byte != 0)
.map(|&byte| match byte {
b'+' => b'-',
b'/' => b'_',
_ => byte,
})
.collect();
URL_SAFE.decode(url_safe_data)
}
// Step 3
pub fn gz_decompress(data: &[u8]) -> Vec<u8> {
let mut decoder: GzDecoder<&[u8]> = GzDecoder::new(data);
let mut decompressed: Vec<u8> = Vec::new();
decoder.read_to_end(&mut decompressed).unwrap();
decompressed
}

33
src/util/file.rs Normal file
View File

@ -0,0 +1,33 @@
use dirs::{data_local_dir, desktop_dir};
use rfd::FileDialog;
use std::{fs::File, io::Read, io::Write, path::PathBuf};
pub fn get_dat_data() -> Vec<u8> {
let path: PathBuf = FileDialog::new()
.set_title("Select Geometry Dash Save")
.set_file_name("CCGameManager.dat")
.add_filter("Geometry Dash Save", &["dat"])
.set_directory(data_local_dir().unwrap())
.pick_file()
.unwrap();
let mut file: File = File::open(path).unwrap();
let mut data: Vec<u8> = Vec::new();
file.read_to_end(&mut data).unwrap();
data
}
pub fn prompt_output_path() -> PathBuf {
FileDialog::new()
.set_title("Select Output Location")
.set_file_name("CCGameManager.xml")
.add_filter("Geometry Dash Decrypted Save", &["xml"])
.set_directory(desktop_dir().unwrap())
.save_file()
.unwrap()
}
pub fn write_decrypted_data(data: &[u8], output_path: PathBuf) {
let mut output_file: File = File::create(output_path).unwrap();
output_file.write_all(data).unwrap()
}

3
src/util/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod decrypt;
pub mod file;
pub mod xml;

12
src/util/xml.rs Normal file
View File

@ -0,0 +1,12 @@
pub fn validate_xml(xml: &[u8]) -> Result<(), String> {
let xml_str: String = String::from_utf8_lossy(xml).to_string();
if xml_str.starts_with("<?xml version=\"1.0\"?>") {
return Ok(());
} else {
return Err("Invalid/missing XML header".to_string());
}
#[allow(unreachable_code)]
Ok(())
}